diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderVisitor.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderVisitor.java index 9169493abe7..f9c9d7840ec 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderVisitor.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderVisitor.java @@ -282,7 +282,7 @@ public void onTrait(ShapeId target, ShapeId trait, Node traitValue) { onTrait(target, traitDef); } else { PendingTrait pendingTrait = new PendingTrait(trait, traitValue); - pendingTraits.computeIfAbsent(target, targetId -> new ArrayList<>()).add(pendingTrait); + addPendingTrait(target, traitValue.getSourceLocation(), trait, pendingTrait); } } @@ -294,7 +294,22 @@ public void onTrait(ShapeId target, ShapeId trait, Node traitValue) { */ public void onTrait(ShapeId target, Trait trait) { PendingTrait pending = new PendingTrait(target, trait); - pendingTraits.computeIfAbsent(target, targetId -> new ArrayList<>()).add(pending); + addPendingTrait(target, trait.getSourceLocation(), trait.toShapeId(), pending); + } + + private void addPendingTrait(ShapeId target, SourceLocation sourceLocation, ShapeId trait, PendingTrait pending) { + if (Prelude.isImmutablePublicPreludeShape(target)) { + onError(ValidationEvent.builder() + .severity(Severity.ERROR) + .eventId(Validator.MODEL_ERROR) + .sourceLocation(sourceLocation) + .shapeId(target) + .message(String.format( + "Cannot apply `%s` to an immutable prelude shape defined in `smithy.api`.", trait)) + .build()); + } else { + pendingTraits.computeIfAbsent(target, targetId -> new ArrayList<>()).add(pending); + } } /** diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java index f43eaedf5f3..90dfe1705ab 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java @@ -235,6 +235,16 @@ public static boolean isPublicPreludeShape(ToShapeId id) { return PUBLIC_PRELUDE_SHAPE_IDS.contains(toId) || PRELUDE_TRAITS.contains(toId); } + /** + * Checks if the given shape is an immutable public shape. + * + * @param id Shape to check. + * @return Returns true if the shape is immutable. + */ + static boolean isImmutablePublicPreludeShape(ToShapeId id) { + return PUBLIC_PRELUDE_SHAPE_IDS.contains(id.toShapeId()); + } + // Used by the ModelAssembler to load the prelude into another visitor. static Model getPreludeModel() { return PreludeHolder.PRELUDE; diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/apply-trait-to-prelude.errors b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/apply-trait-to-prelude.errors new file mode 100644 index 00000000000..4b7d40b8b29 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/apply-trait-to-prelude.errors @@ -0,0 +1 @@ +[ERROR] smithy.api#String: Cannot apply `smithy.api#deprecated` to an immutable prelude shape defined in `smithy.api`. | Model diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/apply-trait-to-prelude.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/apply-trait-to-prelude.smithy new file mode 100644 index 00000000000..c9872263165 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/loader/apply-trait-to-prelude.smithy @@ -0,0 +1,3 @@ +namespace com.foo + +apply smithy.api#String @deprecated