Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve dupe apply to mixed members #2030

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import software.amazon.smithy.model.SourceException;
import software.amazon.smithy.model.SourceLocation;
Expand All @@ -47,6 +49,7 @@ final class LoaderTraitMap {
private final List<ValidationEvent> events;
private final boolean allowUnknownTraits;
private final Map<ShapeId, Map<ShapeId, Trait>> unclaimed = new HashMap<>();
private final Set<ShapeId> claimed = new HashSet<>();

LoaderTraitMap(TraitFactory traitFactory, List<ValidationEvent> events, boolean allowUnknownTraits) {
this.traitFactory = traitFactory;
Expand Down Expand Up @@ -147,12 +150,19 @@ private void applyTraitsToShape(AbstractShapeBuilder<?, ?> shape, Trait trait) {
// Traits can be applied to synthesized members inherited from mixins. Applying these traits is deferred until
// the point in which mixin members are synthesized into shapes.
Map<ShapeId, Trait> claimTraitsForShape(ShapeId id) {
return unclaimed.containsKey(id) ? unclaimed.remove(id) : Collections.emptyMap();
if (!unclaimed.containsKey(id)) {
return Collections.emptyMap();
}
claimed.add(id);
return unclaimed.get(id);
}

// Emit events if any traits were applied to shapes that weren't found in the model.
void emitUnclaimedTraits() {
for (Map.Entry<ShapeId, Map<ShapeId, Trait>> entry : unclaimed.entrySet()) {
if (claimed.contains(entry.getKey())) {
continue;
}
for (Map.Entry<ShapeId, Trait> traitEntry : entry.getValue().entrySet()) {
events.add(ValidationEvent.builder()
.id(Validator.MODEL_ERROR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1341,4 +1341,31 @@ public void exceptionsThrownWhenCreatingTraitsDontCrashSmithy() {
assertThat(result.getValidationEvents(Severity.ERROR).get(0).getMessage(),
equalTo("Error creating trait `smithy.foo#baz`: Oops!"));
}

@Test
public void resolvesDuplicateTraitApplicationsToDuplicateMixedInMembers() throws Exception {
String model = IoUtils.readUtf8File(Paths.get(getClass().getResource("mixins/apply-to-mixed-member.json").toURI()));
// Should be able to de-conflict the apply statements when the same model is loaded multiple times.
// See https://github.com/smithy-lang/smithy/issues/2004
Model.assembler()
.addUnparsedModel("test.json", model)
.addUnparsedModel("test2.json", model)
.addUnparsedModel("test3.json", model)
.assemble()
.unwrap();
}

@Test
public void resolvesDuplicateTraitApplicationsToSameMixedInMember() throws Exception {
String modelToApplyTo = IoUtils.readUtf8File(Paths.get(getClass().getResource("mixins/mixed-member.smithy").toURI()));
String modelWithApply = IoUtils.readUtf8File(Paths.get(getClass().getResource("mixins/member-apply-other-namespace.smithy").toURI()));
// Should be able to load when you have multiple identical apply statements to the same mixed in member.
// See https://github.com/smithy-lang/smithy/issues/2004
Model.assembler()
.addUnparsedModel("mixed-member.smithy", modelToApplyTo)
.addUnparsedModel("member-apply-1.smithy", modelWithApply)
.addUnparsedModel("member-apply-2.smithy", modelWithApply)
.assemble()
.unwrap();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"smithy": "2.0",
"shapes": {
"com.example#Common": {
"type": "structure",
"members": {
"description": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": {}
}
}
},
"traits": {
"smithy.api#mixin": {}
}
},
"com.example#Thing": {
"type": "structure",
"mixins": [
{
"target": "com.example#Common"
}
],
"members": {}
},
"com.example#Thing$description": {
"type": "apply",
"traits": {
"smithy.api#default": "test"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
$version: "2.0"

namespace com.example.other

use com.example#Thing

apply Thing$description @default("test")
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
$version: "2.0"

namespace com.example

@mixin
structure Common {
@required
description: String
}

structure Thing with [Common] {}