diff --git a/designs/mixins.md b/designs/mixins.md index 77b34777393..fc10d05f0af 100644 --- a/designs/mixins.md +++ b/designs/mixins.md @@ -1,7 +1,7 @@ # Smithy Mixins -This proposal defines _mixins_, a modeling mechanism that allows for structure -and union definition reuse. +This proposal defines _mixins_, a modeling mechanism that allows for shape +definition reuse. > Note: mixins will be considered for Smithy IDL 1.1, which treats commas as > optional whitespace. @@ -78,8 +78,8 @@ over time. ## Proposal This proposal introduces *mixins* to reduce the amount of repetition in -structures and unions, reduce copy/paste errors, and provide the ability to -define reusable partial structures and unions. +shapes, reduce copy/paste errors, and provide the ability to define reusable +partial shapes. ### Goals @@ -98,8 +98,7 @@ define reusable partial structures and unions. ### Overview -A mixin is a structure or union marked with the `@mixin` trait -(`smithy.api#mixin`): +A mixin is a shape marked with the `@mixin` trait(`smithy.api#mixin`): ``` $version: "1.1" @@ -114,14 +113,14 @@ structure CityResourceInput { } ``` -Adding a mixin to a structure or union shape causes the members and traits of -a shape to be copied into the shape. Mixins can be added to a shape using +Adding a mixin to a shape causes the members and traits of the other +shape to be copied into the shape. Mixins can be added to a shape using `with` followed by any number of shape IDs. Each shape ID MUST target a -shape marked with the `@mixin` trait. Structure shapes can only use structure -mixins, and union shapes can only use union mixins. +shape marked with the `@mixin` trait. Shapes can only use mixins that +are of the same shape type. ``` -structure GetCityInput with CityResourceInput { +structure GetCityInput with [CityResourceInput] { foo: String } ``` @@ -129,10 +128,10 @@ structure GetCityInput with CityResourceInput { Multiple mixins can be applied: ``` -structure GetAnotherCityInput with +structure GetAnotherCityInput with [ CityResourceInput SomeOtherMixin -{ +] { foo: String } ``` @@ -146,11 +145,11 @@ structure MixinA { } @mixin -structure MixinB with MixinA { +structure MixinB with [MixinA] { b: String } -structure C with MixinB { +structure C with [MixinB] { c: String } ``` @@ -173,7 +172,7 @@ structure C { } ``` -Unions can be mixins and use mixins too. +Other shape types can be mixins and use mixins too. ``` @mixin @@ -182,7 +181,7 @@ union UserActions { unsubscribe: UnsubscribeAction, } -union AdminActions with UserAction { +union AdminActions with [UserAction] { banUser: BanUserAction, promoteToAdmin: PromoteToAdminAction } @@ -205,7 +204,7 @@ structure UserInfo { userId: String } -structure UserSummary with UserInfo {} +structure UserSummary with [UserInfo] {} ``` Is equivalent to the following flattened structure because it inherits the @@ -231,7 +230,7 @@ structure UserInfo { /// Specific documentation @tags(["replaced-tags"]) -structure UserSummary with UserInfo {} +structure UserSummary with [UserInfo] {} ``` Is equivalent to the following flattened structure because it inherits the @@ -269,13 +268,11 @@ structure StructB {} /// C @threeTrait @mixin -structure StructC with - StructA - StructB {} +structure StructC with [StructA, StructB] {} /// D @fourTrait -structure StructD with StructC {} +structure StructD with [StructC] {} ``` Is equivalent to the following flattened structure: @@ -326,7 +323,7 @@ structure PrivateMixin { foo: String } -structure PublicShape with PrivateMixin {} +structure PublicShape with [PrivateMixin] {} ``` `PublicShape` is equivalent to the following flattened structure: @@ -350,13 +347,11 @@ can be referred to outside of `smithy.example`. The members and traits applied to members of a mixin are copied onto the target shape. It is sometimes necessary to provide a more specific trait value for a copied member or to add traits only to a specific copy of a member. Traits can -be applied to these members in the JSON AST using the `apply` type and in the +be added on to these members like any other member. Additionally, Traits can be +applied to these members in the JSON AST using the `apply` type and in the Smithy IDL using `apply` statements. -> Note: using the `apply` type and `apply` statements on members that are -copied from mixin members _do not_ merge the applied trait value with the -traits applied to the original mixin member. Applying traits to copied mixin -members completely supersedes any traits applied to mixin members. +> Note: Traits applied to shapes supersede any traits inherited from mixins. #### Applying traits in the JSON AST @@ -404,6 +399,45 @@ applied to `MyStruct`. The `mixinMember` member of `MyMixin` has a } ``` +Traits can also be applied to copies of mixin members as if they were local +members. + +```json +{ + "smithy": "1.1", + "shapes": { + "smithy.example#MyMixin": { + "type": "structure", + "members": { + "mixinMember": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "Generic docs" + } + } + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MyStruct": { + "type": "structure", + "members": { + "mixinMember": { + "target": "smithy.api#String", + "traits": { + "smithy.api#documentation": "Specific docs" + } + } + }, + "mixins": [ + {"target": "smithy.example#MyMixin"} + ] + } + } +} +``` + #### Applying traits in the IDL The previous example can be defined in the Smithy IDL using an @@ -425,10 +459,28 @@ structure MyMixin { mixinMember: String } -structure MyStruct with MyMixin {} +structure MyStruct with [MyMixin] {} apply MyStruct$mixinMember @documentation("Specific docs") ``` +Alternatively, the member can be redefined if it targets the same shape: + +```smithy +$version: "1.1" +namespace smithy.example + +@mixin +structure MyMixin { + /// Generic docs + mixinMember: String +} + +structure MyStruct with [MyMixin] { + /// Specific docs + mixinMember: String +} +``` + ### Mixins are not code generated @@ -478,17 +530,18 @@ Mixins MUST NOT introduce circular references. The following model is invalid: ``` @mixin -structure CycleA with CycleB {} +structure CycleA with [CycleB] {} @mixin -structure CycleB with CycleA {} +structure CycleB with [CycleA] {} ``` ### Mixin members MUST NOT conflict The list of mixins applied to a structure or union MUST NOT attempt to define -members that use the same member name. The following model is invalid: +members that use the same member name with different targets. The following model +is invalid: ``` @mixin @@ -498,12 +551,10 @@ structure A1 { @mixin structure A2 { - a: String + a: Integer } -structure Invalid with - A1 - A2 {} +structure Invalid with [A1, A2] {} ``` The following model is also invalid, but not specifically because of mixins. @@ -518,27 +569,52 @@ structure A1 { @mixin structure A2 { - A: String + A: Integer } -structure Invalid with - A1 - A2 {} +structure Invalid with [A1, A2] {} +``` + +Members that are mixed into shapes MAY be redefined if and only if each +redefined member targets the same shape. Traits applied to redefined members +supersede any traits inherited from mixins. + +``` +@mixin +structure A1 { + @private + a: String +} + +@mixin +structure A2 { + @required + a: String +} + +structure Valid with [A1, A2] {} ``` ### Mixins in the IDL -To support mixins, `structure_statement` and `union_statement` ABNF rules +To support mixins, shape ABNF rules will be updated to contain an optional `mixins` production -that comes after the shape name and before `{`. Each shape ID referenced in +that comes after the shape name. Each shape ID referenced in the `mixins` production MUST target a shape of the same type as the shape being defined and MUST be marked with the `@mixin` trait. ``` -structure_statement = "structure" ws identifier ws [mixins ws] structure_members -union_statement = "union" ws identifier ws [mixins ws] union_members -mixins = "with" 1*(ws shape_id) +simple_shape_statement = simple_type_name ws identifier [mixins] +list_statement = "list" ws identifier [mixins] ws shape_members +set_statement = "set" ws identifier [mixins] ws shape_members +map_statement = "map" ws identifier [mixins] ws shape_members +structure_statement = "structure" ws identifier [mixins] ws structure_members +union_statement = "union" ws identifier [mixins] ws union_members +service_statement = "service" ws identifier [mixins] ws node_object +operation_statement = "operation" ws identifier [mixins] ws node_object +resource_statement = "resource" ws identifier [mixins] ws node_object +mixins = sp "with" ws "[" 1*(ws shape_id) ws "]" ``` @@ -547,7 +623,7 @@ mixins = "with" 1*(ws shape_id) Mixins are defined in the JSON AST using the `mixins` property of structure and union shapes. The `mixins` property is a list of objects. To match every other shape target used in the AST, the object supports a single member named -`target` which defines the absolute shape ID of a structure or union marked +`target` which defines the absolute shape ID of a shape marked with the `smithy.api#mixin` trait. ```json @@ -586,7 +662,7 @@ with the `smithy.api#mixin` trait. ### Mixins in selectors A new relationship is introduced to selectors called `mixin` that traverses from -structure and union shapes to every mixin applied to them. The members of each +shapes to every mixin applied to them. The members of each applied mixin are connected to the structure or union through a normal `member` relationship. @@ -599,7 +675,7 @@ structure PaginatedInput { pageSize: Integer } -structure ListSomethingInput with PaginatedInput { +structure ListSomethingInput with [PaginatedInput] { nameFilter: String } ``` @@ -676,10 +752,10 @@ structure PaginatedInput { pageSize: Integer } -structure ListSomethingInput with +structure ListSomethingInput with [ PaginatedInput FilteredByName -{ +] { sizeFilter: Integer } ``` @@ -692,12 +768,127 @@ The members are ordered as follows: - `sizeFilter` +### Mixins on shapes with non-member properties + +Some shapes don't have members, but do have other properties. Adding a mixin +to such a shape merges the properties of each mixin into the local shape. Only +certain properties may be defined in the mixin shapes. See the sections below +for which properties are permitted for each shape type. + +Scalar properties defined in the local shape are kept, and non-scalar +properties are merged. When merging map properties, the values for local keys +are kept. The ordering of merged lists / sets follows the same ordering as +members. + +#### Service mixins + +Service shapes with the `@mixin` trait may define any property. For example, +in the following model: + +``` +operation OperationA {} + +@mixin +service A { + version: "A" + operations: [OperationA] +} + +operation OperationB {} + +@mixin +service B with [A] { + version: "B" + rename: { + "smithy.example#OperationA": "OperA" + "smithy.example#OperationB": "OperB" + } + operations: [OperationB] +} + +operation OperationC {} + +service C with [B] { + version: "C" + rename: { + "smithy.example#OperationA": "OpA" + "smithy.example#OperationC": "OpC" + } + operations: [OperationC] +} +``` + +The `version` property of the local shape is kept and the `rename` and +`operations` properties are merged. This is equivalent to the following: + +``` +operation OperationA {} + +operation OperationB {} + +operation OperationC {} + +service C { + version: "C" + rename: { + "smithy.example#OperationA": "OpA" + "smithy.example#OperationB": "OperB" + "smithy.example#OperationC": "OpC" + } + operations: [OperationA, OperationB, OperationC] +} +``` + +#### Resource mixins + +Resource shapes with the `@mixin` trait MAY NOT define any properties. This is +because every property of a resource shape is intrinsically tied to its set of +identifiers. Changing these identifiers would invalidate every other property +of a given resource. + +Example: + +``` +@mixin +@internal +resource MixinResource {} + +resource MixedResource with [MixinResource] {} +``` + +#### Operation mixins + +Operation shapes with the `@mixin` trait MAY NOT define an `input` or `output` +shape other than `smithy.api#Unit`. This is because allowing input and output +shapes to be shared goes against the goal of the `@input` and `@output` traits. + +Operation shapes with the `@mixin` trait MAY define the errors shape. + +Example: + +``` +@mixin +operation MixinOperation { + errors: [MixinError] +} + +operation MixedOperation with [MixinOperation] { + error: [MixedError] +} + +@error("client") +structure MixinError {} + +@error("client") +structure MixedError {} +``` + ### `@mixin` trait The `@mixin` trait is a structured trait defined in the Smithy prelude as: ``` -@trait(selector: ":is(structure, union)") +@trait(selector: ":not(member)") structure mixin { localTraits: LocalMixinTraitList } @@ -818,7 +1009,7 @@ mixin UserActions { subscribe: SubscribeAction, } -union AdminActions with UserAction {} +union AdminActions with [UserAction] {} ``` The `required` trait on `UserActions$subscribe` is valid, but it makes it so @@ -861,7 +1052,7 @@ structure FooMixin { otherMember: String } -structure ApplicationOfFooMixin with FooMixin { +structure ApplicationOfFooMixin with [FooMixin] { // Remove the required trait from this member. @override([omitTraits: [required]]) someMember: String @@ -885,7 +1076,7 @@ structure FooMixinOptional { } @mixin -structure FooMixinRequired with FooMixinOptional {} +structure FooMixinRequired with [FooMixinOptional] {} apply FooMixinRequired$someMember @required ``` diff --git a/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/model-with-mixins.smithy b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/model-with-mixins.smithy index 2b78022d96b..4e109f46b1f 100644 --- a/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/model-with-mixins.smithy +++ b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/model-with-mixins.smithy @@ -7,6 +7,6 @@ structure Mixin { foo: String } -structure UsesMixin with Mixin { +structure UsesMixin with [Mixin] { baz: String } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/AbstractMutableModelFile.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/AbstractMutableModelFile.java index a1481aa6cb5..5862ce8dd72 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/AbstractMutableModelFile.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/AbstractMutableModelFile.java @@ -228,30 +228,40 @@ private PendingShape createPendingShape( for (ShapeId mixin : mixins) { Shape mixinShape = shapeMap.get(mixin); for (MemberShape member : mixinShape.members()) { + ShapeId targetId = builder.getId().withMember(member.getMemberName()); + Map introducedTraits = traitContainer.getTraitsForShape(targetId); + + MemberShape introducedMember = null; if (builderMembers.containsKey(member.getMemberName())) { - // Members cannot be redefined. - MemberShape.Builder conflict = builderMembers.get(member.getMemberName()); - events.add(ValidationEvent.builder() - .severity(Severity.ERROR) - .id(Validator.MODEL_ERROR) - .shapeId(conflict.getId()) - .sourceLocation(conflict.getSourceLocation()) - .message("Member conflicts with an inherited mixin member: " + member.getId()) - .build()); - } else { - // Build local member copies before adding mixins if traits - // were introduced to inherited mixin members. - ShapeId targetId = builder.getId().withMember(member.getMemberName()); - Map introducedTraits = traitContainer.getTraitsForShape(targetId); - if (!introducedTraits.isEmpty()) { - builder.addMember(MemberShape.builder() - .id(targetId) - .target(member.getTarget()) - .source(member.getSourceLocation()) - .addTraits(introducedTraits.values()) - .addMixin(member) + introducedMember = builderMembers.get(member.getMemberName()) + .addMixin(member) + .build(); + + if (!introducedMember.getTarget().equals(member.getTarget())) { + // Members cannot be redefined if their targets conflict. + MemberShape.Builder conflict = builderMembers.get(member.getMemberName()); + events.add(ValidationEvent.builder() + .severity(Severity.ERROR) + .id(Validator.MODEL_ERROR) + .shapeId(conflict.getId()) + .sourceLocation(conflict.getSourceLocation()) + .message("Member conflicts with an inherited mixin member: " + member.getId()) .build()); } + } else if (!introducedTraits.isEmpty()) { + // Build local member copies before adding mixins if traits + // were introduced to inherited mixin members. + introducedMember = MemberShape.builder() + .id(targetId) + .target(member.getTarget()) + .source(member.getSourceLocation()) + .addTraits(introducedTraits.values()) + .addMixin(member) + .build(); + } + + if (introducedMember != null) { + builder.addMember(introducedMember); } } builder.addMixin(mixinShape); diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/AstModelLoader.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/AstModelLoader.java index e23b8156203..b5481390f3a 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/AstModelLoader.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/AstModelLoader.java @@ -223,6 +223,10 @@ private void loadMember(FullyResolvedModelFile modelFile, ShapeId id, ObjectNode modelFile.onShape(builder); } + private void loadOptionalMember(FullyResolvedModelFile modelFile, ShapeId id, ObjectNode node, String member) { + node.getObjectMember(member).ifPresent(targetNode -> loadMember(modelFile, id, targetNode)); + } + private void loadCollection( ShapeId id, ObjectNode node, @@ -231,16 +235,18 @@ private void loadCollection( ) { LoaderUtils.checkForAdditionalProperties(node, id, COLLECTION_PROPERTY_NAMES, modelFile.events()); applyShapeTraits(id, node, modelFile); - loadMember(modelFile, id.withMember("member"), node.expectObjectMember("member")); + loadOptionalMember(modelFile, id.withMember("member"), node, "member"); modelFile.onShape(builder.id(id).source(node.getSourceLocation())); + addMixins(id, node, modelFile); } private void loadMap(ShapeId id, ObjectNode node, FullyResolvedModelFile modelFile) { LoaderUtils.checkForAdditionalProperties(node, id, MAP_PROPERTY_NAMES, modelFile.events()); - loadMember(modelFile, id.withMember("key"), node.expectObjectMember("key")); - loadMember(modelFile, id.withMember("value"), node.expectObjectMember("value")); + loadOptionalMember(modelFile, id.withMember("key"), node, "key"); + loadOptionalMember(modelFile, id.withMember("value"), node, "value"); applyShapeTraits(id, node, modelFile); modelFile.onShape(MapShape.builder().id(id).source(node.getSourceLocation())); + addMixins(id, node, modelFile); } private void loadOperation(ShapeId id, ObjectNode node, FullyResolvedModelFile modelFile) { @@ -253,6 +259,7 @@ private void loadOperation(ShapeId id, ObjectNode node, FullyResolvedModelFile m loadOptionalTarget(modelFile, id, node, "input").ifPresent(builder::input); loadOptionalTarget(modelFile, id, node, "output").ifPresent(builder::output); modelFile.onShape(builder); + addMixins(id, node, modelFile); } private void loadResource(ShapeId id, ObjectNode node, FullyResolvedModelFile modelFile) { @@ -279,6 +286,7 @@ private void loadResource(ShapeId id, ObjectNode node, FullyResolvedModelFile mo }); modelFile.onShape(builder); + addMixins(id, node, modelFile); } private void loadService(ShapeId id, ObjectNode node, FullyResolvedModelFile modelFile) { @@ -291,6 +299,7 @@ private void loadService(ShapeId id, ObjectNode node, FullyResolvedModelFile mod loadServiceRenameIntoBuilder(builder, node); builder.addErrors(loadOptionalTargetList(modelFile, id, node, ERRORS)); modelFile.onShape(builder); + addMixins(id, node, modelFile); } static void loadServiceRenameIntoBuilder(ServiceShape.Builder builder, ObjectNode node) { @@ -308,6 +317,7 @@ private void loadSimpleShape( LoaderUtils.checkForAdditionalProperties(node, id, SIMPLE_PROPERTY_NAMES, modelFile.events()); applyShapeTraits(id, node, modelFile); modelFile.onShape(builder.id(id).source(node.getSourceLocation())); + addMixins(id, node, modelFile); } private void loadStructure(ShapeId id, ObjectNode node, FullyResolvedModelFile modelFile) { @@ -328,7 +338,10 @@ private void finishLoadingStructOrUnionMembers(ShapeId id, ObjectNode node, Full for (Map.Entry entry : memberObject.getStringMap().entrySet()) { loadMember(modelFile, id.withMember(entry.getKey()), entry.getValue().expectObjectNode()); } + addMixins(id, node, modelFile); + } + private void addMixins(ShapeId id, ObjectNode node, FullyResolvedModelFile modelFile) { ArrayNode mixins = node.getArrayMember(MIXINS).orElse(Node.arrayNode()); for (ObjectNode mixin : mixins.getElementsAs(ObjectNode.class)) { modelFile.addPendingMixin(id, loadReferenceBody(modelFile, id, mixin)); diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java index a6630fd91f0..098ac7efd74 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java @@ -66,7 +66,6 @@ import software.amazon.smithy.model.traits.TraitFactory; import software.amazon.smithy.model.validation.Severity; import software.amazon.smithy.model.validation.ValidationEvent; -import software.amazon.smithy.model.validation.ValidationUtils; import software.amazon.smithy.model.validation.Validator; import software.amazon.smithy.utils.ListUtils; import software.amazon.smithy.utils.SetUtils; @@ -443,7 +442,6 @@ private void parseShape(List traits) { } addTraits(id, traits); - clearPendingDocs(); ws(); } @@ -454,22 +452,21 @@ private ShapeId parseShapeName() { private void parseSimpleShape(ShapeId id, SourceLocation location, AbstractShapeBuilder builder) { modelFile.onShape(builder.source(location).id(id)); + parseMixins(id); } // See parseMap for information on why members are parsed before the // list/set is registered with the ModelFile. private void parseCollection(ShapeId id, SourceLocation location, CollectionShape.Builder builder) { - ws(); builder.id(id).source(location); + parseMixins(id); parseMembers(id, SetUtils.of("member")); modelFile.onShape(builder.id(id)); + clearPendingDocs(); } private void parseMembers(ShapeId id, Set requiredMembers) { Set definedMembers = new HashSet<>(); - Set remaining = requiredMembers.isEmpty() - ? requiredMembers - : new HashSet<>(requiredMembers); ws(); expect('{'); @@ -480,7 +477,7 @@ private void parseMembers(ShapeId id, Set requiredMembers) { break; } - parseMember(id, requiredMembers, definedMembers, remaining); + parseMember(id, requiredMembers, definedMembers); // Clears out any previously captured documentation // comments that may have been found when parsing the member. @@ -493,15 +490,10 @@ private void parseMembers(ShapeId id, Set requiredMembers) { expect('}'); } - if (!remaining.isEmpty()) { - throw syntax(id, "Missing required members of shape `" + id + "`: [" - + ValidationUtils.tickedList(remaining) + ']'); - } - expect('}'); } - private void parseMember(ShapeId parent, Set required, Set defined, Set remaining) { + private void parseMember(ShapeId parent, Set allowed, Set defined) { // Parse optional member traits. List memberTraits = parseDocsAndTraits(); SourceLocation memberLocation = currentLocation(); @@ -513,10 +505,9 @@ private void parseMember(ShapeId parent, Set required, Set defin } defined.add(memberName); - remaining.remove(memberName); // Only enforce "allowedMembers" if it isn't empty. - if (!required.isEmpty() && !required.contains(memberName)) { + if (!allowed.isEmpty() && !allowed.contains(memberName)) { throw syntax(parent, "Unexpected member of " + parent + ": '" + memberName + '\''); } @@ -545,8 +536,10 @@ private void parseMapStatement(ShapeId id, SourceLocation location) { // on a builder. This does not suffer the same error messages as // structures/unions because list/set/map have a fixed and required // set of members that must be provided. + parseMixins(id); parseMembers(id, SetUtils.of("key", "value")); modelFile.onShape(MapShape.builder().id(id).source(location)); + clearPendingDocs(); } private void parseStructuredShape( @@ -561,14 +554,14 @@ private void parseStructuredShape( // "Member `foo.baz#Foo$Baz` cannot be added to software.amazon.smithy.model.shapes.OperationShape$Builder" modelFile.onShape(builder.id(id).source(location)); - ws(); - // Parse optional "with" statements to add mixins, but only if it's supported by the version. parseMixins(id); parseMembers(id, Collections.emptySet()); + clearPendingDocs(); } private void parseMixins(ShapeId id) { + sp(); if (peek() != 'w') { return; } @@ -584,14 +577,20 @@ private void parseMixins(ShapeId id) { } ws(); + expect('['); + ws(); + do { String target = ParserUtils.parseShapeId(this); modelFile.addForwardReference(target, resolved -> modelFile.addPendingMixin(id, resolved)); ws(); - } while (peek() != '{'); + } while (peek() != ']'); + expect(']'); + clearPendingDocs(); } private void parseOperationStatement(ShapeId id, SourceLocation location) { + parseMixins(id); OperationShape.Builder builder = OperationShape.builder().id(id).source(location); parseProperties(id, propertyName -> { switch (propertyName) { @@ -611,6 +610,7 @@ private void parseOperationStatement(ShapeId id, SourceLocation location) { } }); modelFile.onShape(builder); + clearPendingDocs(); } private void parseProperties(ShapeId id, Consumer valueParser) { @@ -690,6 +690,7 @@ private void parseIdList(Consumer consumer) { } private void parseServiceStatement(ShapeId id, SourceLocation location) { + parseMixins(id); ws(); ServiceShape.Builder builder = new ServiceShape.Builder().id(id).source(location); ObjectNode shapeNode = IdlNodeParser.parseObjectNode(this, id.toString()); @@ -700,6 +701,7 @@ private void parseServiceStatement(ShapeId id, SourceLocation location) { optionalIdList(shapeNode, RESOURCES_KEY, builder::addResource); optionalIdList(shapeNode, ERRORS_KEYS, builder::addError); AstModelLoader.loadServiceRenameIntoBuilder(builder, shapeNode); + clearPendingDocs(); } private void optionalId(ObjectNode node, String name, Consumer consumer) { @@ -718,6 +720,7 @@ private void optionalIdList(ObjectNode node, String name, Consumer cons } private void parseResourceStatement(ShapeId id, SourceLocation location) { + parseMixins(id); ws(); ResourceShape.Builder builder = ResourceShape.builder().id(id).source(location); modelFile.onShape(builder); @@ -742,6 +745,7 @@ private void parseResourceStatement(ShapeId id, SourceLocation location) { modelFile.addForwardReference(target.getValue(), targetId -> builder.addIdentifier(name, targetId)); } }); + clearPendingDocs(); } // "//" *(not_newline) diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/CollectionShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/CollectionShape.java index 9f2d1c8befb..3635f7ef7b1 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/CollectionShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/CollectionShape.java @@ -19,7 +19,8 @@ import java.util.Collections; import java.util.Objects; import java.util.function.Consumer; -import software.amazon.smithy.utils.SmithyBuilder; +import software.amazon.smithy.model.SourceLocation; +import software.amazon.smithy.model.traits.Trait; /** * Abstract class representing Set and List shapes. @@ -28,9 +29,9 @@ public abstract class CollectionShape extends Shape { private final MemberShape member; - CollectionShape(Builder builder) { + CollectionShape(Builder builder) { super(builder, false); - member = SmithyBuilder.requiredState("member", builder.member); + member = builder.member != null ? builder.member : getRequiredMixinMember(builder, "member"); ShapeId expected = getId().withMember("member"); if (!member.getId().equals(expected)) { throw new IllegalArgumentException(String.format( @@ -124,5 +125,27 @@ public B member(ShapeId target, Consumer memberUpdater) { public final B addMember(MemberShape member) { return member(member); } + + @Override + public B flattenMixins() { + for (Shape mixin : getMixins().values()) { + SourceLocation location = getSourceLocation(); + Collection localTraits = Collections.emptyList(); + MemberShape mixinMember = ((CollectionShape) mixin).getMember(); + MemberShape existing = member; + if (existing != null) { + localTraits = existing.getIntroducedTraits().values(); + location = existing.getSourceLocation(); + } + member = MemberShape.builder() + .id(getId().withMember(mixinMember.getMemberName())) + .target(mixinMember.getTarget()) + .addTraits(mixinMember.getAllTraits().values()) + .addTraits(localTraits) + .source(location) + .build(); + } + return super.flattenMixins(); + } } } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/EntityShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/EntityShape.java index 0b04993c2f7..136c8bb8ed5 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/EntityShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/EntityShape.java @@ -16,6 +16,8 @@ package software.amazon.smithy.model.shapes; import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Set; import software.amazon.smithy.utils.BuilderRef; @@ -25,12 +27,38 @@ public abstract class EntityShape extends Shape { private final Set resources; + private final Set introducedResources; private final Set operations; + private final Set introducedOperations; EntityShape(Builder builder) { super(builder, false); - resources = builder.resources.copy(); - operations = builder.operations.copy(); + + if (getMixins().isEmpty()) { + resources = builder.resources.copy(); + introducedResources = resources; + operations = builder.operations.copy(); + introducedOperations = operations; + } else { + Set computedResources = new LinkedHashSet<>(); + Set computedOperations = new LinkedHashSet<>(); + + for (Shape shape : builder.getMixins().values()) { + // validateMixins should have already assured that this is an EntityShape. + EntityShape mixin = (EntityShape) shape; + computedResources.addAll(mixin.getResources()); + computedOperations.addAll(mixin.getOperations()); + } + + introducedResources = builder.resources.copy(); + introducedOperations = builder.operations.copy(); + + computedResources.addAll(introducedResources); + computedOperations.addAll(introducedOperations); + + resources = Collections.unmodifiableSet(computedResources); + operations = Collections.unmodifiableSet(computedOperations); + } } /** @@ -40,6 +68,16 @@ public final Set getResources() { return resources; } + /** + * Gets all the directly-bound resources introduced by this shape and + * not inherited from mixins. + * + * @return Gets the introduced resources directly-bound to the shape. + */ + public final Set getIntroducedResources() { + return introducedResources; + } + /** * Gets operations bound only through the "operations" property. * @@ -55,6 +93,20 @@ public final Set getOperations() { return operations; } + /** + * Gets operations bound through the "operations" property that + * were not inherited from mixins. + * + *

This will not include operations bound to resources using + * a lifecycle operation binding. This will also not include + * operations bound to this entity through sub-resources. + * + * @return Gets the introduced operations. + */ + public final Set getIntroducedOperations() { + return introducedOperations; + } + /** * Get all operations directly bound to this shape. * @@ -147,5 +199,24 @@ public B clearResources() { resources.clear(); return (B) this; } + + @Override + public B flattenMixins() { + Set flatResources = new LinkedHashSet<>(); + Set flatOperations = new LinkedHashSet<>(); + + for (Shape shape : getMixins().values()) { + EntityShape mixin = (EntityShape) shape; + flatResources.addAll(mixin.getResources()); + flatOperations.addAll(mixin.getOperations()); + } + + flatResources.addAll(resources.peek()); + flatOperations.addAll(operations.peek()); + resources(flatResources); + operations(flatOperations); + + return super.flattenMixins(); + } } } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MapShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MapShape.java index 109972573ef..cfae051b535 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MapShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MapShape.java @@ -16,11 +16,13 @@ package software.amazon.smithy.model.shapes; import java.util.Collection; +import java.util.Collections; import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; +import software.amazon.smithy.model.SourceLocation; +import software.amazon.smithy.model.traits.Trait; import software.amazon.smithy.utils.ListUtils; -import software.amazon.smithy.utils.SmithyBuilder; import software.amazon.smithy.utils.ToSmithyBuilder; /** @@ -33,8 +35,8 @@ public final class MapShape extends Shape implements ToSmithyBuilder { private MapShape(Builder builder) { super(builder, false); - key = SmithyBuilder.requiredState("key", builder.key); - value = SmithyBuilder.requiredState("value", builder.value); + key = builder.key != null ? builder.key : getRequiredMixinMember(builder, "key"); + value = builder.value != null ? builder.value : getRequiredMixinMember(builder, "value"); validateMemberShapeIds(); } @@ -208,5 +210,37 @@ public Builder value(ShapeId target, Consumer memberUpdater return value(builder.build()); } + + @Override + public Builder flattenMixins() { + for (Shape mixin : getMixins().values()) { + for (MemberShape member : mixin.members()) { + SourceLocation location = getSourceLocation(); + Collection localTraits = Collections.emptyList(); + + MemberShape existing; + if (member.getMemberName().equals("key")) { + existing = key; + } else { + existing = value; + } + + if (existing != null) { + localTraits = existing.getIntroducedTraits().values(); + location = existing.getSourceLocation(); + } + + addMember(MemberShape.builder() + .id(getId().withMember(member.getMemberName())) + .target(member.getTarget()) + .addTraits(member.getAllTraits().values()) + .addTraits(localTraits) + .source(location) + .build()); + } + } + + return super.flattenMixins(); + } } } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MemberShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MemberShape.java index 8034d7876b6..23d16764751 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MemberShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/MemberShape.java @@ -15,7 +15,7 @@ package software.amazon.smithy.model.shapes; -import java.util.Collection; +import java.util.Map; import java.util.Optional; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.SourceException; @@ -39,7 +39,7 @@ private MemberShape(Builder builder) { } @Override - protected void validateMixins(Collection mixins) { + protected void validateMixins(Map mixins, Map introducedTraits) { // This can only happen by manipulating the semantic model in code. if (mixins.size() > 1) { throw new SourceException("Members must not have more than one mixin: " + getId(), this); diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java index a2435e82ddf..0c31c645932 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ModelSerializer.java @@ -206,12 +206,22 @@ private final class ShapeSerializer extends ShapeVisitor.Default { private final Set mixinMemberTraits = new TreeSet<>(); private ObjectNode.Builder createTypedBuilder(Shape shape) { - return Node.objectNodeBuilder() + ObjectNode.Builder builder = Node.objectNodeBuilder() .withMember("type", Node.from(shape.getType().toString())); + + if (!shape.getMixins().isEmpty()) { + List mixins = new ArrayList<>(shape.getMixins().size()); + for (ShapeId mixin : shape.getMixins()) { + mixins.add(serializeReference(mixin)); + } + builder.withMember("mixins", Node.fromNodes(mixins)); + } + + return builder; } private ObjectNode.Builder serializeAllTraits(Shape shape, ObjectNode.Builder builder) { - return serializeTraits(builder, shape.getAllTraits().values()); + return serializeTraits(builder, shape.getIntroducedTraits().values()); } @Override @@ -221,24 +231,34 @@ protected ObjectNode getDefault(Shape shape) { @Override public Node listShape(ListShape shape) { - return serializeAllTraits(shape, createTypedBuilder(shape) - .withMember("member", shape.getMember().accept(this))) - .build(); + return collectionShape(shape); } @Override public Node setShape(SetShape shape) { - return serializeAllTraits(shape, createTypedBuilder(shape) - .withMember("member", shape.getMember().accept(this))) - .build(); + return collectionShape(shape); + } + + public Node collectionShape(CollectionShape shape) { + ObjectNode.Builder result = createTypedBuilder(shape); + mixinMember(result, shape.getMember(), "member"); + return serializeAllTraits(shape, result).build(); + } + + private void mixinMember(ObjectNode.Builder builder, MemberShape member, String key) { + if (member.getMixins().isEmpty()) { + builder.withMember(key, member.accept(this)); + } else if (!member.getIntroducedTraits().isEmpty()) { + mixinMemberTraits.add(member); + } } @Override public Node mapShape(MapShape shape) { - return serializeAllTraits(shape, createTypedBuilder(shape) - .withMember("key", shape.getKey().accept(this)) - .withMember("value", shape.getValue().accept(this))) - .build(); + ObjectNode.Builder result = createTypedBuilder(shape); + mixinMember(result, shape.getKey(), "key"); + mixinMember(result, shape.getValue(), "value"); + return serializeAllTraits(shape, result).build(); } @Override @@ -246,7 +266,7 @@ public Node operationShape(OperationShape shape) { return serializeAllTraits(shape, createTypedBuilder(shape) .withMember("input", serializeReference(shape.getInputShape())) .withMember("output", serializeReference(shape.getOutputShape())) - .withOptionalMember("errors", createOptionalIdList(shape.getErrors()))) + .withOptionalMember("errors", createOptionalIdList(shape.getIntroducedErrors()))) .build(); } @@ -268,9 +288,9 @@ public Node resourceShape(ResourceShape shape) { .withOptionalMember("update", shape.getUpdate().map(this::serializeReference)) .withOptionalMember("delete", shape.getDelete().map(this::serializeReference)) .withOptionalMember("list", shape.getList().map(this::serializeReference)) - .withOptionalMember("operations", createOptionalIdList(shape.getOperations())) + .withOptionalMember("operations", createOptionalIdList(shape.getIntroducedOperations())) .withOptionalMember("collectionOperations", createOptionalIdList(shape.getCollectionOperations())) - .withOptionalMember("resources", createOptionalIdList(shape.getResources()))) + .withOptionalMember("resources", createOptionalIdList(shape.getIntroducedResources()))) .build(); } @@ -278,17 +298,17 @@ public Node resourceShape(ResourceShape shape) { public Node serviceShape(ServiceShape shape) { ObjectNode.Builder serviceBuilder = createTypedBuilder(shape); - if (!StringUtils.isBlank(shape.getVersion())) { - serviceBuilder.withMember("version", Node.from(shape.getVersion())); + if (!StringUtils.isBlank(shape.getIntroducedVersion())) { + serviceBuilder.withMember("version", Node.from(shape.getIntroducedVersion())); } - serviceBuilder.withOptionalMember("operations", createOptionalIdList(shape.getOperations())); - serviceBuilder.withOptionalMember("resources", createOptionalIdList(shape.getResources())); - serviceBuilder.withOptionalMember("errors", createOptionalIdList(shape.getErrors())); + serviceBuilder.withOptionalMember("operations", createOptionalIdList(shape.getIntroducedOperations())); + serviceBuilder.withOptionalMember("resources", createOptionalIdList(shape.getIntroducedResources())); + serviceBuilder.withOptionalMember("errors", createOptionalIdList(shape.getIntroducedErrors())); - if (!shape.getRename().isEmpty()) { + if (!shape.getIntroducedRename().isEmpty()) { ObjectNode.Builder renameBuilder = Node.objectNodeBuilder(); - for (Map.Entry entry : shape.getRename().entrySet()) { + for (Map.Entry entry : shape.getIntroducedRename().entrySet()) { renameBuilder.withMember(entry.getKey().toString(), entry.getValue()); } serviceBuilder.withMember("rename", renameBuilder.build()); @@ -323,22 +343,9 @@ public Node unionShape(UnionShape shape) { private ObjectNode createStructureAndUnion(Shape shape, Map members) { ObjectNode.Builder result = createTypedBuilder(shape); - if (!shape.getMixins().isEmpty()) { - List mixins = new ArrayList<>(shape.getMixins().size()); - for (ShapeId mixin : shape.getMixins()) { - mixins.add(serializeReference(mixin)); - } - result.withMember("mixins", Node.fromNodes(mixins)); - } - ObjectNode.Builder membersBuilder = ObjectNode.objectNodeBuilder(); for (MemberShape member : members.values()) { - if (member.getMixins().isEmpty()) { - membersBuilder.withMember(member.getMemberName(), member.accept(this)); - } else if (!member.getIntroducedTraits().isEmpty()) { - // There are traits that need to be added to inherited members. - mixinMemberTraits.add(member); - } + mixinMember(membersBuilder, member, member.getMemberName()); } result.withMember("members", membersBuilder.build()); diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/NamedMembersShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/NamedMembersShape.java index 3bd2f0df6cb..ee0748ef657 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/NamedMembersShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/NamedMembersShape.java @@ -86,11 +86,6 @@ public abstract class NamedMembersShape extends Shape { validateMemberShapeIds(); } - @Override - protected void validateMixins(Collection mixins) { - // do nothing. Mixins are allowed on structures and unions. - } - /** * Gets the members of the shape, including mixin members. * diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/OperationShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/OperationShape.java index 489c73b8b43..fb989104f68 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/OperationShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/OperationShape.java @@ -17,12 +17,15 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import software.amazon.smithy.model.SourceException; import software.amazon.smithy.model.knowledge.OperationIndex; +import software.amazon.smithy.model.traits.MixinTrait; import software.amazon.smithy.model.traits.UnitTypeTrait; import software.amazon.smithy.utils.BuilderRef; import software.amazon.smithy.utils.ToSmithyBuilder; @@ -34,12 +37,37 @@ public final class OperationShape extends Shape implements ToSmithyBuilder errors; + private final List introducedErrors; private OperationShape(Builder builder) { super(builder, false); - errors = builder.errors.copy(); + input = Objects.requireNonNull(builder.input); output = Objects.requireNonNull(builder.output); + + if (getMixins().isEmpty()) { + errors = builder.errors.copy(); + introducedErrors = errors; + } else { + // Compute mixin properties of the operation. Input / output are + // forbidden in operation mixins, so we don't bother with them + // here. + Set computedErrors = new LinkedHashSet<>(); + for (Shape shape : builder.getMixins().values()) { + shape.asOperationShape().ifPresent(mixin -> computedErrors.addAll(mixin.getErrors())); + } + introducedErrors = builder.errors.copy(); + computedErrors.addAll(introducedErrors); + errors = Collections.unmodifiableList(new ArrayList<>(computedErrors)); + } + + if (hasTrait(MixinTrait.ID) && (!input.equals(UnitTypeTrait.UNIT) || !output.equals(UnitTypeTrait.UNIT))) { + throw new SourceException(String.format( + "Operation shapes with the mixin trait MUST target `%s` for their input and output. Operation " + + "mixin shape `%s` defines one or both of these properties.", + UnitTypeTrait.UNIT, getId() + ), builder.getSourceLocation()); + } } public static Builder builder() { @@ -51,7 +79,7 @@ public Builder toBuilder() { return updateBuilder(builder()) .input(input) .output(output) - .errors(errors); + .errors(getIntroducedErrors()); } @Override @@ -138,6 +166,16 @@ public List getErrors() { return errors; } + /** + * Gets the errors introduced by the shape and not inherited + * from mixins. + * + * @return Returns the introduced errors. + */ + public List getIntroducedErrors() { + return introducedErrors; + } + /** *

Gets a list of the error shape IDs the operation can encounter, * including any common errors of a service. @@ -270,5 +308,16 @@ public Builder clearErrors() { public OperationShape build() { return new OperationShape(this); } + + @Override + public Builder flattenMixins() { + Set computedErrors = new LinkedHashSet<>(); + for (Shape shape : getMixins().values()) { + shape.asOperationShape().ifPresent(mixin -> computedErrors.addAll(mixin.getErrors())); + } + computedErrors.addAll(errors.peek()); + errors(computedErrors); + return super.flattenMixins(); + } } } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ResourceShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ResourceShape.java index 5946b667987..51ca9f6fbf8 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ResourceShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ResourceShape.java @@ -22,6 +22,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import software.amazon.smithy.model.traits.MixinTrait; import software.amazon.smithy.utils.BuilderRef; import software.amazon.smithy.utils.ToSmithyBuilder; @@ -59,6 +60,20 @@ private ResourceShape(Builder builder) { getUpdate().ifPresent(allOperations::add); getDelete().ifPresent(allOperations::add); getList().ifPresent(allOperations::add); + + if (hasTrait(MixinTrait.ID) && (!getIdentifiers().isEmpty() + || getPut().isPresent() + || getCreate().isPresent() + || getRead().isPresent() + || getUpdate().isPresent() + || getDelete().isPresent() + || getList().isPresent() + || !getOperations().isEmpty() + || !getResources().isEmpty())) { + throw new IllegalStateException(String.format( + "Resource shapes with the mixin trait may not define any properties. Resource mixin shape `%s` " + + "defines one or more properties.", getId())); + } } public static Builder builder() { diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ServiceShape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ServiceShape.java index 489e2bd7091..0e65ac93753 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ServiceShape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/ServiceShape.java @@ -15,11 +15,16 @@ package software.amazon.smithy.model.shapes; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import software.amazon.smithy.utils.BuilderRef; import software.amazon.smithy.utils.ToSmithyBuilder; @@ -29,14 +34,52 @@ public final class ServiceShape extends EntityShape implements ToSmithyBuilder { private final String version; + private final String introducedVersion; private final Map rename; + private final Map introducedRename; private final List errors; + private final List introducedErrors; private ServiceShape(Builder builder) { super(builder); - version = builder.version; - rename = builder.rename.copy(); - errors = builder.errors.copy(); + + if (getMixins().isEmpty()) { + version = builder.version; + introducedVersion = version; + rename = builder.rename.copy(); + introducedRename = rename; + errors = builder.errors.copy(); + introducedErrors = errors; + } else { + String computedVersion = ""; + Map computedRename = new LinkedHashMap<>(); + Set computedErrors = new LinkedHashSet<>(); + + for (Shape shape : builder.getMixins().values()) { + if (shape.isServiceShape()) { + ServiceShape mixin = shape.asServiceShape().get(); + if (!mixin.version.isEmpty()) { + computedVersion = mixin.version; + } + computedRename.putAll(mixin.getRename()); + computedErrors.addAll(mixin.getErrors()); + } + } + + introducedVersion = builder.version; + introducedRename = builder.rename.copy(); + introducedErrors = builder.errors.copy(); + + if (!introducedVersion.isEmpty()) { + computedVersion = introducedVersion; + } + computedRename.putAll(introducedRename); + computedErrors.addAll(introducedErrors); + + version = computedVersion; + rename = Collections.unmodifiableMap(computedRename); + errors = Collections.unmodifiableList(new ArrayList<>(computedErrors)); + } } public static Builder builder() { @@ -46,11 +89,11 @@ public static Builder builder() { @Override public Builder toBuilder() { return updateBuilder(builder()) - .version(version) - .errors(errors) - .rename(rename) - .operations(getOperations()) - .resources(getResources()); + .version(introducedVersion) + .errors(introducedErrors) + .rename(introducedRename) + .operations(getIntroducedOperations()) + .resources(getIntroducedResources()); } @Override @@ -85,6 +128,17 @@ public String getVersion() { return version; } + /** + * Gets the version of the service introduced by the shape and not + * inherited from mixins. An empty string is returned if the version + * is undefined. + * + * @return The introduced version of the service. + */ + public String getIntroducedVersion() { + return introducedVersion; + } + /** * @return The rename map of the service. */ @@ -92,6 +146,16 @@ public Map getRename() { return rename; } + /** + * Gets the rename map introduced by the shape and not inherited + * from mixins. + * + * @return The introduced rename map of the service. + */ + public Map getIntroducedRename() { + return introducedRename; + } + /** *

Gets a list of the common errors that can be encountered by * every operation in the service.

@@ -106,6 +170,21 @@ public List getErrors() { return errors; } + /** + * Gets the list of common errors introduced by the shape and not + * inherited from mixins. These errors can be encountered by every + * operation in the service. + * + * Each returned {@link ShapeId} must resolve to a + * {@link StructureShape} that is targeted by an error trait; however, + * this is only guaranteed after a model is validated. + * + * @return Returns the introduced service errors. + */ + public List getIntroducedErrors() { + return introducedErrors; + } + /** * Gets the contextual name of a shape within the closure. * @@ -237,5 +316,34 @@ public Builder clearErrors() { errors.clear(); return this; } + + @Override + public Builder flattenMixins() { + String flatVersion = version; + Map flatRename = new LinkedHashMap<>(); + Set flatErrors = new LinkedHashSet<>(); + + for (Shape shape : getMixins().values()) { + if (shape.isServiceShape()) { + ServiceShape mixin = shape.asServiceShape().get(); + if (!mixin.version.isEmpty()) { + flatVersion = mixin.version; + } + flatRename.putAll(mixin.getRename()); + flatErrors.addAll(mixin.getErrors()); + } + } + + if (!version.isEmpty()) { + flatVersion = version; + } + flatRename.putAll(rename.peek()); + flatErrors.addAll(errors.peek()); + + version = flatVersion; + rename(flatRename); + errors(flatErrors); + return super.flattenMixins(); + } } } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/Shape.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/Shape.java index cffff3156ad..ad1b47428ad 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/Shape.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/Shape.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.TreeSet; import software.amazon.smithy.model.FromSourceLocation; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.SourceException; @@ -75,7 +76,7 @@ public abstract class Shape implements FromSourceLocation, Tagged, ToShapeId, Co // Simple case when there are no mixins. traits = introducedTraits; } else { - validateMixins(mixins.keySet()); + validateMixins(mixins, introducedTraits); // Compute mixin traits. Map computedTraits = new HashMap<>(); for (Shape shape : mixins.values()) { @@ -88,10 +89,49 @@ public abstract class Shape implements FromSourceLocation, Tagged, ToShapeId, Co } } - protected void validateMixins(Collection mixins) { - // This method is overridden in subclasses that allow mixins (structure, union, member). - // This situation can only happen by manipulating the semantic model in code. - throw new SourceException("Mixins are not allowed on " + getId() + ", a " + getType(), this); + protected void validateMixins(Map mixins, Map introducedTraits) { + Set invalid = new TreeSet<>(); + for (Shape mixin : mixins.values()) { + if (mixin.getType() != getType()) { + invalid.add(mixin.getId().toString()); + } + } + if (!invalid.isEmpty()) { + String invalidList = String.join("`, `", invalid); + throw new SourceException(String.format( + "Mixins may only be mixed into shapes of the same type. The following mixins were applied to the " + + "%s shape `%s` which are not %1$s shapes: [`%s`]", getType(), getId(), invalidList), + source + ); + } + } + + protected MemberShape getRequiredMixinMember( + AbstractShapeBuilder builder, + String name + ) { + // Get the most recently introduced mixin member with the given name. + MemberShape mixedMember = null; + for (Shape shape : builder.getMixins().values()) { + for (MemberShape member : shape.members()) { + if (member.getMemberName().equals(name)) { + mixedMember = MemberShape.builder() + .id(getId().withMember(name)) + .target(member.getTarget()) + .source(getSourceLocation()) + .addMixin(member) + .build(); + break; + } + } + } + if (mixedMember == null) { + throw new SourceException( + String.format("Missing required member of shape `%s`: %s", getId(), name), + builder.getSourceLocation() + ); + } + return mixedMember; } /** diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java index 80c63b42f68..29fed1bd877 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java @@ -389,7 +389,9 @@ private static final class ShapeSerializer extends ShapeVisitor.Default { @Override protected Void getDefault(Shape shape) { serializeTraits(shape); - codeWriter.write("$L $L", shape.getType(), shape.getId().getName()).write(""); + codeWriter.writeInline("$L $L ", shape.getType(), shape.getId().getName()); + writeMixins(shape); + codeWriter.write("").write(""); return null; } @@ -404,27 +406,25 @@ private void shapeWithMembers(Shape shape, List members) { } } - serializeTraits(shape.getIntroducedTraits()); + serializeTraits(shape); codeWriter.writeInline("$L $L ", shape.getType(), shape.getId().getName()); - writeMixins(shape, !nonMixinMembers.isEmpty()); + writeMixins(shape); writeShapeMembers(nonMixinMembers); codeWriter.write(""); applyIntroducedTraits(mixinMembers); } - private void writeMixins(Shape shape, boolean hasNonMixinMembers) { + private void writeMixins(Shape shape) { if (shape.getMixins().size() == 1) { - codeWriter.writeInline("with $I ", shape.getMixins().iterator().next()); + codeWriter.writeInline("with [$I] ", shape.getMixins().iterator().next()); } else if (shape.getMixins().size() > 1) { - codeWriter.writeInline("with"); + codeWriter.write("with [").indent(); for (ShapeId id : shape.getMixins()) { // Trailing spaces are trimmed. - codeWriter.writeInline("\n $I ", id); - } - if (hasNonMixinMembers) { - codeWriter.write(""); + codeWriter.write("$I", id); } + codeWriter.dedent().writeInline("] "); } } @@ -459,7 +459,7 @@ private void applyIntroducedTraits(Collection members) { } private void serializeTraits(Shape shape) { - serializeTraits(shape.getAllTraits()); + serializeTraits(shape.getIntroducedTraits()); } private void serializeTraits(Map traits, TraitFeature... traitFeatures) { @@ -544,18 +544,20 @@ public Void unionShape(UnionShape shape) { @Override public Void serviceShape(ServiceShape shape) { serializeTraits(shape); - codeWriter.openBlock("service $L {", shape.getId().getName()); + codeWriter.writeInline("service $L ", shape.getId().getName()); + writeMixins(shape); + codeWriter.openBlock("{"); - if (!StringUtils.isBlank(shape.getVersion())) { - codeWriter.write("version: $S", shape.getVersion()); + if (!StringUtils.isBlank(shape.getIntroducedVersion())) { + codeWriter.write("version: $S", shape.getIntroducedVersion()); } - codeWriter.writeOptionalIdList("operations", shape.getOperations()); - codeWriter.writeOptionalIdList("resources", shape.getResources()); - codeWriter.writeOptionalIdList("errors", shape.getErrors()); - if (!shape.getRename().isEmpty()) { + codeWriter.writeOptionalIdList("operations", shape.getIntroducedOperations()); + codeWriter.writeOptionalIdList("resources", shape.getIntroducedResources()); + codeWriter.writeOptionalIdList("errors", shape.getIntroducedErrors()); + if (!shape.getIntroducedRename().isEmpty()) { codeWriter.openBlock("rename: {", "}", () -> { - for (Map.Entry entry : shape.getRename().entrySet()) { + for (Map.Entry entry : shape.getIntroducedRename().entrySet()) { codeWriter.write("$S: $S", entry.getKey(), entry.getValue()); } }); @@ -567,7 +569,9 @@ public Void serviceShape(ServiceShape shape) { @Override public Void resourceShape(ResourceShape shape) { serializeTraits(shape); - codeWriter.openBlock("resource $L {", shape.getId().getName()); + codeWriter.writeInline("resource $L ", shape.getId().getName()); + writeMixins(shape); + codeWriter.openBlock("{"); if (!shape.getIdentifiers().isEmpty()) { codeWriter.openBlock("identifiers: {"); shape.getIdentifiers().entrySet().stream() @@ -583,9 +587,9 @@ public Void resourceShape(ResourceShape shape) { shape.getUpdate().ifPresent(shapeId -> codeWriter.write("update: $I", shapeId)); shape.getDelete().ifPresent(shapeId -> codeWriter.write("delete: $I", shapeId)); shape.getList().ifPresent(shapeId -> codeWriter.write("list: $I", shapeId)); - codeWriter.writeOptionalIdList("operations", shape.getOperations()); + codeWriter.writeOptionalIdList("operations", shape.getIntroducedOperations()); codeWriter.writeOptionalIdList("collectionOperations", shape.getCollectionOperations()); - codeWriter.writeOptionalIdList("resources", shape.getResources()); + codeWriter.writeOptionalIdList("resources", shape.getIntroducedResources()); codeWriter.closeBlock("}"); codeWriter.write(""); @@ -595,11 +599,13 @@ public Void resourceShape(ResourceShape shape) { @Override public Void operationShape(OperationShape shape) { serializeTraits(shape); - codeWriter.openBlock("operation $L {", shape.getId().getName()); + codeWriter.writeInline("operation $L ", shape.getId().getName()); + writeMixins(shape); + codeWriter.openBlock("{"); List mixinMembers = new ArrayList<>(); mixinMembers.addAll(writeInlineableProperty("input", shape.getInputShape(), InputTrait.ID)); mixinMembers.addAll(writeInlineableProperty("output", shape.getOutputShape(), OutputTrait.ID)); - codeWriter.writeOptionalIdList("errors", shape.getErrors()); + codeWriter.writeOptionalIdList("errors", shape.getIntroducedErrors()); codeWriter.closeBlock("}"); codeWriter.write(""); applyIntroducedTraits(mixinMembers); @@ -636,7 +642,7 @@ private Collection writeInlineableProperty(String key, ShapeId shap } } - writeMixins(structure, !nonMixinMembers.isEmpty()); + writeMixins(structure); writeShapeMembers(nonMixinMembers); if (!hasOnlyDefaultTrait(structure, defaultTrait)) { diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/transform/FlattenAndRemoveMixins.java b/smithy-model/src/main/java/software/amazon/smithy/model/transform/FlattenAndRemoveMixins.java index fc3787c9c36..a1e1702f7a9 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/transform/FlattenAndRemoveMixins.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/transform/FlattenAndRemoveMixins.java @@ -17,11 +17,8 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Stream; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.Shape; -import software.amazon.smithy.model.shapes.StructureShape; -import software.amazon.smithy.model.shapes.UnionShape; import software.amazon.smithy.model.traits.MixinTrait; /** @@ -32,7 +29,7 @@ Model transform(ModelTransformer transformer, Model model) { List updatedShapes = new ArrayList<>(); List toRemove = new ArrayList<>(); - Stream.concat(model.shapes(StructureShape.class), model.shapes(UnionShape.class)).forEach(shape -> { + model.shapes().forEach(shape -> { if (shape.hasTrait(MixinTrait.class)) { toRemove.add(shape); } else if (!shape.getMixins().isEmpty()) { diff --git a/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude.smithy b/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude.smithy index ca19c522421..3523787324c 100644 --- a/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude.smithy +++ b/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude.smithy @@ -762,7 +762,7 @@ structure output {} structure unitType {} /// Makes a structure or union a mixin. -@trait(selector: ":is(structure, union)") +@trait(selector: ":not(member)") structure mixin { localTraits: LocalMixinTraitList } diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ListShapeTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ListShapeTest.java index a846b6dd721..44aa6da4705 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ListShapeTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ListShapeTest.java @@ -49,7 +49,7 @@ public void mustNotContainMembersInShapeId() { @Test public void requiresMember() { - Assertions.assertThrows(IllegalStateException.class, () -> { + Assertions.assertThrows(SourceException.class, () -> { ListShape.builder().id("ns.foo#bar").build(); }); } diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java index ccd6cff5b1d..ef4d78c59d2 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ModelSerializerTest.java @@ -24,8 +24,18 @@ import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; import java.util.Optional; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.SourceLocation; import software.amazon.smithy.model.node.Node; @@ -34,8 +44,25 @@ import software.amazon.smithy.model.traits.DocumentationTrait; import software.amazon.smithy.model.traits.SensitiveTrait; import software.amazon.smithy.model.traits.synthetic.OriginalShapeIdTrait; +import software.amazon.smithy.utils.IoUtils; public class ModelSerializerTest { + @TestFactory + public Stream generateTests() throws IOException, URISyntaxException { + return Files.list(Paths.get( + SmithyIdlModelSerializer.class.getResource("ast-serialization/cases").toURI())) + .map(path -> DynamicTest.dynamicTest(path.getFileName().toString(), () -> testRoundTrip(path))); + } + + public void testRoundTrip(Path path) { + Model model = Model.assembler().addImport(path).assemble().unwrap(); + ModelSerializer serializer = ModelSerializer.builder().build(); + ObjectNode actual = serializer.serialize(model); + ObjectNode expected = Node.parse(IoUtils.readUtf8File(path)).expectObjectNode(); + + Node.assertEquals(actual, expected); + } + @Test public void serializesModels() { Model model = Model.assembler() diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ShapeTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ShapeTest.java index 026382ba001..b259b7f2881 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ShapeTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/shapes/ShapeTest.java @@ -252,12 +252,4 @@ public void validatesMemberShapeIds() { .build(); }); } - - @Test - public void membersCannotBeAddedToSomeShapes() { - Assertions.assertThrows(SourceException.class, () -> { - StringShape mixin = StringShape.builder().id("ns.foo#string").build(); - StringShape.builder().id("ns.foo#bar").addMixin(mixin).build(); - }); - } } diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/transform/ModelTransformerTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/transform/ModelTransformerTest.java index 075f7196af8..974b1e35d70 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/transform/ModelTransformerTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/transform/ModelTransformerTest.java @@ -169,7 +169,11 @@ public static String[] flattenShapesData() { "loads-mixins", "mixins-with-members", "mixins-with-members-and-traits", - "mixins-with-mixin-local-traits" + "mixins-with-mixin-local-traits", + "operations", + "resources", + "services", + "idl-mixins-redefine-member" }; } } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-illegal-redefined-member.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-illegal-redefined-member.smithy index eac6aab99ba..0aeee813398 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-illegal-redefined-member.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-illegal-redefined-member.smithy @@ -5,17 +5,21 @@ namespace smithy.example @mixin structure Foo { foo: String + bar: String } -structure Baz with Foo { - foo: String // cannot redefine mixin members! +structure Baz with [Foo] { + foo: Integer // cannot redefine mixin members with a different target! + bar: String // This is allowed because the target hasn't changed } @mixin -structure Bam with Foo { - foo: String // cannot redefine mixin members! +structure Bam with [Foo] { + foo: Integer // cannot redefine mixin members with a different target! + bar: String // This is allowed because the target hasn't changed } -structure Boo with Bam { - foo: String // cannot redefine mixin members! +structure Boo with [Bam] { + foo: Long // cannot redefine mixin members with a different target! + bar: String // This is allowed because the target hasn't changed } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-trait-applied.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-trait-applied.smithy index a7e48f4e018..c234a49322b 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-trait-applied.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/mixins/mixin-trait-applied.smithy @@ -6,7 +6,7 @@ namespace smithy.example structure mixinTrait {} // This is fine, and it makes this shape a trait! -structure usesMixinTrait with mixinTrait {} +structure usesMixinTrait with [mixinTrait] {} @usesMixinTrait structure FineUseOfTrait {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/list-empty-members.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/list-empty-members.smithy index 99e9ec77621..c7d7fafdba2 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/list-empty-members.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/list-empty-members.smithy @@ -1,4 +1,4 @@ -// Parse error at line 4, column 14 near `}\n`: Missing required members of shape `com.foo#MyList`: [`member`] +// Missing required member of shape `com.foo#MyList`: member namespace com.foo list MyList {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/map-empty-members.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/map-empty-members.smithy index e5d312b558d..f0033ef7cfd 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/map-empty-members.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/map-empty-members.smithy @@ -1,4 +1,4 @@ -// Parse error at line 4, column 12 near `}\n`: Missing required members of shape `com.foo#MyMap`: [`key`, `value`] | Model +// Missing required member of shape `com.foo#MyMap`: key namespace com.foo map MyMap {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/dangling-with.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/dangling-with.smithy index f55462a2ffe..b8319bb95cb 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/dangling-with.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/dangling-with.smithy @@ -1,4 +1,4 @@ -// Parse error at line 6, column 1 near ``: Expected a valid identifier character, but found '' +// Parse error at line 6, column 1 near ``: Expected: '[', but found '' $version: "2" namespace com.foo diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/invalid-mixins-on-resource.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/invalid-mixins-on-resource.smithy deleted file mode 100644 index 8640db189f6..00000000000 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/invalid-mixins-on-resource.smithy +++ /dev/null @@ -1,5 +0,0 @@ -// Parse error at line 5, column 15 near `with`: Expected: '{', but found 'w' -$version: "2" -namespace com.foo - -resource Test with X {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/invalid-mixins-on-string.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/invalid-mixins-on-string.smithy deleted file mode 100644 index 4ebce8558b4..00000000000 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/invalid-mixins-on-string.smithy +++ /dev/null @@ -1,5 +0,0 @@ -// Parse error at line 5, column 21 near ` X`: Unexpected shape type: with -$version: "2" -namespace com.foo - -string MyString with X diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/missing-rest-of-with-word.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/missing-rest-of-with-word.smithy index d7e31186a52..f038cf9b8f6 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/missing-rest-of-with-word.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/missing-rest-of-with-word.smithy @@ -1,5 +1,5 @@ -// Parse error at line 5, column 18 near ` Baz`: Expected: 'h', but found ' ' +// Parse error at line 5, column 18 near ` `: Expected: 'h', but found ' ' $version: "2" namespace com.foo -structure Foo wit Baz {} +structure Foo wit [Baz] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mix-types.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mix-types.smithy new file mode 100644 index 00000000000..7c415c1b3cb --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mix-types.smithy @@ -0,0 +1,10 @@ +// Mixins may only be mixed into shapes of the same type. The following mixins were applied to the structure shape `com.foo#Foo` which are not structure shapes: [`com.foo#Bar`] +$version: "2" +namespace com.foo + +@mixin +union Bar { + first: String +} + +structure Foo with [Bar] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-explicit-1-0.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-explicit-1-0.smithy index 5634323e0e4..3de5b363575 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-explicit-1-0.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-explicit-1-0.smithy @@ -1,5 +1,5 @@ -// Parse error at line 5, column 19 near ` Bar`: Mixins can only be used with Smithy version 2 or later. Attempted to use mixins with version `1.0`. +// Parse error at line 5, column 19 near ` `: Mixins can only be used with Smithy version 2 or later. Attempted to use mixins with version `1.0`. $version: "1.0" namespace smithy.example -structure Foo with Bar {} +structure Foo with [Bar] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-implicit-1-0.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-implicit-1-0.smithy index 22291e3be0a..df8a67024d4 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-implicit-1-0.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/mixins-usage-in-implicit-1-0.smithy @@ -1,4 +1,4 @@ -// Parse error at line 4, column 19 near ` Bar`: Mixins can only be used with Smithy version 2 or later. Attempted to use mixins with version `1.0`. +// Parse error at line 4, column 19 near ` `: Mixins can only be used with Smithy version 2 or later. Attempted to use mixins with version `1.0`. namespace smithy.example -structure Foo with Bar {} +structure Foo with [Bar] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/operation-mixin-with-io.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/operation-mixin-with-io.smithy new file mode 100644 index 00000000000..99c5b3f5536 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/operation-mixin-with-io.smithy @@ -0,0 +1,10 @@ +// Operation shapes with the mixin trait MUST target `smithy.api#Unit` for their input and output. Operation mixin shape `smithy.example#MixinOperation` defines one or both of these properties. +$version: "2.0" + +namespace smithy.example + +@mixin +operation MixinOperation { + input := {} + output := {} +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/resource-mixin-with-properties.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/resource-mixin-with-properties.smithy new file mode 100644 index 00000000000..ba98ff8934c --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/resource-mixin-with-properties.smithy @@ -0,0 +1,12 @@ +// Resource shapes with the mixin trait may not define any properties. Resource mixin shape `smithy.example#MixinResource` defines one or more properties. +$version: "2.0" + +namespace smithy.example + +@mixin +@internal +resource MixinResource { + identifiers: { + "mixinId": String + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-but-no-ids.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-but-no-ids.smithy index 2a32ac1a8b1..f9fd01569ba 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-but-no-ids.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-but-no-ids.smithy @@ -1,5 +1,5 @@ -// Parse error at line 5, column 20 near `{}`: Expected a valid identifier character, but found '{' +// Parse error at line 5, column 21 near `] `: Expected a valid identifier character, but found ']' $version: "2" namespace com.foo -structure Foo with {} +structure Foo with [] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-bigDecimal.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-bigDecimal.smithy new file mode 100644 index 00000000000..fef40fbe38f --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-bigDecimal.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +bigDecimal MixinBigdecimal + +bigDecimal MixedBigdecimal + with [MixinBigdecimal] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-bigInteger.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-bigInteger.smithy new file mode 100644 index 00000000000..8a3d2ac9fb8 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-bigInteger.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +bigInteger MixinBiginteger + +bigInteger MixedBiginteger + with [MixinBiginteger] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-blob.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-blob.smithy new file mode 100644 index 00000000000..915f9d2f338 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-blob.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +blob MixinBlob + +blob MixedBlob + with [MixinBlob] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-boolean.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-boolean.smithy new file mode 100644 index 00000000000..f27c986d456 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-boolean.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +boolean MixinBoolean + +boolean MixedBoolean + with [MixinBoolean] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-byte.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-byte.smithy new file mode 100644 index 00000000000..ece46a413e4 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-byte.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +byte MixinByte + +byte MixedByte + with [MixinByte] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-document.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-document.smithy new file mode 100644 index 00000000000..8bf58a2fa98 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-document.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +document MixinDocument + +document MixedDocument + with [MixinDocument] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-double.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-double.smithy new file mode 100644 index 00000000000..ef3ba335ebc --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-double.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +double MixinDouble + +double MixedDouble + with [MixinDouble] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-float.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-float.smithy new file mode 100644 index 00000000000..a00e813883e --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-float.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +float MixinFloat + +float MixedFloat + with [MixinFloat] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-integer.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-integer.smithy new file mode 100644 index 00000000000..f653c969bc8 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-integer.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +integer MixinInteger + +integer MixedInteger + with [MixinInteger] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-list.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-list.smithy new file mode 100644 index 00000000000..f2ed6af4d5c --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-list.smithy @@ -0,0 +1,12 @@ +// Parse error at line 7, column 5 near `with`: Expected: '{', but found 'w' +$version: "2.0" + +namespace smithy.example + +list MixedList + with [MixinList] {} + +@mixin +list MixinList { + member: String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-long.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-long.smithy new file mode 100644 index 00000000000..e2ab79e32b9 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-long.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +long MixinLong + +long MixedLong + with [MixinLong] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-map.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-map.smithy new file mode 100644 index 00000000000..35d1311774e --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-map.smithy @@ -0,0 +1,13 @@ +// Parse error at line 7, column 5 near `with`: Expected: '{', but found 'w' +$version: "2.0" + +namespace smithy.example + +map MixedMap + with [MixinMap] {} + +@mixin +map MixinMap { + key: String + value: String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-resource.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-resource.smithy new file mode 100644 index 00000000000..48037ed3e5b --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-resource.smithy @@ -0,0 +1,10 @@ +// Parse error at line 7, column 5 near `with`: Expected: '{', but found 'w' +$version: "2.0" + +namespace smithy.example + +resource MixedResource + with [MixinResource] {} + +@mixin +resource MixinResource {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-service.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-service.smithy new file mode 100644 index 00000000000..9fdd1087145 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-service.smithy @@ -0,0 +1,10 @@ +// Parse error at line 7, column 5 near `with`: Expected: '{', but found 'w' +$version: "2.0" + +namespace smithy.example + +service MixedService + with [MixinService] {} + +@mixin +service MixinService {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-set.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-set.smithy new file mode 100644 index 00000000000..35d1311774e --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-set.smithy @@ -0,0 +1,13 @@ +// Parse error at line 7, column 5 near `with`: Expected: '{', but found 'w' +$version: "2.0" + +namespace smithy.example + +map MixedMap + with [MixinMap] {} + +@mixin +map MixinMap { + key: String + value: String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-short.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-short.smithy new file mode 100644 index 00000000000..314e7f0e5c6 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-short.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +short MixinShort + +short MixedShort + with [MixinShort] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-string.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-string.smithy new file mode 100644 index 00000000000..88df5d1ee88 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-string.smithy @@ -0,0 +1,10 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +string MixinString + +string MixedString + with [MixinString] diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-structure.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-structure.smithy new file mode 100644 index 00000000000..a99c82c8996 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-structure.smithy @@ -0,0 +1,10 @@ +// Parse error at line 7, column 5 near `with`: Expected: '{', but found 'w' +$version: "2.0" + +namespace smithy.example + +structure MixedStructure + with [MixinStructure] {} + +@mixin +structure MixinStructure {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-timestamp.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-timestamp.smithy new file mode 100644 index 00000000000..2a5bd835eb6 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-timestamp.smithy @@ -0,0 +1,11 @@ +// Parse error at line 10, column 9 near ` `: Unexpected shape type: with +$version: "2.0" + +namespace smithy.example + +@mixin +timestamp MixinTimestamp + +timestamp MixedTimestamp + with [MixinTimestamp] + \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-union.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-union.smithy new file mode 100644 index 00000000000..9aa858348e0 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/mixins/with-on-next-line-union.smithy @@ -0,0 +1,10 @@ +// Parse error at line 7, column 5 near `with`: Expected: '{', but found 'w' +$version: "2.0" + +namespace smithy.example + +union MixedUnion + with [MixinUnion] {} + +@mixin +union MixinUnion {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/set-empty-members.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/set-empty-members.smithy index a1799904d7b..5dc7866ff09 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/set-empty-members.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/invalid/set-empty-members.smithy @@ -1,4 +1,4 @@ -// Parse error at line 4, column 12 near `}\n`: Missing required members of shape `com.foo#MySet`: [`member`] +// Missing required member of shape `com.foo#MySet`: member namespace com.foo set MySet {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-1.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-1.smithy index 09fd6389650..a39bc837ed8 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-1.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-1.smithy @@ -3,7 +3,7 @@ $version: "2.0" namespace smithy.example @mixin -structure A with B { +structure A with [B] { a: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-2.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-2.smithy index 37e2185b578..412a4111324 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-2.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-acceptable-2.smithy @@ -3,6 +3,6 @@ $version: "2.0" namespace smithy.example @mixin -structure A with B { +structure A with [B] { a: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-error.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-error.smithy index 0307fa5a8d6..a299c4ec41d 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-error.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixin-conflict-error.smithy @@ -4,6 +4,6 @@ $version: "2.0" namespace smithy.example @mixin -structure A with B { +structure A with [B] { a: Integer } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixins-can-override-traits.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixins-can-override-traits.smithy index 6c73aac11c4..5edeb5f4a5d 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixins-can-override-traits.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/mixins/mixins-can-override-traits.smithy @@ -12,7 +12,7 @@ structure A { /// B @deprecated @mixin -structure B with A { +structure B with [A] { /// B.b b: String } @@ -28,7 +28,7 @@ structure C { /// D @mixin @externalDocumentation(web: "http://example.com") -structure D with C { +structure D with [C] { /// D.d d: String } @@ -40,14 +40,14 @@ apply D$c @documentation("I've changed") /// E @since("X") @mixin -structure E with D { +structure E with [D] { /// E.e e: String } /// F @internal -structure F with B, E { +structure F with [B, E] { /// F.f f: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.smithy index 2438c748680..858368f5d09 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.smithy @@ -30,11 +30,11 @@ structure NameBearer { } operation UsesMixins { - input := with NameBearer { + input := with [NameBearer] { id: String } - output := with NameBearer { + output := with [NameBearer] { id: String } } @@ -42,13 +42,13 @@ operation UsesMixins { operation UsesTraitsAndMixins { input := @sensitive - with NameBearer { + with [NameBearer] { id: String } output := @sensitive - with NameBearer { + with [NameBearer] { id: String } } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.flattened.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.flattened.smithy new file mode 100644 index 00000000000..02b2ea6a820 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.flattened.smithy @@ -0,0 +1,9 @@ +$version: "2.0" + +namespace smithy.example + +structure MixedStructure { + @required + @internal + redefineable: String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.json new file mode 100644 index 00000000000..b846559ccca --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.json @@ -0,0 +1,33 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#MixinStructure": { + "type": "structure", + "members": { + "redefineable": { + "target": "smithy.api#String", + "traits": { + "smithy.api#internal": {} + } + } + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedStructure": { + "type": "structure", + "members": { + "redefineable": { + "target": "smithy.api#String", + "traits": { + "smithy.api#required": {} + } + } + }, + "mixins": [{ + "target": "smithy.example#MixinStructure" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.smithy new file mode 100644 index 00000000000..96d901f885d --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/idl-mixins-redefine-member.smithy @@ -0,0 +1,17 @@ +$version: "2.0" + +namespace smithy.example + +@mixin +structure MixinStructure { + @internal + redefineable: String +} + +structure MixedStructure with [MixinStructure] { + // Since the target hasn't changed, this is an acceptable redefinition. + // Traits are still inherited as normal, they just don't have to use + // apply to introduce new ones. + @required + redefineable: String +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins-with-whitespace.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins-with-whitespace.smithy index 3a2ac3b7d70..17d4082292d 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins-with-whitespace.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins-with-whitespace.smithy @@ -8,8 +8,8 @@ structure A {} @mixin structure B {} -structure C with +structure C with [ A ,,, - , B ,,, , {} + , B ,,, , ] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.flattened.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.flattened.smithy index cf5a2cfb61d..f2dc5aedb89 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.flattened.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.flattened.smithy @@ -5,3 +5,48 @@ namespace smithy.example structure B {} structure F {} + +blob MixedBlob + +boolean MixedBoolean + +string MixedString + +byte MixedByte + +short MixedShort + +integer MixedInteger + +long MixedLong + +float MixedFloat + +double MixedDouble + +bigInteger MixedBigInt + +bigDecimal MixedBigDecimal + +timestamp MixedTimestamp + +document MixedDocument + +list MixedList { + member: String +} + +set MixedSet { + member: String +} + +map MixedMap { + key: String + value: String +} + +service MixedService {} + +resource MixedResource {} + +operation MixedOperation {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.json index 022e5393ff3..6ca2a6e83a2 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.json +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.json @@ -43,6 +43,246 @@ {"target": "smithy.example#A"}, {"target": "smithy.example#E"} ] + }, + "smithy.example#MixinBlob": { + "type": "blob", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBlob": { + "type": "blob", + "mixins": [{ + "target": "smithy.example#MixinBlob" + }] + }, + "smithy.example#MixinBoolean": { + "type": "boolean", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBoolean": { + "type": "boolean", + "mixins": [{ + "target": "smithy.example#MixinBoolean" + }] + }, + "smithy.example#MixinString": { + "type": "string", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedString": { + "type": "string", + "mixins": [{ + "target": "smithy.example#MixinString" + }] + }, + "smithy.example#MixinByte": { + "type": "byte", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedByte": { + "type": "byte", + "mixins": [{ + "target": "smithy.example#MixinByte" + }] + }, + "smithy.example#MixinShort": { + "type": "short", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedShort": { + "type": "short", + "mixins": [{ + "target": "smithy.example#MixinShort" + }] + }, + "smithy.example#MixinInteger": { + "type": "integer", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedInteger": { + "type": "integer", + "mixins": [{ + "target": "smithy.example#MixinInteger" + }] + }, + "smithy.example#MixinLong": { + "type": "long", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedLong": { + "type": "long", + "mixins": [{ + "target": "smithy.example#MixinLong" + }] + }, + "smithy.example#MixinFloat": { + "type": "float", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedFloat": { + "type": "float", + "mixins": [{ + "target": "smithy.example#MixinFloat" + }] + }, + "smithy.example#MixinDouble": { + "type": "double", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedDouble": { + "type": "double", + "mixins": [{ + "target": "smithy.example#MixinDouble" + }] + }, + "smithy.example#MixinBigInt": { + "type": "bigInteger", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBigInt": { + "type": "bigInteger", + "mixins": [{ + "target": "smithy.example#MixinBigInt" + }] + }, + "smithy.example#MixinBigDecimal": { + "type": "bigDecimal", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBigDecimal": { + "type": "bigDecimal", + "mixins": [{ + "target": "smithy.example#MixinBigDecimal" + }] + }, + "smithy.example#MixinTimestamp": { + "type": "timestamp", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedTimestamp": { + "type": "timestamp", + "mixins": [{ + "target": "smithy.example#MixinTimestamp" + }] + }, + "smithy.example#MixinDocument": { + "type": "document", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedDocument": { + "type": "document", + "mixins": [{ + "target": "smithy.example#MixinDocument" + }] + }, + "smithy.example#MixinList": { + "type": "list", + "member": { + "target": "smithy.api#String" + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedList": { + "type": "list", + "mixins": [{ + "target": "smithy.example#MixinList" + }] + }, + "smithy.example#MixinSet": { + "type": "set", + "member": { + "target": "smithy.api#String" + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedSet": { + "type": "set", + "mixins": [{ + "target": "smithy.example#MixinSet" + }] + }, + "smithy.example#MixinMap": { + "type": "map", + "key": { + "target": "smithy.api#String" + }, + "value": { + "target": "smithy.api#String" + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedMap": { + "type": "map", + "mixins": [{ + "target": "smithy.example#MixinMap" + }] + }, + "smithy.example#MixinService": { + "type": "service", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedService": { + "type": "service", + "mixins": [{ + "target": "smithy.example#MixinService" + }] + }, + "smithy.example#MixinResource": { + "type": "resource", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedResource": { + "type": "resource", + "mixins": [{ + "target": "smithy.example#MixinResource" + }] + }, + "smithy.example#MixinOperation": { + "type": "operation", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedOperation": { + "type": "operation", + "mixins": [{ + "target": "smithy.example#MixinOperation" + }] } } } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.smithy index 8b1ceb08bda..5055db8be9b 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/loads-mixins.smithy @@ -5,15 +5,117 @@ namespace smithy.example @mixin structure A {} -structure B with A {} +structure B with [A] {} @mixin structure C {} @mixin -structure D with C {} +structure D with [C] {} @mixin -structure E with D {} +structure E with [D] {} -structure F with A, E {} +structure F with [A, E] {} + +@mixin +blob MixinBlob + +blob MixedBlob with [MixinBlob] + +@mixin +boolean MixinBoolean + +boolean MixedBoolean with [MixinBoolean] + +@mixin +string MixinString + +string MixedString with [MixinString] + +@mixin +byte MixinByte + +byte MixedByte with [MixinByte] + +@mixin +short MixinShort + +short MixedShort with [MixinShort] + +@mixin +integer MixinInteger + +integer MixedInteger with [MixinInteger] + +@mixin +long MixinLong + +long MixedLong with [MixinLong] + +@mixin +float MixinFloat + +float MixedFloat with [MixinFloat] + +@mixin +double MixinDouble + +double MixedDouble with [MixinDouble] + +@mixin +bigInteger MixinBigInt + +bigInteger MixedBigInt with [MixinBigInt] + +@mixin +bigDecimal MixinBigDecimal + +bigDecimal MixedBigDecimal with [MixinBigDecimal] + +@mixin +timestamp MixinTimestamp + +timestamp MixedTimestamp with [MixinTimestamp] + +@mixin +document MixinDocument + +document MixedDocument with [MixinDocument] + +@mixin +list MixinList { + member: String +} + +list MixedList with [MixinList] {} + +@mixin +set MixinSet { + member: String +} + +set MixedSet with [MixinSet] {} + +@mixin +map MixinMap { + key: String + value: String +} + +map MixedMap with [MixinMap] {} + +@mixin +service MixinService {} + +service MixedService with [MixinService] {} + +@mixin +resource MixinResource {} + +resource MixedResource with [MixinResource] {} + +@mixin +operation MixinOperation {} + +operation MixedOperation with [MixinOperation] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixin-names.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixin-names.json new file mode 100644 index 00000000000..f4e525ac386 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixin-names.json @@ -0,0 +1,40 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#integerPrefixed": { + "type": "integer", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#NotPrefixed": { + "type": "integer", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#iiiiiii": { + "type": "integer", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedInt": { + "type": "integer", + "mixins": [ + { + "target": "smithy.example#NotPrefixed" + }, + { + "target": "smithy.example#integerPrefixed" + }, + { + "target": "smithy.example#iiiiiii" + } + ] + }, + "smithy.example#SafeInt": { + "type": "integer" + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixin-names.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixin-names.smithy new file mode 100644 index 00000000000..69359715e82 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixin-names.smithy @@ -0,0 +1,21 @@ +$version: "2.0" + +namespace smithy.example + +@mixin +integer integerPrefixed + +@mixin +integer NotPrefixed + +// This is related to a bug that was present where only +// the first character of the identifier following a mixin +// was ever looked at. This ensures that happens to start +// with an i and be the same length as "integer" doesn't +// trip up the parser. +@mixin +integer iiiiiii + +integer MixedInt with [NotPrefixed integerPrefixed iiiiiii] + +integer SafeInt diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members-and-traits.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members-and-traits.smithy index 2185be685ac..7d9e89d9535 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members-and-traits.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members-and-traits.smithy @@ -13,7 +13,7 @@ structure A { /// B @deprecated @mixin -structure B with A { +structure B with [A] { /// B.b b: String } @@ -29,7 +29,7 @@ structure C { /// D @mixin @externalDocumentation(web: "http://example.com") -structure D with C { +structure D with [C] { /// D.d d: String } @@ -41,14 +41,14 @@ apply D$c @documentation("I've changed") /// E @since("X") @mixin -structure E with D { +structure E with [D] { /// E.e e: String } /// F @internal -structure F with B, E { +structure F with [B, E] { /// F.f f: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members.smithy index 3a084c370d6..d7ddbe8b4ce 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-members.smithy @@ -8,7 +8,7 @@ structure A { } @mixin -structure B with A { +structure B with [A] { b: String } @@ -18,15 +18,15 @@ structure C { } @mixin -structure D with C { +structure D with [C] { d: String } @mixin -structure E with D { +structure E with [D] { e: String } -structure F with B, E { +structure F with [B, E] { f: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-mixin-local-traits.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-mixin-local-traits.smithy index e4091a58873..70169b1123a 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-mixin-local-traits.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/mixins-with-mixin-local-traits.smithy @@ -11,4 +11,4 @@ structure PrivateMixin { foo: String } -structure PublicShape with PrivateMixin {} +structure PublicShape with [PrivateMixin] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.flattened.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.flattened.smithy new file mode 100644 index 00000000000..0620dc803c2 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.flattened.smithy @@ -0,0 +1,14 @@ +$version: "2.0" + +namespace smithy.example + +@error("client") +structure ConcreteError {} + +@error("server") +structure MixinError {} + +@internal +operation ConcreteOperation { + errors: [MixinError, ConcreteError] +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.json new file mode 100644 index 00000000000..50987331a75 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.json @@ -0,0 +1,36 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#ConcreteError": { + "type": "structure", + "traits": { + "smithy.api#error": "client" + } + }, + "smithy.example#MixinError": { + "type": "structure", + "traits": { + "smithy.api#error": "server" + } + }, + "smithy.example#InternalMixin": { + "type": "operation", + "errors": [{ + "target": "smithy.example#MixinError" + }], + "traits": { + "smithy.api#mixin": {}, + "smithy.api#internal": {} + } + }, + "smithy.example#ConcreteOperation": { + "type": "operation", + "errors": [{ + "target": "smithy.example#ConcreteError" + }], + "mixins": [{ + "target": "smithy.example#InternalMixin" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.smithy new file mode 100644 index 00000000000..368e9ea0967 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/operations.smithy @@ -0,0 +1,19 @@ +$version: "2.0" + +namespace smithy.example + +@error("client") +structure ConcreteError {} + +@error("server") +structure MixinError {} + +@mixin +@internal +operation InternalMixin { + errors: [MixinError] +} + +operation ConcreteOperation with [InternalMixin] { + errors: [ConcreteError] +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.flattened.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.flattened.smithy new file mode 100644 index 00000000000..794665ab05a --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.flattened.smithy @@ -0,0 +1,6 @@ +$version: "2.0" + +namespace smithy.example + +@internal +resource MixedResource {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.json new file mode 100644 index 00000000000..0040b90ba78 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.json @@ -0,0 +1,18 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#MixinResource": { + "type": "resource", + "traits": { + "smithy.api#mixin": {}, + "smithy.api#internal": {} + } + }, + "smithy.example#MixedResource": { + "type": "resource", + "mixins": [{ + "target": "smithy.example#MixinResource" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.smithy new file mode 100644 index 00000000000..b06a60d054d --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/resources.smithy @@ -0,0 +1,9 @@ +$version: "2.0" + +namespace smithy.example + +@mixin +@internal +resource MixinResource {} + +resource MixedResource with [MixinResource] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.flattened.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.flattened.smithy new file mode 100644 index 00000000000..47c6ee239fe --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.flattened.smithy @@ -0,0 +1,25 @@ +$version: "2.0" + +namespace smithy.example + +string ShapeToRename + +@error("client") +structure MixinError { + message: ShapeToRename +} + +operation MixinOperation {} + +resource MixinResource {} + +@internal +service MixedService { + version: "2021-12-31" + errors: [MixinError] + rename: { + "smithy.example#ShapeToRename": "RenamedShape" + } + operations: [MixinOperation] + resources: [MixinResource] +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.json b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.json new file mode 100644 index 00000000000..adcb63eccba --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.json @@ -0,0 +1,57 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#ShapeToRename": { + "type": "string" + }, + "smithy.example#MixinError": { + "type": "structure", + "members": { + "message": { + "target": "smithy.example#ShapeToRename" + } + }, + "traits": { + "smithy.api#error": "client" + } + }, + "smithy.example#MixinOperation": { + "type": "operation", + "input": { + "target": "smithy.api#Unit" + }, + "output": { + "target": "smithy.api#Unit" + } + }, + "smithy.example#MixinResource": { + "type": "resource" + }, + "smithy.example#MixinService": { + "type": "service", + "version": "2021-12-31", + "errors": [{ + "target": "smithy.example#MixinError" + }], + "rename": { + "smithy.example#ShapeToRename": "RenamedShape" + }, + "operations": [{ + "target": "smithy.example#MixinOperation" + }], + "resources": [{ + "target": "smithy.example#MixinResource" + }], + "traits": { + "smithy.api#mixin": {}, + "smithy.api#internal": {} + } + }, + "smithy.example#MixedService": { + "type": "service", + "mixins": [{ + "target": "smithy.example#MixinService" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.smithy new file mode 100644 index 00000000000..7b8085ed568 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/mixins/services.smithy @@ -0,0 +1,28 @@ +$version: "2.0" + +namespace smithy.example + +string ShapeToRename + +@error("client") +structure MixinError { + message: ShapeToRename +} + +operation MixinOperation {} + +resource MixinResource {} + +@internal +@mixin +service MixinService { + version: "2021-12-31" + errors: [MixinError] + rename: { + "smithy.example#ShapeToRename": "RenamedShape" + } + operations: [MixinOperation] + resources: [MixinResource] +} + +service MixedService with [MixinService] {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/selector/structure-with-mixins.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/selector/structure-with-mixins.smithy index 4e99ef54e70..650dbfbafeb 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/selector/structure-with-mixins.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/selector/structure-with-mixins.smithy @@ -6,9 +6,9 @@ namespace smithy.example structure Mixin1 {} @mixin -structure Mixin2 with Mixin1 {} +structure Mixin2 with [Mixin1] {} -structure Concrete with Mixin2 {} +structure Concrete with [Mixin2] {} structure NoMixins {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/data-mixins.json b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/data-mixins.json new file mode 100644 index 00000000000..3051d34eef0 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/data-mixins.json @@ -0,0 +1,258 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#A": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#B": { + "type": "structure", + "members": {}, + "mixins": [ + {"target": "smithy.example#A"} + ] + }, + "smithy.example#C": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#D": { + "type": "structure", + "members": {}, + "mixins": [ + {"target": "smithy.example#C"} + ], + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#E": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#mixin": {} + }, + "mixins": [ + {"target": "smithy.example#D"} + ] + }, + "smithy.example#F": { + "type": "structure", + "members": {}, + "mixins": [ + {"target": "smithy.example#A"}, + {"target": "smithy.example#E"} + ] + }, + "smithy.example#MixinBlob": { + "type": "blob", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBlob": { + "type": "blob", + "mixins": [{ + "target": "smithy.example#MixinBlob" + }] + }, + "smithy.example#MixinBoolean": { + "type": "boolean", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBoolean": { + "type": "boolean", + "mixins": [{ + "target": "smithy.example#MixinBoolean" + }] + }, + "smithy.example#MixinString": { + "type": "string", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedString": { + "type": "string", + "mixins": [{ + "target": "smithy.example#MixinString" + }] + }, + "smithy.example#MixinByte": { + "type": "byte", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedByte": { + "type": "byte", + "mixins": [{ + "target": "smithy.example#MixinByte" + }] + }, + "smithy.example#MixinShort": { + "type": "short", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedShort": { + "type": "short", + "mixins": [{ + "target": "smithy.example#MixinShort" + }] + }, + "smithy.example#MixinInteger": { + "type": "integer", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedInteger": { + "type": "integer", + "mixins": [{ + "target": "smithy.example#MixinInteger" + }] + }, + "smithy.example#MixinLong": { + "type": "long", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedLong": { + "type": "long", + "mixins": [{ + "target": "smithy.example#MixinLong" + }] + }, + "smithy.example#MixinFloat": { + "type": "float", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedFloat": { + "type": "float", + "mixins": [{ + "target": "smithy.example#MixinFloat" + }] + }, + "smithy.example#MixinDouble": { + "type": "double", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedDouble": { + "type": "double", + "mixins": [{ + "target": "smithy.example#MixinDouble" + }] + }, + "smithy.example#MixinBigInt": { + "type": "bigInteger", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBigInt": { + "type": "bigInteger", + "mixins": [{ + "target": "smithy.example#MixinBigInt" + }] + }, + "smithy.example#MixinBigDecimal": { + "type": "bigDecimal", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedBigDecimal": { + "type": "bigDecimal", + "mixins": [{ + "target": "smithy.example#MixinBigDecimal" + }] + }, + "smithy.example#MixinTimestamp": { + "type": "timestamp", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedTimestamp": { + "type": "timestamp", + "mixins": [{ + "target": "smithy.example#MixinTimestamp" + }] + }, + "smithy.example#MixinDocument": { + "type": "document", + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedDocument": { + "type": "document", + "mixins": [{ + "target": "smithy.example#MixinDocument" + }] + }, + "smithy.example#MixinList": { + "type": "list", + "member": { + "target": "smithy.api#String" + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedList": { + "type": "list", + "mixins": [{ + "target": "smithy.example#MixinList" + }] + }, + "smithy.example#MixinSet": { + "type": "set", + "member": { + "target": "smithy.api#String" + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedSet": { + "type": "set", + "mixins": [{ + "target": "smithy.example#MixinSet" + }] + }, + "smithy.example#MixinMap": { + "type": "map", + "key": { + "target": "smithy.api#String" + }, + "value": { + "target": "smithy.api#String" + }, + "traits": { + "smithy.api#mixin": {} + } + }, + "smithy.example#MixedMap": { + "type": "map", + "mixins": [{ + "target": "smithy.example#MixinMap" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/operation-mixins.json b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/operation-mixins.json new file mode 100644 index 00000000000..af5f24087fd --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/operation-mixins.json @@ -0,0 +1,50 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#MixinError": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#error": "server" + } + }, + "smithy.example#MixinOperation": { + "type": "operation", + "input": { + "target": "smithy.api#Unit" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [{ + "target": "smithy.example#MixinError" + }], + "traits": { + "smithy.api#mixin": {}, + "smithy.api#internal": {} + } + }, + "smithy.example#ConcreteError": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#error": "client" + } + }, + "smithy.example#MixedOperation": { + "type": "operation", + "input": { + "target": "smithy.api#Unit" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [{ + "target": "smithy.example#ConcreteError" + }], + "mixins": [{ + "target": "smithy.example#MixinOperation" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/resource-mixins.json b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/resource-mixins.json new file mode 100644 index 00000000000..0040b90ba78 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/resource-mixins.json @@ -0,0 +1,18 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#MixinResource": { + "type": "resource", + "traits": { + "smithy.api#mixin": {}, + "smithy.api#internal": {} + } + }, + "smithy.example#MixedResource": { + "type": "resource", + "mixins": [{ + "target": "smithy.example#MixinResource" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/service-mixins-with-overrides.json b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/service-mixins-with-overrides.json new file mode 100644 index 00000000000..d1d8adcf9b2 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/service-mixins-with-overrides.json @@ -0,0 +1,104 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#MixedService": { + "type": "service", + "version": "2022-01-01", + "operations": [{ + "target": "smithy.example#MixedOperation" + }], + "resources": [{ + "target": "smithy.example#MixedResource" + }], + "errors": [{ + "target": "smithy.example#MixedError" + }], + "rename": { + "smithy.example#OverriddenRename": "Override", + "smithy.example#MixedRename": "LocalRename" + }, + "mixins": [{ + "target": "smithy.example#MixinService" + }] + }, + "smithy.example#MixinService": { + "type": "service", + "version": "2021-12-31", + "operations": [{ + "target": "smithy.example#MixinOperation" + }], + "resources": [{ + "target": "smithy.example#MixinResource" + }], + "errors": [{ + "target": "smithy.example#MixinError" + }], + "rename": { + "smithy.example#MixinRename": "ThisWillBeInherited", + "smithy.example#OverriddenRename": "ThisWillBeOverridden" + }, + "traits": { + "smithy.api#mixin": {}, + "smithy.api#internal": {} + } + }, + "smithy.example#MixedResource": { + "type": "resource" + }, + "smithy.example#MixinResource": { + "type": "resource" + }, + "smithy.example#MixedOperation": { + "type": "operation", + "input": { + "target": "smithy.api#Unit" + }, + "output": { + "target": "smithy.api#Unit" + } + }, + "smithy.example#MixinOperation": { + "type": "operation", + "input": { + "target": "smithy.api#Unit" + }, + "output": { + "target": "smithy.api#Unit" + } + }, + "smithy.example#MixedError": { + "type": "structure", + "members": { + "message": { + "target": "smithy.example#MixedRename" + } + }, + "traits": { + "smithy.api#error": "server" + } + }, + "smithy.example#MixinError": { + "type": "structure", + "members": { + "message": { + "target": "smithy.example#MixinRename" + }, + "state": { + "target": "smithy.example#OverriddenRename" + } + }, + "traits": { + "smithy.api#error": "client" + } + }, + "smithy.example#MixedRename": { + "type": "string" + }, + "smithy.example#MixinRename": { + "type": "string" + }, + "smithy.example#OverriddenRename": { + "type": "string" + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/service-mixins.json b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/service-mixins.json new file mode 100644 index 00000000000..adcb63eccba --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/ast-serialization/cases/service-mixins.json @@ -0,0 +1,57 @@ +{ + "smithy": "2.0", + "shapes": { + "smithy.example#ShapeToRename": { + "type": "string" + }, + "smithy.example#MixinError": { + "type": "structure", + "members": { + "message": { + "target": "smithy.example#ShapeToRename" + } + }, + "traits": { + "smithy.api#error": "client" + } + }, + "smithy.example#MixinOperation": { + "type": "operation", + "input": { + "target": "smithy.api#Unit" + }, + "output": { + "target": "smithy.api#Unit" + } + }, + "smithy.example#MixinResource": { + "type": "resource" + }, + "smithy.example#MixinService": { + "type": "service", + "version": "2021-12-31", + "errors": [{ + "target": "smithy.example#MixinError" + }], + "rename": { + "smithy.example#ShapeToRename": "RenamedShape" + }, + "operations": [{ + "target": "smithy.example#MixinOperation" + }], + "resources": [{ + "target": "smithy.example#MixinResource" + }], + "traits": { + "smithy.api#mixin": {}, + "smithy.api#internal": {} + } + }, + "smithy.example#MixedService": { + "type": "service", + "mixins": [{ + "target": "smithy.example#MixinService" + }] + } + } +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/data-mixins.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/data-mixins.smithy new file mode 100644 index 00000000000..1aa73197fe0 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/data-mixins.smithy @@ -0,0 +1,106 @@ +$version: "2.0" + +namespace smithy.example + +list MixedList with [MixinList] {} + +@internal +@mixin +list MixinList { + member: String +} + +set MixedSet with [MixinSet] {} + +@internal +@mixin +set MixinSet { + member: String +} + +map MixedMap with [MixinMap] {} + +@internal +@mixin +map MixinMap { + key: String + value: String +} + +bigDecimal MixedBigDecimal with [MixinBigDecimal] + +bigInteger MixedBigInt with [MixinBigInt] + +blob MixedBlob with [MixinBlob] + +boolean MixedBoolean with [MixinBoolean] + +byte MixedByte with [MixinByte] + +document MixedDocument with [MixinDocument] + +double MixedDouble with [MixinDouble] + +float MixedFloat with [MixinFloat] + +integer MixedInteger with [MixinInteger] + +long MixedLong with [MixinLong] + +short MixedShort with [MixinShort] + +string MixedString with [MixinString] + +timestamp MixedTimestamp with [MixinTimestamp] + +@internal +@mixin +bigDecimal MixinBigDecimal + +@internal +@mixin +bigInteger MixinBigInt + +@internal +@mixin +blob MixinBlob + +@internal +@mixin +boolean MixinBoolean + +@internal +@mixin +byte MixinByte + +@internal +@mixin +document MixinDocument + +@internal +@mixin +double MixinDouble + +@internal +@mixin +float MixinFloat + +@internal +@mixin +integer MixinInteger + +@internal +@mixin +long MixinLong + +@internal +@mixin +short MixinShort + +@internal +@mixin +string MixinString + +@internal +@mixin +timestamp MixinTimestamp diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy index 521b35b0738..3b192bcc73a 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy @@ -31,10 +31,10 @@ operation HasDocComments { } operation UsesMixins { - input := with NameBearer { + input := with [NameBearer] { id: String } - output := with NameBearer { + output := with [NameBearer] { id: String } } @@ -55,12 +55,12 @@ operation UsesTraits { operation UsesTraitsAndMixins { input := @sensitive - with NameBearer { + with [NameBearer] { id: String } output := @sensitive - with NameBearer { + with [NameBearer] { id: String } } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/mixins.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/mixins.smithy index 68d19cb55c5..1c3585cf4ea 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/mixins.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/mixins.smithy @@ -13,7 +13,7 @@ structure A { /// B @deprecated @mixin -structure B with A { +structure B with [A] { /// B.b b: String } @@ -33,7 +33,7 @@ structure C { web: "http://example.com" ) @mixin -structure D with C { +structure D with [C] { /// D.d d: String } @@ -46,17 +46,17 @@ apply D$c { /// E @mixin @since("X") -structure E with D { +structure E with [D] { /// E.e e: String } /// F @internal -structure F with +structure F with [ B E -{ +] { /// F.f f: String } @@ -68,10 +68,11 @@ apply F$a { apply F$e @sensitive -structure G with +structure G with [ B - E {} + E +] {} -structure H with B {} +structure H with [B] {} structure I {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/operation-mixins.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/operation-mixins.smithy new file mode 100644 index 00000000000..074f742ebfd --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/operation-mixins.smithy @@ -0,0 +1,27 @@ +$version: "2.0" + +namespace smithy.example + +operation ConcreteOperation with [InternalMixin] { + input: Unit + output: Unit + errors: [ + ConcreteError + ] +} + +@internal +@mixin +operation InternalMixin { + input: Unit + output: Unit + errors: [ + MixinError + ] +} + +@error("client") +structure ConcreteError {} + +@error("server") +structure MixinError {} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/resource-mixins.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/resource-mixins.smithy new file mode 100644 index 00000000000..325a6f403ba --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/resource-mixins.smithy @@ -0,0 +1,11 @@ +$version: "2.0" + +namespace smithy.example + +resource MixedResource with [MixinResource] { +} + +@internal +@mixin +resource MixinResource { +} diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-mixins-with-merging.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-mixins-with-merging.smithy new file mode 100644 index 00000000000..0cbe5249ea4 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-mixins-with-merging.smithy @@ -0,0 +1,72 @@ +$version: "2.0" + +namespace smithy.example + +service MixedService with [MixinService] { + version: "2022-01-01" + operations: [ + MixedOperation + ] + resources: [ + MixedResource + ] + errors: [ + MixedError + ] + rename: { + "smithy.example#OverriddenRename": "Override" + "smithy.example#MixedRename": "LocalRename" + } +} + +@internal +@mixin +service MixinService { + version: "2021-12-31" + operations: [ + MixinOperation + ] + resources: [ + MixinResource + ] + errors: [ + MixinError + ] + rename: { + "smithy.example#MixinRename": "ThisWillBeInherited" + "smithy.example#OverriddenRename": "ThisWillBeOverridden" + } +} + +resource MixedResource { +} + +resource MixinResource { +} + +operation MixedOperation { + input: Unit + output: Unit +} + +operation MixinOperation { + input: Unit + output: Unit +} + +@error("server") +structure MixedError { + message: MixedRename +} + +@error("client") +structure MixinError { + message: MixinRename + state: OverriddenRename +} + +string MixedRename + +string MixinRename + +string OverriddenRename diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-mixins.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-mixins.smithy new file mode 100644 index 00000000000..f98a1ab14a0 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/service-mixins.smithy @@ -0,0 +1,39 @@ +$version: "2.0" + +namespace smithy.example + +service MixedService with [MixinService] { +} + +@internal +@mixin +service MixinService { + version: "2021-12-31" + operations: [ + MixinOperation + ] + resources: [ + MixinResource + ] + errors: [ + MixinError + ] + rename: { + "smithy.example#ShapeToRename": "RenamedShape" + } +} + +resource MixinResource { +} + +operation MixinOperation { + input: Unit + output: Unit +} + +@error("client") +structure MixinError { + message: ShapeToRename +} + +string ShapeToRename diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/model.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/model.smithy index 2082229ef3a..aedd4db24ae 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/model.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/model.smithy @@ -9,14 +9,14 @@ structure A { } @mixin -structure A2 with A { +structure A2 with [A] { a2: String } apply A2$a @documentation("A2") @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -28,23 +28,23 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } apply C$a @documentation("C") -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3-b-b2-b3.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3-b-b2-b3.smithy index b7a0b8d3b3a..3abdd383f49 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3-b-b2-b3.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3-b-b2-b3.smithy @@ -7,6 +7,6 @@ structure C { c: String } -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3.smithy index 2a9ca83efce..58f96956cf8 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2-a3.smithy @@ -8,20 +8,20 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with B3 { +structure C with [B3] { c: String } -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2.smithy index dc5a55765fd..34da3740069 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-a2.smithy @@ -13,20 +13,20 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-b.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-b.smithy index e95e468a6ce..53d18568f44 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-b.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a-b.smithy @@ -8,7 +8,7 @@ structure A2 { } @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -18,15 +18,15 @@ structure B2 { } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a.smithy index 327a8929f8d..54eea282916 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a.smithy @@ -8,7 +8,7 @@ structure A2 { } @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -18,20 +18,20 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a2.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a2.smithy index 90607a9ce22..7f0c3be9446 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a2.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a2.smithy @@ -19,20 +19,20 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a3.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a3.smithy index 04f56c5b6cf..16942c7007e 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a3.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-a3.smithy @@ -9,7 +9,7 @@ structure A { } @mixin -structure A2 with A { +structure A2 with [A] { a2: String } @@ -21,20 +21,20 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with B3 { +structure C with [B3] { c: String } -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b.smithy index 8bd7c4c6f80..d80b7402d09 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b.smithy @@ -9,14 +9,14 @@ structure A { } @mixin -structure A2 with A { +structure A2 with [A] { a2: String } apply A2$a @documentation("A2") @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -28,18 +28,18 @@ structure B2 { } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } apply C$a @documentation("C") -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b2.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b2.smithy index 531921ab821..5056fe8440d 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b2.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b2.smithy @@ -9,14 +9,14 @@ structure A { } @mixin -structure A2 with A { +structure A2 with [A] { a2: String } apply A2$a @documentation("A2") @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -33,13 +33,13 @@ structure B3 { } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } apply C$a @documentation("C") -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b3.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b3.smithy index a8ad265cdde..94cbb77633d 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b3.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-b3.smithy @@ -9,14 +9,14 @@ structure A { } @mixin -structure A2 with A { +structure A2 with [A] { a2: String } apply A2$a @documentation("A2") @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -28,18 +28,18 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure C with A3 { +structure C with [A3] { c: String } apply C$a @documentation("C") -structure D with C { +structure D with [C] { d: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-c.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-c.smithy index 1a855bb381d..650c4ca6539 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-c.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-c.smithy @@ -12,14 +12,14 @@ structure A { } @mixin -structure A2 with A { +structure A2 with [A] { a2: String } apply A2$a @documentation("A2") @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -31,12 +31,12 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-d.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-d.smithy index 50e5b9b4c6c..a82391c3c3d 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-d.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/transform/mixin-removal/without-d.smithy @@ -11,14 +11,14 @@ structure A { } @mixin -structure A2 with A { +structure A2 with [A] { a2: String } apply A2$a @documentation("A2") @mixin -structure A3 with A2 { +structure A3 with [A2] { a3: String } @@ -30,17 +30,17 @@ structure B { } @mixin -structure B2 with B { +structure B2 with [B] { b2: String } @mixin -structure B3 with B2 { +structure B3 with [B2] { b3: String } @mixin -structure C with A3, B3 { +structure C with [A3, B3] { c: String } diff --git a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/model-with-mixins.smithy b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/model-with-mixins.smithy index d0041360f8c..c9c09f68ba8 100644 --- a/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/model-with-mixins.smithy +++ b/smithy-openapi/src/test/resources/software/amazon/smithy/openapi/fromsmithy/model-with-mixins.smithy @@ -19,6 +19,6 @@ structure Mixin { greeting: String } -structure Output with Mixin { +structure Output with [Mixin] { language: String } diff --git a/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java b/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java index 789efca1072..fcdefd02f52 100644 --- a/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java +++ b/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java @@ -103,6 +103,12 @@ public final int column() { return column; } + public final void rewind(int position, int line, int column) { + this.column = column; + this.line = line; + this.position = position; + } + /** * Checks if the parser has reached the end of the expression. *