Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: const modifier to replace @Constant annotation #618

Merged
merged 15 commits into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/language/formatting/safe-ds-formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import {
isAstNode,
} from 'langium';
import * as ast from '../generated/ast.js';
import { annotationCallsOrEmpty, literalsOrEmpty, typeArgumentsOrEmpty } from '../helpers/nodeProperties.js';
import { last } from 'radash';
import noSpace = Formatting.noSpace;
import newLine = Formatting.newLine;
import newLines = Formatting.newLines;
import oneSpace = Formatting.oneSpace;
import indent = Formatting.indent;
import { annotationCallsOrEmpty, literalsOrEmpty, typeArgumentsOrEmpty } from '../helpers/nodeProperties.js';

const newLinesWithIndent = function (count: number, options?: FormattingActionOptions): FormattingAction {
return {
Expand Down Expand Up @@ -549,14 +550,13 @@ export class SafeDsFormatter extends AbstractFormatter {
private formatSdsParameter(node: ast.SdsParameter): void {
const formatter = this.getNodeFormatter(node);

if (annotationCallsOrEmpty(node).length === 0) {
if (node.isVariadic) {
formatter.property('name').prepend(oneSpace());
}
} else {
formatter.property('name').prepend(newLine());
const lastAnnotationCall = last(annotationCallsOrEmpty(node));
if (lastAnnotationCall) {
formatter.node(lastAnnotationCall).append(newLine());
}

formatter.keyword('const').append(oneSpace());
formatter.keyword('vararg').append(oneSpace());
formatter.keyword(':').prepend(noSpace()).append(oneSpace());
formatter.keyword('=').surround(oneSpace());
}
Expand Down
3 changes: 2 additions & 1 deletion src/language/grammar/safe-ds.langium
Original file line number Diff line number Diff line change
Expand Up @@ -416,14 +416,15 @@ SdsParameterList returns SdsParameterList:
;

interface SdsParameter extends SdsLocalVariable {
isConstant: boolean
isVariadic: boolean
^type?: SdsType
defaultValue?: SdsExpression
}

SdsParameter returns SdsParameter:
annotationCalls+=SdsAnnotationCall*
isVariadic?='vararg'?
(isConstant?='const' & isVariadic?='vararg')
name=ID
(':' ^type=SdsType)?
('=' defaultValue=SdsExpression)?
Expand Down
17 changes: 17 additions & 0 deletions src/language/validation/other/declarations/annotations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SdsAnnotation } from '../../../generated/ast.js';
import { ValidationAcceptor } from 'langium';
import { parametersOrEmpty } from '../../../helpers/nodeProperties.js';

export const CODE_ANNOTATION_PARAMETER_CONST_MODIFIER = 'annotation/parameter-const-modifier';

export const annotationParameterShouldNotHaveConstModifier = (node: SdsAnnotation, accept: ValidationAcceptor) => {
for (const parameter of parametersOrEmpty(node)) {
if (parameter.isConstant) {
accept('info', 'Parameters of annotations implicitly have the const modifier.', {
node: parameter,
property: 'name',
code: CODE_ANNOTATION_PARAMETER_CONST_MODIFIER,
});
}
}
};
17 changes: 17 additions & 0 deletions src/language/validation/other/expressions/lambdas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SdsLambda } from '../../../generated/ast.js';
import { ValidationAcceptor } from 'langium';
import { parametersOrEmpty } from '../../../helpers/nodeProperties.js';

export const CODE_LAMBDA_CONST_MODIFIER = 'lambda/const-modifier';

export const lambdaParameterMustNotHaveConstModifier = (node: SdsLambda, accept: ValidationAcceptor): void => {
for (const parameter of parametersOrEmpty(node)) {
if (parameter.isConstant) {
accept('error', 'The const modifier is not applicable to parameters of lambdas.', {
node: parameter,
property: 'name',
code: CODE_LAMBDA_CONST_MODIFIER,
});
}
}
};
16 changes: 16 additions & 0 deletions src/language/validation/other/types/callableTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,24 @@ import { ValidationAcceptor } from 'langium';

import { parametersOrEmpty } from '../../../helpers/nodeProperties.js';

export const CODE_CALLABLE_TYPE_CONST_MODIFIER = 'callable-type/const-modifier';
export const CODE_CALLABLE_TYPE_NO_OPTIONAL_PARAMETERS = 'callable-type/no-optional-parameters';

export const callableTypeParameterMustNotHaveConstModifier = (
node: SdsCallableType,
accept: ValidationAcceptor,
): void => {
for (const parameter of parametersOrEmpty(node)) {
if (parameter.isConstant) {
accept('error', 'The const modifier is not applicable to parameters of callable types.', {
node: parameter,
property: 'name',
code: CODE_CALLABLE_TYPE_CONST_MODIFIER,
});
}
}
};

export const callableTypeMustNotHaveOptionalParameters = (node: SdsCallableType, accept: ValidationAcceptor): void => {
for (const parameter of parametersOrEmpty(node)) {
if (parameter.defaultValue && !parameter.isVariadic) {
Expand Down
20 changes: 17 additions & 3 deletions src/language/validation/safe-ds-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ import {
parameterListVariadicParameterMustBeLast,
} from './other/declarations/parameterLists.js';
import { unionTypeMustHaveTypeArguments } from './other/types/unionTypes.js';
import { callableTypeMustNotHaveOptionalParameters } from './other/types/callableTypes.js';
import {
callableTypeMustNotHaveOptionalParameters,
callableTypeParameterMustNotHaveConstModifier,
} from './other/types/callableTypes.js';
import { typeArgumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/types/typeArgumentLists.js';
import { argumentListMustNotHavePositionalArgumentsAfterNamedArguments } from './other/argumentLists.js';
import { parameterMustNotBeVariadicAndOptional } from './other/declarations/parameters.js';
Expand All @@ -63,6 +66,8 @@ import {
} from './builtins/experimental.js';
import { placeholderShouldBeUsed } from './other/declarations/placeholders.js';
import { segmentParameterShouldBeUsed, segmentResultMustBeAssignedExactlyOnce } from './other/declarations/segments.js';
import { annotationParameterShouldNotHaveConstModifier } from './other/declarations/annotations.js';
import { lambdaParameterMustNotHaveConstModifier } from './other/expressions/lambdas.js';

/**
* Register custom validation checks.
Expand All @@ -75,7 +80,11 @@ export const registerValidationChecks = function (services: SafeDsServices) {
assigneeAssignedResultShouldNotBeExperimental(services),
],
SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees],
SdsAnnotation: [annotationMustContainUniqueNames, annotationParameterListShouldNotBeEmpty],
SdsAnnotation: [
annotationMustContainUniqueNames,
annotationParameterListShouldNotBeEmpty,
annotationParameterShouldNotHaveConstModifier,
],
SdsAnnotationCall: [
annotationCallAnnotationShouldNotBeDeprecated(services),
annotationCallAnnotationShouldNotBeExperimental(services),
Expand All @@ -89,7 +98,11 @@ export const registerValidationChecks = function (services: SafeDsServices) {
SdsAttribute: [attributeMustHaveTypeHint],
SdsBlockLambda: [blockLambdaMustContainUniqueNames],
SdsCall: [callArgumentListShouldBeNeeded(services)],
SdsCallableType: [callableTypeMustContainUniqueNames, callableTypeMustNotHaveOptionalParameters],
SdsCallableType: [
callableTypeMustContainUniqueNames,
callableTypeMustNotHaveOptionalParameters,
callableTypeParameterMustNotHaveConstModifier,
],
SdsClass: [classMustContainUniqueNames],
SdsClassBody: [classBodyShouldNotBeEmpty],
SdsConstraintList: [constraintListShouldNotBeEmpty],
Expand All @@ -99,6 +112,7 @@ export const registerValidationChecks = function (services: SafeDsServices) {
SdsEnumVariant: [enumVariantMustContainUniqueNames, enumVariantParameterListShouldNotBeEmpty],
SdsExpressionLambda: [expressionLambdaMustContainUniqueNames],
SdsFunction: [functionMustContainUniqueNames, functionResultListShouldNotBeEmpty],
SdsLambda: [lambdaParameterMustNotHaveConstModifier],
SdsMemberAccess: [memberAccessNullSafetyShouldBeNeeded(services)],
SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage],
SdsNamedType: [
Expand Down
4 changes: 0 additions & 4 deletions src/resources/builtins/safeds/lang/coreAnnotations.sdsstub
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,3 @@ annotation Pure
@Description("The function has no side effects.")
@Target(AnnotationTarget.Function)
annotation NoSideEffects

@Description("Values assigned to this parameter must be constant.")
@Target(AnnotationTarget.Parameter)
annotation Constant
7 changes: 7 additions & 0 deletions tests/language/formatting/creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const createFormattingTest = async (uri: URI): Promise<FormattingTest> => {
testName: `[${shortenedResourceName}] should be formatted correctly`,
originalCode,
expectedFormattedCode,
uri,
};
};

Expand All @@ -59,6 +60,7 @@ const invalidTest = (uri: URI, error: Error): FormattingTest => {
testName: `INVALID TEST FILE [${shortenedResourceName}]`,
originalCode: '',
expectedFormattedCode: '',
uri: URI.file(''),
error,
};
};
Expand Down Expand Up @@ -86,6 +88,11 @@ interface FormattingTest extends TestDescription {
* The expected formatted code.
*/
expectedFormattedCode: string;

/**
* The URI of the corresponding file.
*/
uri: URI;
}

/**
Expand Down
7 changes: 7 additions & 0 deletions tests/language/grammar/creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const createGrammarTest = (uri: URI): GrammarTest => {
testName,
code,
expectedResult: comment,
uri,
};
};

Expand All @@ -59,6 +60,7 @@ const invalidTest = (uri: URI, error: Error): GrammarTest => {
testName: `INVALID TEST FILE [${shortenedResourceName}]`,
code: '',
expectedResult: 'invalid',
uri: URI.file(''),
error,
};
};
Expand All @@ -76,6 +78,11 @@ interface GrammarTest extends TestDescription {
* The expected result after parsing the program.
*/
expectedResult: 'syntax_error' | 'no_syntax_error' | 'invalid';

/**
* The URI of the corresponding file.
*/
uri: URI;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/language/grammar/testGrammar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('grammar', () => {
if (test.expectedResult === 'syntax_error') {
if (actualSyntaxErrors.length === 0) {
throw new AssertionError({
message: 'Expected syntax errors but found none.',
message: `Expected syntax errors in ${test.uri} but found none.`,
actual: actualSyntaxErrors,
expected: [],
});
Expand All @@ -37,7 +37,7 @@ describe('grammar', () => {
else if (test.expectedResult === 'no_syntax_error') {
if (actualSyntaxErrors.length > 0) {
throw new AssertionError({
message: 'Expected no syntax errors but found some.',
message: `Expected no syntax errors in ${test.uri} but found some.`,
actual: actualSyntaxErrors,
expected: [],
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,73 @@
annotation MyAnnotation (

@Annotation a ,
@Annotation a1 ,

b = 0 ,
b1 = 0 ,

vararg c ,
vararg c1 ,

vararg d : Int = 1 ,
vararg d1 : Int = 1 ,

e : Int ,
e1 : Int ,

f : Int = 2 ,
f1 : Int = 2 ,

vararg g : Int ,
vararg g1 : Int ,

vararg h : Int = 3
vararg h1 : Int = 3 ,

@Annotation const a2 ,

const b2 = 0 ,

const vararg c2 ,

const vararg d2 : Int = 1 ,

const e2 : Int ,

const f2 : Int = 2 ,

const vararg g2 : Int ,

const vararg h2 : Int = 3 ,

@Annotation vararg const a3 ,

vararg const c3 ,

vararg const d3 : Int = 1 ,

vararg const g3 : Int ,

vararg const h3 : Int = 3
)

// -----------------------------------------------------------------------------

annotation MyAnnotation(
@Annotation
a,
b = 0,
vararg c,
vararg d: Int = 1,
e: Int,
f: Int = 2,
vararg g: Int,
vararg h: Int = 3
a1,
b1 = 0,
vararg c1,
vararg d1: Int = 1,
e1: Int,
f1: Int = 2,
vararg g1: Int,
vararg h1: Int = 3,
@Annotation
const a2,
const b2 = 0,
const vararg c2,
const vararg d2: Int = 1,
const e2: Int,
const f2: Int = 2,
const vararg g2: Int,
const vararg h2: Int = 3,
@Annotation
vararg const a3,
vararg const c3,
vararg const d3: Int = 1,
vararg const g3: Int,
vararg const h3: Int = 3
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
annotation MyAnnotation ( const a : Int = 1 )

// -----------------------------------------------------------------------------

annotation MyAnnotation(const a: Int = 1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
annotation MyAnnotation ( const a : Int )

// -----------------------------------------------------------------------------

annotation MyAnnotation(const a: Int)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
annotation MyAnnotation1 ( const vararg a : Int = 1 )

annotation MyAnnotation2 ( vararg const a : Int = 1 )

// -----------------------------------------------------------------------------

annotation MyAnnotation1(const vararg a: Int = 1)

annotation MyAnnotation2(vararg const a: Int = 1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
annotation MyAnnotation1 ( const vararg a : Int )

annotation MyAnnotation2 ( vararg const a : Int )

// -----------------------------------------------------------------------------

annotation MyAnnotation1(const vararg a: Int)

annotation MyAnnotation2(vararg const a: Int)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
annotation MyAnnotation ( const a = 1 )

// -----------------------------------------------------------------------------

annotation MyAnnotation(const a = 1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
annotation MyAnnotation ( const a )

// -----------------------------------------------------------------------------

annotation MyAnnotation(const a)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
annotation MyAnnotation1 ( const vararg a = 1 )

annotation MyAnnotation2 ( vararg const a = 1 )

// -----------------------------------------------------------------------------

annotation MyAnnotation1(const vararg a = 1)

annotation MyAnnotation2(vararg const a = 1)
Loading