Skip to content

Commit

Permalink
Merge branch 'shouldInitializeAllAttributes' into expression-body-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
PalumboN authored Aug 12, 2023
2 parents ca5e776 + c1bd2e4 commit 5f6109b
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 19 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "wollok-ts",
"version": "4.0.4",
"wollokVersion": "3.1.7",
"wollokVersion": "3.1.8",
"description": "TypeScript based Wollok language implementation",
"repository": "https://github.com/uqbar-project/wollok-ts",
"license": "MIT",
Expand Down
46 changes: 30 additions & 16 deletions src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export const shouldNotReassignConst = error<Assignment>(node => {
})

// TODO: Test if the reference points to the right kind of node
export const missingReference = error<Reference<Node>>(node => !!node.target )
export const missingReference = error<Reference<Node>>(node => !!node.target)

export const shouldNotHaveLoopInHierarchy = error<Class | Mixin>(node => !allParents(node).includes(node))

Expand Down Expand Up @@ -218,11 +218,15 @@ export const shouldPassValuesToAllAttributes = error<New>(
node => getUninitializedAttributesForInstantation(node),
)

export const shouldInitializeAllAttributes = error<Singleton>(
node => isEmpty(getUninitializedAttributes(node)),
node => getUninitializedAttributes(node)
export const shouldInitializeInheritedAttributes = error<Singleton>(
node => isEmpty(getInheritedUninitializedAttributes(node)),
node => [getInheritedUninitializedAttributes(node).join(', ')],
)

export const shouldInitializeSingletonAttribute = error<Field>(node => {
return !node.parent.is(Singleton) || !isUninitialized(node.value)
})

export const shouldNotUseSelf = error<Self>(node => {
const ancestors = node.ancestors
return node.isSynthetic || !ancestors.some(is(Program)) || ancestors.some(is(Singleton))
Expand Down Expand Up @@ -395,7 +399,7 @@ export const shouldUseConditionalExpression = warning<If>(node => {
elseValue === undefined ||
![true, false].includes(thenValue) ||
thenValue === elseValue) && (!nextSentence ||
![true, false].includes(valueFor(nextSentence))
![true, false].includes(valueFor(nextSentence))
)
})

Expand Down Expand Up @@ -511,17 +515,27 @@ const getUninitializedAttributesForInstantation = (node: New): string[] => {
const target = node.instantiated.target
if (!target) return []
const initializers = node.args.map(_ => _.name)
return getUninitializedAttributes(target, initializers)
return getAllUninitializedAttributes(target, initializers)
}

const getUninitializedAttributes = (node: Module, initializers: string[] = []) =>
node.allFields
.filter(field => {
const getAllUninitializedAttributes = (node: Module, initializers: string[] = []) =>
getUninitializedAttributesIn(node, [...node.allFields], initializers)

const getInheritedUninitializedAttributes = (node: Module, initializers: string[] = []) =>
getUninitializedAttributesIn(node, [...node.allFields.filter(f => f.parent !== node)], initializers)


const getUninitializedAttributesIn = (node: Module, fields: Field[], initializers: string[] = []) =>
fields.
filter(field => {
const value = node.defaultValueFor(field)
return value.isSynthetic && value.is(Literal) && value.isNull() && !initializers.includes(field.name)
return isUninitialized(value) && !initializers.includes(field.name)
})
.map(field => field.name)


const isUninitialized = (value: Expression) => value.isSynthetic && value.is(Literal) && value.isNull()

const isBooleanLiteral = (node: Expression, value: boolean) => node.is(Literal) && node.value === value

const targetSupertypes = (node: Class | Singleton) => node.supertypes.map(_ => _?.reference.target)
Expand Down Expand Up @@ -560,10 +574,10 @@ const referencesSingleton = (node: Expression) => node.is(Reference) && node.tar

const isBooleanOrUnknownType = (node: Node): boolean => match(node)(
when(Literal)(condition => condition.value === true || condition.value === false),
when(Send)( _ => true), // tackled in a different validator
when(Super)( _ => true),
when(Reference)( condition => !condition.target?.is(Singleton)),
when(Node)( _ => false),
when(Send)(_ => true), // tackled in a different validator
when(Super)(_ => true),
when(Reference)(condition => !condition.target?.is(Singleton)),
when(Node)(_ => false),
)

const valueFor: any | undefined = (node: Node) =>
Expand Down Expand Up @@ -746,9 +760,9 @@ const validationsByKind = (node: Node): Record<string, Validation<any>> => match
when(Program)(() => ({ nameShouldNotBeKeyword, shouldNotUseReservedWords, shouldMatchFileExtension, shouldNotDuplicateEntities })),
when(Test)(() => ({ shouldHaveNonEmptyName, shouldNotMarkMoreThanOneOnlyTest, shouldHaveAssertInTest, shouldMatchFileExtension })),
when(Class)(() => ({ nameShouldBeginWithUppercase, nameShouldNotBeKeyword, shouldNotHaveLoopInHierarchy, linearizationShouldNotRepeatNamedArguments, shouldNotDefineMoreThanOneSuperclass, superclassShouldBeLastInLinearization, shouldNotDuplicateGlobalDefinitions, shouldNotDuplicateVariablesInLinearization, shouldImplementAllMethodsInHierarchy, shouldNotUseReservedWords, shouldNotDuplicateEntities })),
when(Singleton)(() => ({ nameShouldBeginWithLowercase, inlineSingletonShouldBeAnonymous, topLevelSingletonShouldHaveAName, nameShouldNotBeKeyword, shouldInitializeAllAttributes, linearizationShouldNotRepeatNamedArguments, shouldNotDefineMoreThanOneSuperclass, superclassShouldBeLastInLinearization, shouldNotDuplicateGlobalDefinitions, shouldNotDuplicateVariablesInLinearization, shouldImplementAbstractMethods, shouldImplementAllMethodsInHierarchy, shouldNotUseReservedWords, shouldNotDuplicateEntities })),
when(Singleton)(() => ({ nameShouldBeginWithLowercase, inlineSingletonShouldBeAnonymous, topLevelSingletonShouldHaveAName, nameShouldNotBeKeyword, shouldInitializeInheritedAttributes, linearizationShouldNotRepeatNamedArguments, shouldNotDefineMoreThanOneSuperclass, superclassShouldBeLastInLinearization, shouldNotDuplicateGlobalDefinitions, shouldNotDuplicateVariablesInLinearization, shouldImplementAbstractMethods, shouldImplementAllMethodsInHierarchy, shouldNotUseReservedWords, shouldNotDuplicateEntities })),
when(Mixin)(() => ({ nameShouldBeginWithUppercase, shouldNotHaveLoopInHierarchy, shouldOnlyInheritFromMixin, shouldNotDuplicateGlobalDefinitions, shouldNotDuplicateVariablesInLinearization, shouldNotDuplicateEntities })),
when(Field)(() => ({ nameShouldBeginWithLowercase, shouldNotAssignToItselfInDeclaration, nameShouldNotBeKeyword, shouldNotDuplicateFields, shouldNotUseReservedWords, shouldNotDefineUnusedVariables, shouldDefineConstInsteadOfVar })),
when(Field)(() => ({ nameShouldBeginWithLowercase, shouldNotAssignToItselfInDeclaration, nameShouldNotBeKeyword, shouldNotDuplicateFields, shouldNotUseReservedWords, shouldNotDefineUnusedVariables, shouldDefineConstInsteadOfVar, shouldInitializeSingletonAttribute })),
when(Method)(() => ({ onlyLastParameterCanBeVarArg, nameShouldNotBeKeyword, methodShouldHaveDifferentSignature, shouldNotOnlyCallToSuper, shouldUseOverrideKeyword, possiblyReturningBlock, shouldNotUseOverride, shouldMatchSuperclassReturnValue, shouldNotDefineNativeMethodsOnUnnamedSingleton, overridingMethodShouldHaveABody, getterMethodShouldReturnAValue })),
when(Variable)(() => ({ nameShouldBeginWithLowercase, nameShouldNotBeKeyword, shouldNotAssignToItselfInDeclaration, shouldNotDuplicateLocalVariables, shouldNotDuplicateGlobalDefinitions, shouldNotDefineGlobalMutableVariables, shouldNotUseReservedWords, shouldInitializeGlobalReference, shouldDefineConstInsteadOfVar, shouldNotDuplicateEntities })),
when(Assignment)(() => ({ shouldNotAssignToItself, shouldNotReassignConst })),
Expand Down
11 changes: 9 additions & 2 deletions test/validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { readFileSync } from 'fs'
import globby from 'globby'
import { join } from 'path'
import { Annotation, buildEnvironment } from '../src'
import { notEmpty } from '../src/extensions'
import { List, notEmpty } from '../src/extensions'
import validate from '../src/validator'
import { Node, Problem } from './../src/model'
import { Class, Literal, Node, Problem, Reference } from './../src/model'

const TESTS_PATH = 'language/test/validations'

Expand Down Expand Up @@ -50,6 +50,7 @@ describe('Wollok Validations', () => {
for (const expectedProblem of expectedProblems) {
const code = expectedProblem.args.get('code')!
const level = expectedProblem.args.get('level')
const values = expectedProblem.args.get('values')

if (!code) fail('Missing required "code" argument in @Expect annotation')

Expand All @@ -64,6 +65,12 @@ describe('Wollok Validations', () => {

if (level && effectiveProblem.level !== level)
fail(`Expected ${code} to be ${level} but was ${effectiveProblem.level} at ${errorLocation(node)}`)

if (values) {
const stringValues = (values as [Reference<Class>, List<Literal<string>>])[1].map(v => v.value)
if (stringValues.join('||') !== effectiveProblem.values.join('||'))
fail(`Expected ${code} to have ${JSON.stringify(stringValues)} but was ${JSON.stringify(effectiveProblem.values)} at ${errorLocation(node)}`)
}
}

for (const problem of problems) {
Expand Down

0 comments on commit 5f6109b

Please sign in to comment.