Skip to content

Commit ad9c185

Browse files
Add exactMatch config to RepeatingShapeNameValidator
This adds a configuration option to the repeating shape name validator to specify that it should be an exact match rather than a prefix check.
1 parent ae77b32 commit ad9c185

File tree

4 files changed

+81
-7
lines changed

4 files changed

+81
-7
lines changed

docs/source/1.0/guides/model-linters.rst

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,10 @@ Example:
357357
.. _RepeatedShapeName:
358358

359359
RepeatedShapeName
360-
==================
360+
=================
361361

362362
Validates that :ref:`structure` member names and :ref:`union` member
363-
names do not repeat their container shape names.
363+
names do not case-insensitively repeat their container shape names.
364364

365365
As an example, if a structure named "Table" contained a member named
366366
"TableName", then this validator would emit a WARNING event.
@@ -372,6 +372,19 @@ Rationale
372372
Default severity
373373
``WARNING``
374374

375+
Configuration
376+
.. list-table::
377+
:header-rows: 1
378+
:widths: 20 20 60
379+
380+
* - Property
381+
- Type
382+
- Description
383+
* - exactMatch
384+
- ``boolean``
385+
- If set to true, the validator will only warn if the member name
386+
is case-insensitively identical to the containing shape's name.
387+
375388

376389
.. _InputOutputStructureReuse:
377390

smithy-linters/src/main/java/software/amazon/smithy/linters/RepeatedShapeNameValidator.java

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Locale;
2222
import java.util.stream.Collectors;
2323
import software.amazon.smithy.model.Model;
24+
import software.amazon.smithy.model.node.NodeMapper;
2425
import software.amazon.smithy.model.shapes.Shape;
2526
import software.amazon.smithy.model.shapes.StructureShape;
2627
import software.amazon.smithy.model.shapes.UnionShape;
@@ -34,12 +35,33 @@
3435
*/
3536
public final class RepeatedShapeNameValidator extends AbstractValidator {
3637

38+
public static final class Config {
39+
private boolean exactMatch = false;
40+
41+
public boolean getExactMatch() {
42+
return exactMatch;
43+
}
44+
45+
public void setExactMatch(boolean exactMatch) {
46+
this.exactMatch = exactMatch;
47+
}
48+
}
49+
3750
public static final class Provider extends ValidatorService.Provider {
3851
public Provider() {
39-
super(RepeatedShapeNameValidator.class, RepeatedShapeNameValidator::new);
52+
super(RepeatedShapeNameValidator.class, node -> {
53+
Config config = new NodeMapper().deserialize(node, Config.class);
54+
return new RepeatedShapeNameValidator(config);
55+
});
4056
}
4157
}
4258

59+
private final Config config;
60+
61+
private RepeatedShapeNameValidator(Config config) {
62+
this.config = config;
63+
}
64+
4365
@Override
4466
public List<ValidationEvent> validate(Model model) {
4567
List<ValidationEvent> events = new ArrayList<>();
@@ -54,15 +76,30 @@ private List<ValidationEvent> validateNames(Model model, Shape shape, Collection
5476
String shapeName = shape.getId().getName();
5577
String lowerCaseShapeName = shapeName.toLowerCase(Locale.US);
5678
return memberNames.stream()
57-
.filter(memberName -> memberName.toLowerCase(Locale.US).startsWith(lowerCaseShapeName))
79+
.filter(memberName -> nameConflicts(lowerCaseShapeName, memberName))
5880
.map(memberName -> repeatedMemberName(model, shape, shapeName, memberName))
5981
.collect(Collectors.toList());
6082
}
6183

84+
private boolean nameConflicts(String lowerCaseShapeName, String memberName) {
85+
String lowerCaseMemberName = memberName.toLowerCase(Locale.US);
86+
if (config.getExactMatch()) {
87+
return lowerCaseMemberName.equals(lowerCaseShapeName);
88+
} else {
89+
return lowerCaseMemberName.startsWith(lowerCaseShapeName);
90+
}
91+
}
92+
6293
private ValidationEvent repeatedMemberName(Model model, Shape shape, String shapeName, String memberName) {
6394
Shape member = model.expectShape(shape.getId().withMember(memberName));
64-
return warning(member, String.format(
65-
"The `%s` %s shape repeats its name in the member `%s`; %2$s member names should not be "
66-
+ "prefixed with the %2$s name.", shapeName, shape.getType(), memberName));
95+
if (config.getExactMatch()) {
96+
return warning(member, String.format(
97+
"The `%s` %s shape repeats its name in the member `%s`; %2$s member names should not be "
98+
+ "equal to the %2$s name.", shapeName, shape.getType(), memberName));
99+
} else {
100+
return warning(member, String.format(
101+
"The `%s` %s shape repeats its name in the member `%s`; %2$s member names should not be "
102+
+ "prefixed with the %2$s name.", shapeName, shape.getType(), memberName));
103+
}
67104
}
68105
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[WARNING] smithy.example#RepeatingStructure$repeatingStructure: The `RepeatingStructure` structure shape repeats its name in the member `repeatingStructure`; structure member names should not be equal to the structure name. | RepeatedShapeName
2+
[WARNING] smithy.example#RepeatingUnion$repeatingUnion: The `RepeatingUnion` union shape repeats its name in the member `repeatingUnion`; union member names should not be equal to the union name. | RepeatedShapeName
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
metadata validators = [{
2+
name: "RepeatedShapeName",
3+
configuration: {
4+
exactMatch: true
5+
}
6+
}]
7+
8+
namespace smithy.example
9+
10+
structure RepeatingStructure {
11+
repeatingStructure: String,
12+
13+
// This is fine because it's not an exact match
14+
repeatingStructureMember: String,
15+
}
16+
17+
union RepeatingUnion {
18+
repeatingUnion: String,
19+
20+
// This is fine because it's not an exact match
21+
repeatingUnionMember: String,
22+
}

0 commit comments

Comments
 (0)