From f031d8e9bb9f5832d093432c5c277d533e52f0d5 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 15:53:02 +0200 Subject: [PATCH 1/4] feat: info if argument list can be removed --- src/language/helpers/nodeProperties.ts | 4 ++ src/language/validation/safe-ds-validator.ts | 5 +- src/language/validation/style.ts | 60 ++++++++++++++-- .../main.sdstest | 35 +++++++++ .../main.sdstest | 71 +++++++++++++++++++ 5 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 tests/resources/validation/style/unnecessary argument list in annotation call/main.sdstest create mode 100644 tests/resources/validation/style/unnecessary argument list in call/main.sdstest diff --git a/src/language/helpers/nodeProperties.ts b/src/language/helpers/nodeProperties.ts index 3490fa941..96fc596e4 100644 --- a/src/language/helpers/nodeProperties.ts +++ b/src/language/helpers/nodeProperties.ts @@ -63,6 +63,10 @@ export const isNamedTypeArgument = (node: SdsTypeArgument): boolean => { return Boolean(node.typeParameter); }; +export const isRequiredParameter = (node: SdsParameter): boolean => { + return !node.defaultValue && !node.isVariadic; +} + export const isStatic = (node: SdsClassMember): boolean => { if (isSdsClass(node) || isSdsEnum(node)) { return true; diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 9b115dc2c..a191d07e4 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -16,8 +16,9 @@ import { segmentMustContainUniqueNames, } from './names.js'; import { + annotationCallArgumentListShouldBeNeeded, annotationParameterListShouldNotBeEmpty, - assignmentShouldHaveMoreThanWildcardsAsAssignees, + assignmentShouldHaveMoreThanWildcardsAsAssignees, callArgumentListShouldBeNeeded, classBodyShouldNotBeEmpty, constraintListShouldNotBeEmpty, enumBodyShouldNotBeEmpty, @@ -52,9 +53,11 @@ export const registerValidationChecks = function (services: SafeDsServices) { const checks: ValidationChecks = { SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees], SdsAnnotation: [annotationMustContainUniqueNames, annotationParameterListShouldNotBeEmpty], + SdsAnnotationCall: [annotationCallArgumentListShouldBeNeeded], SdsArgumentList: [argumentListMustNotHavePositionalArgumentsAfterNamedArguments], SdsAttribute: [attributeMustHaveTypeHint], SdsBlockLambda: [blockLambdaMustContainUniqueNames], + SdsCall: [callArgumentListShouldBeNeeded(services)], SdsCallableType: [callableTypeMustContainUniqueNames, callableTypeMustNotHaveOptionalParameters], SdsClass: [classMustContainUniqueNames], SdsClassBody: [classBodyShouldNotBeEmpty], diff --git a/src/language/validation/style.ts b/src/language/validation/style.ts index 2df07a734..89854e09a 100644 --- a/src/language/validation/style.ts +++ b/src/language/validation/style.ts @@ -1,7 +1,8 @@ import { + isSdsEnumVariant, isSdsWildcard, - SdsAnnotation, - SdsAssignment, + SdsAnnotation, SdsAnnotationCall, + SdsAssignment, SdsCall, SdsClassBody, SdsConstraintList, SdsEnumBody, @@ -11,8 +12,10 @@ import { SdsTypeParameterList, SdsUnionType, } from '../generated/ast.js'; -import { ValidationAcceptor } from 'langium'; -import { isEmpty } from 'radash'; +import {ValidationAcceptor} from 'langium'; +import {isEmpty} from 'radash'; +import {isRequiredParameter, parametersOrEmpty} from "../helpers/nodeProperties.js"; +import {SafeDsServices} from "../safe-ds-module.js"; export const CODE_STYLE_UNNECESSARY_ASSIGNMENT = 'style/unnecessary-assignment'; export const CODE_STYLE_UNNECESSARY_ARGUMENT_LIST = 'style/unnecessary-argument-list'; @@ -26,6 +29,55 @@ export const CODE_STYLE_UNNECESSARY_TYPE_ARGUMENT_LIST = 'style/unnecessary-type export const CODE_STYLE_UNNECESSARY_TYPE_PARAMETER_LIST = 'style/unnecessary-type-parameter-list'; export const CODE_STYLE_UNNECESSARY_UNION_TYPE = 'style/unnecessary-union-type'; +// ----------------------------------------------------------------------------- +// Unnecessary argument list +// ----------------------------------------------------------------------------- + +export const annotationCallArgumentListShouldBeNeeded = ( + node: SdsAnnotationCall, + accept: ValidationAcceptor, +): void => { + const argumentList = node.argumentList; + if (!argumentList || !isEmpty(argumentList.arguments)) { + return; + } + + const annotation = node.annotation?.ref; + if (!annotation) { + return; + } + + const hasRequiredParameters = parametersOrEmpty(annotation).some(isRequiredParameter); + if (!hasRequiredParameters) { + accept('info', 'This argument list can be removed.', { + node: argumentList, + code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST, + }) + } +} + +export const callArgumentListShouldBeNeeded = (services: SafeDsServices) => ( + node: SdsCall, + accept: ValidationAcceptor, +): void => { + const argumentList = node.argumentList; + if (!argumentList || !isEmpty(argumentList.arguments)) { + return; + } + + const callable = services.helpers.NodeMapper.callToCallableOrUndefined(node); + if (!isSdsEnumVariant(callable)) { + return; + } + + if (isEmpty(parametersOrEmpty(callable))) { + accept('info', 'This argument list can be removed.', { + node: argumentList, + code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST, + }) + } +} + // ----------------------------------------------------------------------------- // Unnecessary assignment // ----------------------------------------------------------------------------- diff --git a/tests/resources/validation/style/unnecessary argument list in annotation call/main.sdstest b/tests/resources/validation/style/unnecessary argument list in annotation call/main.sdstest new file mode 100644 index 000000000..8ec279c73 --- /dev/null +++ b/tests/resources/validation/style/unnecessary argument list in annotation call/main.sdstest @@ -0,0 +1,35 @@ +package tests.validation.style.unnecessaryArgumentListInAnnotationCall + +@Repeatable +annotation AnnotationWithoutParameterList + +@Repeatable +annotation AnnotationWithEmptyParameterList() + +@Repeatable +annotation AnnotationWithoutRequiredParameters(a: Int = 0, vararg b: Int) + +@Repeatable +annotation AnnotationWithRequiredParameters(a: Int) + +// $TEST$ info "This argument list can be removed." +@AnnotationWithoutParameterList»()« +// $TEST$ no info "This argument list can be removed." +@AnnotationWithoutParameterList»(1)« +// $TEST$ info "This argument list can be removed." +@AnnotationWithEmptyParameterList»()« +// $TEST$ no info "This argument list can be removed." +@AnnotationWithEmptyParameterList»(1)« +// $TEST$ info "This argument list can be removed." +@AnnotationWithoutRequiredParameters»()« +// $TEST$ no info "This argument list can be removed." +@AnnotationWithoutRequiredParameters»(1)« +// $TEST$ no info "This argument list can be removed." +@AnnotationWithRequiredParameters»()« +// $TEST$ no info "This argument list can be removed." +@AnnotationWithRequiredParameters»(1)« +// $TEST$ no info "This argument list can be removed." +@Unresolved»()« +// $TEST$ no info "This argument list can be removed." +@Unresolved»(1)« +class MyClass diff --git a/tests/resources/validation/style/unnecessary argument list in call/main.sdstest b/tests/resources/validation/style/unnecessary argument list in call/main.sdstest new file mode 100644 index 000000000..e98a8ea0d --- /dev/null +++ b/tests/resources/validation/style/unnecessary argument list in call/main.sdstest @@ -0,0 +1,71 @@ +package tests.validation.style.unnecessaryArgumentListInCall + + + +pipeline myPipeline { + +} + +annotation MyAnnotation + +class MyClass() + +enum MyEnum { + EnumVariantWithoutParameterList + EnumVariantWithEmptyParameterList() + EnumVariantWithoutRequiredParameters(a: Int = 0, vararg b: Int) + EnumVariantWithRequiredParameters(a: Int) +} + +fun myFunction() + +segment mySegment1() {} + +segment mySegment2( + callableType: () -> (), +) { + val blockLambda = () {}; + val expressionLambda = () -> 0; + + // $TEST$ no info "This argument list can be removed." + MyAnnotation»()«; + + // $TEST$ no info "This argument list can be removed." + MyClass»()«; + + // $TEST$ info "This argument list can be removed." + MyEnum.EnumVariantWithoutParameterList»()«; + // $TEST$ no info "This argument list can be removed." + MyEnum.EnumVariantWithoutParameterList»(1)«; + // $TEST$ info "This argument list can be removed." + MyEnum.EnumVariantWithEmptyParameterList»()«; + // $TEST$ no info "This argument list can be removed." + MyEnum.EnumVariantWithEmptyParameterList»(1)«; + // $TEST$ no info "This argument list can be removed." + MyEnum.EnumVariantWithoutRequiredParameters»()«; + // $TEST$ no info "This argument list can be removed." + MyEnum.EnumVariantWithoutRequiredParameters»(1)«; + // $TEST$ no info "This argument list can be removed." + MyEnum.EnumVariantWithRequiredParameters»()«; + // $TEST$ no info "This argument list can be removed." + MyEnum.EnumVariantWithRequiredParameters»(1)«; + // $TEST$ no info "This argument list can be removed." + MyEnum.Unresolved»()«; + // $TEST$ no info "This argument list can be removed." + MyEnum.Unresolved»(1)«; + + // $TEST$ no info "This argument list can be removed." + myFunction»()«; + + // $TEST$ no info "This argument list can be removed." + mySegment1»()«; + + // $TEST$ no info "This argument list can be removed." + callableType»()«; + + // $TEST$ no info "This argument list can be removed." + blockLambda»()«; + + // $TEST$ no info "This argument list can be removed." + expressionLambda»()«; +} From 738b7ada252f83b1f3210f6fbf72611a201156e1 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 16:18:26 +0200 Subject: [PATCH 2/4] feat: info if type argument list can be removed --- src/language/validation/safe-ds-validator.ts | 3 +- src/language/validation/style.ts | 39 +++++++++++++-- .../main.sdstest | 49 +++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 tests/resources/validation/style/unnecessary type argument list/main.sdstest diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index a191d07e4..7c08177d5 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -23,7 +23,7 @@ import { constraintListShouldNotBeEmpty, enumBodyShouldNotBeEmpty, enumVariantParameterListShouldNotBeEmpty, - functionResultListShouldNotBeEmpty, + functionResultListShouldNotBeEmpty, namedTypeTypeArgumentListShouldBeNeeded, segmentResultListShouldNotBeEmpty, typeParameterListShouldNotBeEmpty, unionTypeShouldNotHaveASingularTypeArgument, @@ -69,6 +69,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsExpressionLambda: [expressionLambdaMustContainUniqueNames], SdsFunction: [functionMustContainUniqueNames, functionResultListShouldNotBeEmpty], SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage], + SdsNamedType: [namedTypeTypeArgumentListShouldBeNeeded], SdsParameter: [parameterMustHaveTypeHint, parameterMustNotBeVariadicAndOptional], SdsParameterList: [ parameterListMustNotHaveOptionalAndVariadicParameters, diff --git a/src/language/validation/style.ts b/src/language/validation/style.ts index 89854e09a..3b27885cd 100644 --- a/src/language/validation/style.ts +++ b/src/language/validation/style.ts @@ -7,14 +7,14 @@ import { SdsConstraintList, SdsEnumBody, SdsEnumVariant, - SdsFunction, + SdsFunction, SdsNamedType, SdsSegment, SdsTypeParameterList, SdsUnionType, } from '../generated/ast.js'; import {ValidationAcceptor} from 'langium'; import {isEmpty} from 'radash'; -import {isRequiredParameter, parametersOrEmpty} from "../helpers/nodeProperties.js"; +import {isRequiredParameter, parametersOrEmpty, typeParametersOrEmpty} from "../helpers/nodeProperties.js"; import {SafeDsServices} from "../safe-ds-module.js"; export const CODE_STYLE_UNNECESSARY_ASSIGNMENT = 'style/unnecessary-assignment'; @@ -30,7 +30,7 @@ export const CODE_STYLE_UNNECESSARY_TYPE_PARAMETER_LIST = 'style/unnecessary-typ export const CODE_STYLE_UNNECESSARY_UNION_TYPE = 'style/unnecessary-union-type'; // ----------------------------------------------------------------------------- -// Unnecessary argument list +// Unnecessary argument lists // ----------------------------------------------------------------------------- export const annotationCallArgumentListShouldBeNeeded = ( @@ -39,6 +39,7 @@ export const annotationCallArgumentListShouldBeNeeded = ( ): void => { const argumentList = node.argumentList; if (!argumentList || !isEmpty(argumentList.arguments)) { + // If there are arguments, they are either needed or erroneous (i.e. we already show an error) return; } @@ -62,6 +63,7 @@ export const callArgumentListShouldBeNeeded = (services: SafeDsServices) => ( ): void => { const argumentList = node.argumentList; if (!argumentList || !isEmpty(argumentList.arguments)) { + // If there are arguments, they are either needed or erroneous (i.e. we already show an error) return; } @@ -79,7 +81,7 @@ export const callArgumentListShouldBeNeeded = (services: SafeDsServices) => ( } // ----------------------------------------------------------------------------- -// Unnecessary assignment +// Unnecessary assignments // ----------------------------------------------------------------------------- export const assignmentShouldHaveMoreThanWildcardsAsAssignees = ( @@ -118,7 +120,7 @@ export const enumBodyShouldNotBeEmpty = (node: SdsEnumBody, accept: ValidationAc }; // ----------------------------------------------------------------------------- -// Unnecessary constraint list +// Unnecessary constraint lists // ----------------------------------------------------------------------------- export const constraintListShouldNotBeEmpty = (node: SdsConstraintList, accept: ValidationAcceptor) => { @@ -178,6 +180,33 @@ export const segmentResultListShouldNotBeEmpty = (node: SdsSegment, accept: Vali } }; +// ----------------------------------------------------------------------------- +// Unnecessary type argument lists +// ----------------------------------------------------------------------------- + +export const namedTypeTypeArgumentListShouldBeNeeded = ( + node: SdsNamedType, + accept: ValidationAcceptor, +): void => { + const typeArgumentList = node.typeArgumentList; + if (!typeArgumentList || !isEmpty(typeArgumentList.typeArguments)) { + // If there are type arguments, they are either needed or erroneous (i.e. we already show an error) + return; + } + + const namedTypeDeclaration = node.declaration?.ref; + if (!namedTypeDeclaration) { + return; + } + + if (isEmpty(typeParametersOrEmpty(namedTypeDeclaration))) { + accept('info', 'This type argument list can be removed.', { + node: typeArgumentList, + code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST, + }) + } +} + // ----------------------------------------------------------------------------- // Unnecessary type parameter lists // ----------------------------------------------------------------------------- diff --git a/tests/resources/validation/style/unnecessary type argument list/main.sdstest b/tests/resources/validation/style/unnecessary type argument list/main.sdstest new file mode 100644 index 000000000..a2b6671b6 --- /dev/null +++ b/tests/resources/validation/style/unnecessary type argument list/main.sdstest @@ -0,0 +1,49 @@ +package tests.validation.style.unnecessaryTypeArgumentList + +class ClassWithoutTypeParameterList +class ClassWithEmptyTypeParameterList<> +class ClassWithTypeParameters + +enum Enum { + EnumVariantWithoutTypeParameterList + EnumVariantWithEmptyTypeParameterList<> + VariantWithTypeParameters +} + +fun myFunction( + // $TEST$ info "This type argument list can be removed." + a1: ClassWithoutTypeParameterList»<>«, + // $TEST$ no info "This type argument list can be removed." + a2: ClassWithoutTypeParameterList»«, + // $TEST$ info "This type argument list can be removed." + a3: ClassWithEmptyTypeParameterList»<>«, + // $TEST$ no info "This type argument list can be removed." + a4: ClassWithEmptyTypeParameterList»«, + // $TEST$ no info "This type argument list can be removed." + a5: ClassWithTypeParameters»<>«, + // $TEST$ no info "This type argument list can be removed." + a6: ClassWithTypeParameters»«, + + // $TEST$ info "This type argument list can be removed." + b1: Enum.EnumVariantWithoutTypeParameterList»<>«, + // $TEST$ no info "This type argument list can be removed." + b2: Enum.EnumVariantWithoutTypeParameterList»«, + // $TEST$ info "This type argument list can be removed." + b3: Enum.EnumVariantWithEmptyTypeParameterList»<>«, + // $TEST$ no info "This type argument list can be removed." + b4: Enum.EnumVariantWithEmptyTypeParameterList»«, + // $TEST$ no info "This type argument list can be removed." + b5: Enum.VariantWithTypeParameters»<>«, + // $TEST$ no info "This type argument list can be removed." + b6: Enum.VariantWithTypeParameters»«, + + // $TEST$ info "This type argument list can be removed." + c1: Enum»<>«, + // $TEST$ no info "This type argument list can be removed." + c2: Enum»«, + + // $TEST$ no info "This type argument list can be removed." + d1: Unresolved»<>«, + // $TEST$ no info "This type argument list can be removed." + d2: Unresolved»«, +) From 21f5d5a757e22916a61988bd343a6abe898f09ec Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 6 Oct 2023 16:26:04 +0200 Subject: [PATCH 3/4] feat: info if safe access can be removed --- src/language/validation/safe-ds-validator.ts | 3 +- src/language/validation/style.ts | 102 +++++++++++------- .../main.sdstest | 6 -- .../unnecessary safe access/main.sdstest | 10 ++ 4 files changed, 74 insertions(+), 47 deletions(-) create mode 100644 tests/resources/validation/style/unnecessary safe access/main.sdstest diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 7c08177d5..559f86aed 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -23,7 +23,7 @@ import { constraintListShouldNotBeEmpty, enumBodyShouldNotBeEmpty, enumVariantParameterListShouldNotBeEmpty, - functionResultListShouldNotBeEmpty, namedTypeTypeArgumentListShouldBeNeeded, + functionResultListShouldNotBeEmpty, memberAccessNullSafetyShouldBeNeeded, namedTypeTypeArgumentListShouldBeNeeded, segmentResultListShouldNotBeEmpty, typeParameterListShouldNotBeEmpty, unionTypeShouldNotHaveASingularTypeArgument, @@ -68,6 +68,7 @@ export const registerValidationChecks = function (services: SafeDsServices) { SdsEnumVariant: [enumVariantMustContainUniqueNames, enumVariantParameterListShouldNotBeEmpty], SdsExpressionLambda: [expressionLambdaMustContainUniqueNames], SdsFunction: [functionMustContainUniqueNames, functionResultListShouldNotBeEmpty], + SdsMemberAccess: [memberAccessNullSafetyShouldBeNeeded(services)], SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage], SdsNamedType: [namedTypeTypeArgumentListShouldBeNeeded], SdsParameter: [parameterMustHaveTypeHint, parameterMustNotBeVariadicAndOptional], diff --git a/src/language/validation/style.ts b/src/language/validation/style.ts index 3b27885cd..1e0b5c414 100644 --- a/src/language/validation/style.ts +++ b/src/language/validation/style.ts @@ -1,30 +1,35 @@ import { isSdsEnumVariant, isSdsWildcard, - SdsAnnotation, SdsAnnotationCall, - SdsAssignment, SdsCall, + SdsAnnotation, + SdsAnnotationCall, + SdsAssignment, + SdsCall, SdsClassBody, SdsConstraintList, SdsEnumBody, SdsEnumVariant, - SdsFunction, SdsNamedType, + SdsFunction, + SdsMemberAccess, + SdsNamedType, SdsSegment, SdsTypeParameterList, SdsUnionType, } from '../generated/ast.js'; -import {ValidationAcceptor} from 'langium'; -import {isEmpty} from 'radash'; -import {isRequiredParameter, parametersOrEmpty, typeParametersOrEmpty} from "../helpers/nodeProperties.js"; -import {SafeDsServices} from "../safe-ds-module.js"; +import { ValidationAcceptor } from 'langium'; +import { isEmpty } from 'radash'; +import { isRequiredParameter, parametersOrEmpty, typeParametersOrEmpty } from '../helpers/nodeProperties.js'; +import { SafeDsServices } from '../safe-ds-module.js'; +import { UnknownType } from '../typing/model.js'; export const CODE_STYLE_UNNECESSARY_ASSIGNMENT = 'style/unnecessary-assignment'; export const CODE_STYLE_UNNECESSARY_ARGUMENT_LIST = 'style/unnecessary-argument-list'; export const CODE_STYLE_UNNECESSARY_BODY = 'style/unnecessary-body'; export const CODE_STYLE_UNNECESSARY_CONSTRAINT_LIST = 'style/unnecessary-constraint-list'; export const CODE_STYLE_UNNECESSARY_ELVIS_OPERATOR = 'style/unnecessary-elvis-operator'; -export const CODE_STYLE_UNNECESSARY_SAFE_ACCESS = 'style/unnecessary-safe-access'; export const CODE_STYLE_UNNECESSARY_PARAMETER_LIST = 'style/unnecessary-parameter-list'; export const CODE_STYLE_UNNECESSARY_RESULT_LIST = 'style/unnecessary-result-list'; +export const CODE_STYLE_UNNECESSARY_SAFE_ACCESS = 'style/unnecessary-safe-access'; export const CODE_STYLE_UNNECESSARY_TYPE_ARGUMENT_LIST = 'style/unnecessary-type-argument-list'; export const CODE_STYLE_UNNECESSARY_TYPE_PARAMETER_LIST = 'style/unnecessary-type-parameter-list'; export const CODE_STYLE_UNNECESSARY_UNION_TYPE = 'style/unnecessary-union-type'; @@ -33,10 +38,7 @@ export const CODE_STYLE_UNNECESSARY_UNION_TYPE = 'style/unnecessary-union-type'; // Unnecessary argument lists // ----------------------------------------------------------------------------- -export const annotationCallArgumentListShouldBeNeeded = ( - node: SdsAnnotationCall, - accept: ValidationAcceptor, -): void => { +export const annotationCallArgumentListShouldBeNeeded = (node: SdsAnnotationCall, accept: ValidationAcceptor): void => { const argumentList = node.argumentList; if (!argumentList || !isEmpty(argumentList.arguments)) { // If there are arguments, they are either needed or erroneous (i.e. we already show an error) @@ -53,32 +55,31 @@ export const annotationCallArgumentListShouldBeNeeded = ( accept('info', 'This argument list can be removed.', { node: argumentList, code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST, - }) + }); } -} +}; -export const callArgumentListShouldBeNeeded = (services: SafeDsServices) => ( - node: SdsCall, - accept: ValidationAcceptor, -): void => { - const argumentList = node.argumentList; - if (!argumentList || !isEmpty(argumentList.arguments)) { - // If there are arguments, they are either needed or erroneous (i.e. we already show an error) - return; - } +export const callArgumentListShouldBeNeeded = + (services: SafeDsServices) => + (node: SdsCall, accept: ValidationAcceptor): void => { + const argumentList = node.argumentList; + if (!argumentList || !isEmpty(argumentList.arguments)) { + // If there are arguments, they are either needed or erroneous (i.e. we already show an error) + return; + } - const callable = services.helpers.NodeMapper.callToCallableOrUndefined(node); - if (!isSdsEnumVariant(callable)) { - return; - } + const callable = services.helpers.NodeMapper.callToCallableOrUndefined(node); + if (!isSdsEnumVariant(callable)) { + return; + } - if (isEmpty(parametersOrEmpty(callable))) { - accept('info', 'This argument list can be removed.', { - node: argumentList, - code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST, - }) - } -} + if (isEmpty(parametersOrEmpty(callable))) { + accept('info', 'This argument list can be removed.', { + node: argumentList, + code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST, + }); + } + }; // ----------------------------------------------------------------------------- // Unnecessary assignments @@ -180,14 +181,35 @@ export const segmentResultListShouldNotBeEmpty = (node: SdsSegment, accept: Vali } }; +// ----------------------------------------------------------------------------- +// Unnecessary safe access +// ----------------------------------------------------------------------------- + +export const memberAccessNullSafetyShouldBeNeeded = + (services: SafeDsServices) => + (node: SdsMemberAccess, accept: ValidationAcceptor): void => { + if (!node.isNullSafe) { + return; + } + + const receiverType = services.types.TypeComputer.computeType(node.receiver); + if (receiverType === UnknownType) { + return; + } + + if (!receiverType.isNullable) { + accept('info', 'The receiver is never null, so the safe access is unnecessary.', { + node, + code: CODE_STYLE_UNNECESSARY_SAFE_ACCESS, + }); + } + }; + // ----------------------------------------------------------------------------- // Unnecessary type argument lists // ----------------------------------------------------------------------------- -export const namedTypeTypeArgumentListShouldBeNeeded = ( - node: SdsNamedType, - accept: ValidationAcceptor, -): void => { +export const namedTypeTypeArgumentListShouldBeNeeded = (node: SdsNamedType, accept: ValidationAcceptor): void => { const typeArgumentList = node.typeArgumentList; if (!typeArgumentList || !isEmpty(typeArgumentList.typeArguments)) { // If there are type arguments, they are either needed or erroneous (i.e. we already show an error) @@ -203,9 +225,9 @@ export const namedTypeTypeArgumentListShouldBeNeeded = ( accept('info', 'This type argument list can be removed.', { node: typeArgumentList, code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST, - }) + }); } -} +}; // ----------------------------------------------------------------------------- // Unnecessary type parameter lists diff --git a/tests/resources/validation/style/unnecessary argument list in call/main.sdstest b/tests/resources/validation/style/unnecessary argument list in call/main.sdstest index e98a8ea0d..3c3eb3615 100644 --- a/tests/resources/validation/style/unnecessary argument list in call/main.sdstest +++ b/tests/resources/validation/style/unnecessary argument list in call/main.sdstest @@ -1,11 +1,5 @@ package tests.validation.style.unnecessaryArgumentListInCall - - -pipeline myPipeline { - -} - annotation MyAnnotation class MyClass() diff --git a/tests/resources/validation/style/unnecessary safe access/main.sdstest b/tests/resources/validation/style/unnecessary safe access/main.sdstest new file mode 100644 index 000000000..c1ad2d81c --- /dev/null +++ b/tests/resources/validation/style/unnecessary safe access/main.sdstest @@ -0,0 +1,10 @@ +package tests.validation.style.unnecessarySafeAccess + +pipeline test { + // $TEST$ info "The receiver is never null, so the safe access is unnecessary." + »1?.toString«(); + // $TEST$ no info "The receiver is never null, so the safe access is unnecessary." + »null?.toString«(); + // $TEST$ no info "The receiver is never null, so the safe access is unnecessary." + »unresolved?.toString«(); +} From ff57b4fa434203504d8b905d1c1919b85179de35 Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:32:06 +0000 Subject: [PATCH 4/4] style: apply automated linter fixes --- src/language/helpers/nodeProperties.ts | 2 +- src/language/validation/safe-ds-validator.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/language/helpers/nodeProperties.ts b/src/language/helpers/nodeProperties.ts index 96fc596e4..9770f2ca7 100644 --- a/src/language/helpers/nodeProperties.ts +++ b/src/language/helpers/nodeProperties.ts @@ -65,7 +65,7 @@ export const isNamedTypeArgument = (node: SdsTypeArgument): boolean => { export const isRequiredParameter = (node: SdsParameter): boolean => { return !node.defaultValue && !node.isVariadic; -} +}; export const isStatic = (node: SdsClassMember): boolean => { if (isSdsClass(node) || isSdsEnum(node)) { diff --git a/src/language/validation/safe-ds-validator.ts b/src/language/validation/safe-ds-validator.ts index 559f86aed..05420011d 100644 --- a/src/language/validation/safe-ds-validator.ts +++ b/src/language/validation/safe-ds-validator.ts @@ -18,12 +18,15 @@ import { import { annotationCallArgumentListShouldBeNeeded, annotationParameterListShouldNotBeEmpty, - assignmentShouldHaveMoreThanWildcardsAsAssignees, callArgumentListShouldBeNeeded, + assignmentShouldHaveMoreThanWildcardsAsAssignees, + callArgumentListShouldBeNeeded, classBodyShouldNotBeEmpty, constraintListShouldNotBeEmpty, enumBodyShouldNotBeEmpty, enumVariantParameterListShouldNotBeEmpty, - functionResultListShouldNotBeEmpty, memberAccessNullSafetyShouldBeNeeded, namedTypeTypeArgumentListShouldBeNeeded, + functionResultListShouldNotBeEmpty, + memberAccessNullSafetyShouldBeNeeded, + namedTypeTypeArgumentListShouldBeNeeded, segmentResultListShouldNotBeEmpty, typeParameterListShouldNotBeEmpty, unionTypeShouldNotHaveASingularTypeArgument,