Skip to content

Commit d53bda3

Browse files
feat: port remaining validation infos that don't need partial evaluation (#607)
Closes partially #543. ### Summary of Changes Show an info if * an argument list can be removed, * a type argument list can be removed, * a safe access can be removed. --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
1 parent 4fd8d86 commit d53bda3

File tree

7 files changed

+277
-3
lines changed

7 files changed

+277
-3
lines changed

src/language/helpers/nodeProperties.ts

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export const isNamedTypeArgument = (node: SdsTypeArgument): boolean => {
6363
return Boolean(node.typeParameter);
6464
};
6565

66+
export const isRequiredParameter = (node: SdsParameter): boolean => {
67+
return !node.defaultValue && !node.isVariadic;
68+
};
69+
6670
export const isStatic = (node: SdsClassMember): boolean => {
6771
if (isSdsClass(node) || isSdsEnum(node)) {
6872
return true;

src/language/validation/safe-ds-validator.ts

+8
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ import {
1616
segmentMustContainUniqueNames,
1717
} from './names.js';
1818
import {
19+
annotationCallArgumentListShouldBeNeeded,
1920
annotationParameterListShouldNotBeEmpty,
2021
assignmentShouldHaveMoreThanWildcardsAsAssignees,
22+
callArgumentListShouldBeNeeded,
2123
classBodyShouldNotBeEmpty,
2224
constraintListShouldNotBeEmpty,
2325
enumBodyShouldNotBeEmpty,
2426
enumVariantParameterListShouldNotBeEmpty,
2527
functionResultListShouldNotBeEmpty,
28+
memberAccessNullSafetyShouldBeNeeded,
29+
namedTypeTypeArgumentListShouldBeNeeded,
2630
segmentResultListShouldNotBeEmpty,
2731
typeParameterListShouldNotBeEmpty,
2832
unionTypeShouldNotHaveASingularTypeArgument,
@@ -52,9 +56,11 @@ export const registerValidationChecks = function (services: SafeDsServices) {
5256
const checks: ValidationChecks<SafeDsAstType> = {
5357
SdsAssignment: [assignmentShouldHaveMoreThanWildcardsAsAssignees],
5458
SdsAnnotation: [annotationMustContainUniqueNames, annotationParameterListShouldNotBeEmpty],
59+
SdsAnnotationCall: [annotationCallArgumentListShouldBeNeeded],
5560
SdsArgumentList: [argumentListMustNotHavePositionalArgumentsAfterNamedArguments],
5661
SdsAttribute: [attributeMustHaveTypeHint],
5762
SdsBlockLambda: [blockLambdaMustContainUniqueNames],
63+
SdsCall: [callArgumentListShouldBeNeeded(services)],
5864
SdsCallableType: [callableTypeMustContainUniqueNames, callableTypeMustNotHaveOptionalParameters],
5965
SdsClass: [classMustContainUniqueNames],
6066
SdsClassBody: [classBodyShouldNotBeEmpty],
@@ -65,7 +71,9 @@ export const registerValidationChecks = function (services: SafeDsServices) {
6571
SdsEnumVariant: [enumVariantMustContainUniqueNames, enumVariantParameterListShouldNotBeEmpty],
6672
SdsExpressionLambda: [expressionLambdaMustContainUniqueNames],
6773
SdsFunction: [functionMustContainUniqueNames, functionResultListShouldNotBeEmpty],
74+
SdsMemberAccess: [memberAccessNullSafetyShouldBeNeeded(services)],
6875
SdsModule: [moduleDeclarationsMustMatchFileKind, moduleWithDeclarationsMustStatePackage],
76+
SdsNamedType: [namedTypeTypeArgumentListShouldBeNeeded],
6977
SdsParameter: [parameterMustHaveTypeHint, parameterMustNotBeVariadicAndOptional],
7078
SdsParameterList: [
7179
parameterListMustNotHaveOptionalAndVariadicParameters,

src/language/validation/style.ts

+106-3
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,88 @@
11
import {
2+
isSdsEnumVariant,
23
isSdsWildcard,
34
SdsAnnotation,
5+
SdsAnnotationCall,
46
SdsAssignment,
7+
SdsCall,
58
SdsClassBody,
69
SdsConstraintList,
710
SdsEnumBody,
811
SdsEnumVariant,
912
SdsFunction,
13+
SdsMemberAccess,
14+
SdsNamedType,
1015
SdsSegment,
1116
SdsTypeParameterList,
1217
SdsUnionType,
1318
} from '../generated/ast.js';
1419
import { ValidationAcceptor } from 'langium';
1520
import { isEmpty } from 'radash';
21+
import { isRequiredParameter, parametersOrEmpty, typeParametersOrEmpty } from '../helpers/nodeProperties.js';
22+
import { SafeDsServices } from '../safe-ds-module.js';
23+
import { UnknownType } from '../typing/model.js';
1624

1725
export const CODE_STYLE_UNNECESSARY_ASSIGNMENT = 'style/unnecessary-assignment';
1826
export const CODE_STYLE_UNNECESSARY_ARGUMENT_LIST = 'style/unnecessary-argument-list';
1927
export const CODE_STYLE_UNNECESSARY_BODY = 'style/unnecessary-body';
2028
export const CODE_STYLE_UNNECESSARY_CONSTRAINT_LIST = 'style/unnecessary-constraint-list';
2129
export const CODE_STYLE_UNNECESSARY_ELVIS_OPERATOR = 'style/unnecessary-elvis-operator';
22-
export const CODE_STYLE_UNNECESSARY_SAFE_ACCESS = 'style/unnecessary-safe-access';
2330
export const CODE_STYLE_UNNECESSARY_PARAMETER_LIST = 'style/unnecessary-parameter-list';
2431
export const CODE_STYLE_UNNECESSARY_RESULT_LIST = 'style/unnecessary-result-list';
32+
export const CODE_STYLE_UNNECESSARY_SAFE_ACCESS = 'style/unnecessary-safe-access';
2533
export const CODE_STYLE_UNNECESSARY_TYPE_ARGUMENT_LIST = 'style/unnecessary-type-argument-list';
2634
export const CODE_STYLE_UNNECESSARY_TYPE_PARAMETER_LIST = 'style/unnecessary-type-parameter-list';
2735
export const CODE_STYLE_UNNECESSARY_UNION_TYPE = 'style/unnecessary-union-type';
2836

2937
// -----------------------------------------------------------------------------
30-
// Unnecessary assignment
38+
// Unnecessary argument lists
39+
// -----------------------------------------------------------------------------
40+
41+
export const annotationCallArgumentListShouldBeNeeded = (node: SdsAnnotationCall, accept: ValidationAcceptor): void => {
42+
const argumentList = node.argumentList;
43+
if (!argumentList || !isEmpty(argumentList.arguments)) {
44+
// If there are arguments, they are either needed or erroneous (i.e. we already show an error)
45+
return;
46+
}
47+
48+
const annotation = node.annotation?.ref;
49+
if (!annotation) {
50+
return;
51+
}
52+
53+
const hasRequiredParameters = parametersOrEmpty(annotation).some(isRequiredParameter);
54+
if (!hasRequiredParameters) {
55+
accept('info', 'This argument list can be removed.', {
56+
node: argumentList,
57+
code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST,
58+
});
59+
}
60+
};
61+
62+
export const callArgumentListShouldBeNeeded =
63+
(services: SafeDsServices) =>
64+
(node: SdsCall, accept: ValidationAcceptor): void => {
65+
const argumentList = node.argumentList;
66+
if (!argumentList || !isEmpty(argumentList.arguments)) {
67+
// If there are arguments, they are either needed or erroneous (i.e. we already show an error)
68+
return;
69+
}
70+
71+
const callable = services.helpers.NodeMapper.callToCallableOrUndefined(node);
72+
if (!isSdsEnumVariant(callable)) {
73+
return;
74+
}
75+
76+
if (isEmpty(parametersOrEmpty(callable))) {
77+
accept('info', 'This argument list can be removed.', {
78+
node: argumentList,
79+
code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST,
80+
});
81+
}
82+
};
83+
84+
// -----------------------------------------------------------------------------
85+
// Unnecessary assignments
3186
// -----------------------------------------------------------------------------
3287

3388
export const assignmentShouldHaveMoreThanWildcardsAsAssignees = (
@@ -66,7 +121,7 @@ export const enumBodyShouldNotBeEmpty = (node: SdsEnumBody, accept: ValidationAc
66121
};
67122

68123
// -----------------------------------------------------------------------------
69-
// Unnecessary constraint list
124+
// Unnecessary constraint lists
70125
// -----------------------------------------------------------------------------
71126

72127
export const constraintListShouldNotBeEmpty = (node: SdsConstraintList, accept: ValidationAcceptor) => {
@@ -126,6 +181,54 @@ export const segmentResultListShouldNotBeEmpty = (node: SdsSegment, accept: Vali
126181
}
127182
};
128183

184+
// -----------------------------------------------------------------------------
185+
// Unnecessary safe access
186+
// -----------------------------------------------------------------------------
187+
188+
export const memberAccessNullSafetyShouldBeNeeded =
189+
(services: SafeDsServices) =>
190+
(node: SdsMemberAccess, accept: ValidationAcceptor): void => {
191+
if (!node.isNullSafe) {
192+
return;
193+
}
194+
195+
const receiverType = services.types.TypeComputer.computeType(node.receiver);
196+
if (receiverType === UnknownType) {
197+
return;
198+
}
199+
200+
if (!receiverType.isNullable) {
201+
accept('info', 'The receiver is never null, so the safe access is unnecessary.', {
202+
node,
203+
code: CODE_STYLE_UNNECESSARY_SAFE_ACCESS,
204+
});
205+
}
206+
};
207+
208+
// -----------------------------------------------------------------------------
209+
// Unnecessary type argument lists
210+
// -----------------------------------------------------------------------------
211+
212+
export const namedTypeTypeArgumentListShouldBeNeeded = (node: SdsNamedType, accept: ValidationAcceptor): void => {
213+
const typeArgumentList = node.typeArgumentList;
214+
if (!typeArgumentList || !isEmpty(typeArgumentList.typeArguments)) {
215+
// If there are type arguments, they are either needed or erroneous (i.e. we already show an error)
216+
return;
217+
}
218+
219+
const namedTypeDeclaration = node.declaration?.ref;
220+
if (!namedTypeDeclaration) {
221+
return;
222+
}
223+
224+
if (isEmpty(typeParametersOrEmpty(namedTypeDeclaration))) {
225+
accept('info', 'This type argument list can be removed.', {
226+
node: typeArgumentList,
227+
code: CODE_STYLE_UNNECESSARY_ARGUMENT_LIST,
228+
});
229+
}
230+
};
231+
129232
// -----------------------------------------------------------------------------
130233
// Unnecessary type parameter lists
131234
// -----------------------------------------------------------------------------
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package tests.validation.style.unnecessaryArgumentListInAnnotationCall
2+
3+
@Repeatable
4+
annotation AnnotationWithoutParameterList
5+
6+
@Repeatable
7+
annotation AnnotationWithEmptyParameterList()
8+
9+
@Repeatable
10+
annotation AnnotationWithoutRequiredParameters(a: Int = 0, vararg b: Int)
11+
12+
@Repeatable
13+
annotation AnnotationWithRequiredParameters(a: Int)
14+
15+
// $TEST$ info "This argument list can be removed."
16+
@AnnotationWithoutParameterList»()«
17+
// $TEST$ no info "This argument list can be removed."
18+
@AnnotationWithoutParameterList»(1)«
19+
// $TEST$ info "This argument list can be removed."
20+
@AnnotationWithEmptyParameterList»()«
21+
// $TEST$ no info "This argument list can be removed."
22+
@AnnotationWithEmptyParameterList»(1)«
23+
// $TEST$ info "This argument list can be removed."
24+
@AnnotationWithoutRequiredParameters»()«
25+
// $TEST$ no info "This argument list can be removed."
26+
@AnnotationWithoutRequiredParameters»(1)«
27+
// $TEST$ no info "This argument list can be removed."
28+
@AnnotationWithRequiredParameters»()«
29+
// $TEST$ no info "This argument list can be removed."
30+
@AnnotationWithRequiredParameters»(1)«
31+
// $TEST$ no info "This argument list can be removed."
32+
@Unresolved»()«
33+
// $TEST$ no info "This argument list can be removed."
34+
@Unresolved»(1)«
35+
class MyClass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package tests.validation.style.unnecessaryArgumentListInCall
2+
3+
annotation MyAnnotation
4+
5+
class MyClass()
6+
7+
enum MyEnum {
8+
EnumVariantWithoutParameterList
9+
EnumVariantWithEmptyParameterList()
10+
EnumVariantWithoutRequiredParameters(a: Int = 0, vararg b: Int)
11+
EnumVariantWithRequiredParameters(a: Int)
12+
}
13+
14+
fun myFunction()
15+
16+
segment mySegment1() {}
17+
18+
segment mySegment2(
19+
callableType: () -> (),
20+
) {
21+
val blockLambda = () {};
22+
val expressionLambda = () -> 0;
23+
24+
// $TEST$ no info "This argument list can be removed."
25+
MyAnnotation»()«;
26+
27+
// $TEST$ no info "This argument list can be removed."
28+
MyClass»()«;
29+
30+
// $TEST$ info "This argument list can be removed."
31+
MyEnum.EnumVariantWithoutParameterList»()«;
32+
// $TEST$ no info "This argument list can be removed."
33+
MyEnum.EnumVariantWithoutParameterList»(1)«;
34+
// $TEST$ info "This argument list can be removed."
35+
MyEnum.EnumVariantWithEmptyParameterList»()«;
36+
// $TEST$ no info "This argument list can be removed."
37+
MyEnum.EnumVariantWithEmptyParameterList»(1)«;
38+
// $TEST$ no info "This argument list can be removed."
39+
MyEnum.EnumVariantWithoutRequiredParameters»()«;
40+
// $TEST$ no info "This argument list can be removed."
41+
MyEnum.EnumVariantWithoutRequiredParameters»(1)«;
42+
// $TEST$ no info "This argument list can be removed."
43+
MyEnum.EnumVariantWithRequiredParameters»()«;
44+
// $TEST$ no info "This argument list can be removed."
45+
MyEnum.EnumVariantWithRequiredParameters»(1)«;
46+
// $TEST$ no info "This argument list can be removed."
47+
MyEnum.Unresolved»()«;
48+
// $TEST$ no info "This argument list can be removed."
49+
MyEnum.Unresolved»(1)«;
50+
51+
// $TEST$ no info "This argument list can be removed."
52+
myFunction»()«;
53+
54+
// $TEST$ no info "This argument list can be removed."
55+
mySegment1»()«;
56+
57+
// $TEST$ no info "This argument list can be removed."
58+
callableType»()«;
59+
60+
// $TEST$ no info "This argument list can be removed."
61+
blockLambda»()«;
62+
63+
// $TEST$ no info "This argument list can be removed."
64+
expressionLambda»()«;
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package tests.validation.style.unnecessarySafeAccess
2+
3+
pipeline test {
4+
// $TEST$ info "The receiver is never null, so the safe access is unnecessary."
5+
»1?.toString«();
6+
// $TEST$ no info "The receiver is never null, so the safe access is unnecessary."
7+
»null?.toString«();
8+
// $TEST$ no info "The receiver is never null, so the safe access is unnecessary."
9+
»unresolved?.toString«();
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package tests.validation.style.unnecessaryTypeArgumentList
2+
3+
class ClassWithoutTypeParameterList
4+
class ClassWithEmptyTypeParameterList<>
5+
class ClassWithTypeParameters<T>
6+
7+
enum Enum {
8+
EnumVariantWithoutTypeParameterList
9+
EnumVariantWithEmptyTypeParameterList<>
10+
VariantWithTypeParameters<T>
11+
}
12+
13+
fun myFunction(
14+
// $TEST$ info "This type argument list can be removed."
15+
a1: ClassWithoutTypeParameterList»<>«,
16+
// $TEST$ no info "This type argument list can be removed."
17+
a2: ClassWithoutTypeParameterList»<Int>«,
18+
// $TEST$ info "This type argument list can be removed."
19+
a3: ClassWithEmptyTypeParameterList»<>«,
20+
// $TEST$ no info "This type argument list can be removed."
21+
a4: ClassWithEmptyTypeParameterList»<Int>«,
22+
// $TEST$ no info "This type argument list can be removed."
23+
a5: ClassWithTypeParameters»<>«,
24+
// $TEST$ no info "This type argument list can be removed."
25+
a6: ClassWithTypeParameters»<Int>«,
26+
27+
// $TEST$ info "This type argument list can be removed."
28+
b1: Enum.EnumVariantWithoutTypeParameterList»<>«,
29+
// $TEST$ no info "This type argument list can be removed."
30+
b2: Enum.EnumVariantWithoutTypeParameterList»<Int>«,
31+
// $TEST$ info "This type argument list can be removed."
32+
b3: Enum.EnumVariantWithEmptyTypeParameterList»<>«,
33+
// $TEST$ no info "This type argument list can be removed."
34+
b4: Enum.EnumVariantWithEmptyTypeParameterList»<Int>«,
35+
// $TEST$ no info "This type argument list can be removed."
36+
b5: Enum.VariantWithTypeParameters»<>«,
37+
// $TEST$ no info "This type argument list can be removed."
38+
b6: Enum.VariantWithTypeParameters»<Int>«,
39+
40+
// $TEST$ info "This type argument list can be removed."
41+
c1: Enum»<>«,
42+
// $TEST$ no info "This type argument list can be removed."
43+
c2: Enum»<Int>«,
44+
45+
// $TEST$ no info "This type argument list can be removed."
46+
d1: Unresolved»<>«,
47+
// $TEST$ no info "This type argument list can be removed."
48+
d2: Unresolved»<Int>«,
49+
)

0 commit comments

Comments
 (0)