Explicitly model and constrain members
Members of list, map, union, and structure are now explicitly modeled rather than relying on a generic grammar for all members. Two new constraints: 1. Map keys, if defined, must be defined before map values. 2. Members that are not elided must be on the same line. `foo:\nBar` is no longer valid.
This commit is contained in:
parent
c3ddd3b25a
commit
53c7c77021
|
@ -100,7 +100,7 @@ string support defined in `RFC 5234 <https://www.rfc-editor.org/rfc/rfc7405>`_.
|
|||
WS :1*(`SP` / `NL` / `Comment` / ",") ; whitespace
|
||||
SP :1*(%x20 / %x09) ; one or more spaces or tabs
|
||||
NL :%x0A / %x0D.0A ; Newline: \n and \r\n
|
||||
NotNL: %x09 / %x20-10FFFF ; Any character except newline
|
||||
NotNL:%x09 / %x20-10FFFF ; Any character except newline
|
||||
BR :*`SP` 1*(`Comment` / `NL`) *`WS`; line break followed by whitespace
|
||||
|
||||
.. rubric:: Comments
|
||||
|
@ -166,58 +166,71 @@ string support defined in `RFC 5234 <https://www.rfc-editor.org/rfc/rfc7405>`_.
|
|||
.. rubric:: Shapes
|
||||
|
||||
.. productionlist:: smithy
|
||||
ShapeSection :[`NamespaceStatement` `UseSection` `ShapeStatements`]
|
||||
NamespaceStatement :%s"namespace" `SP` `Namespace` `BR`
|
||||
UseSection :*(`UseStatement`)
|
||||
UseStatement :%s"use" `SP` `AbsoluteRootShapeId` `BR`
|
||||
ShapeStatements :*(`ShapeStatement` / `ApplyStatement`)
|
||||
ShapeStatement :`TraitStatements` `ShapeBody` `BR`
|
||||
ShapeBody :`SimpleShapeStatement`
|
||||
:/ `EnumShapeStatement`
|
||||
:/ `ListStatement`
|
||||
:/ `MapStatement`
|
||||
:/ `StructureStatement`
|
||||
:/ `UnionStatement`
|
||||
:/ `ServiceStatement`
|
||||
:/ `OperationStatement`
|
||||
:/ `ResourceStatement`
|
||||
SimpleShapeStatement :`SimpleTypeName` `SP` `Identifier` [`Mixins`]
|
||||
SimpleTypeName :%s"blob" / %s"boolean" / %s"document" / %s"string"
|
||||
:/ %s"byte" / %s"short" / %s"integer" / %s"long"
|
||||
:/ %s"float" / %s"double" / %s"bigInteger"
|
||||
:/ %s"bigDecimal" / %s"timestamp"
|
||||
Mixins :*`SP` %s"with" *`WS` "[" 1*(*`WS` `ShapeId`) *`WS` "]"
|
||||
EnumShapeStatement :`EnumTypeName` `SP` `Identifier` [`Mixins`] *`WS` `EnumShapeMembers`
|
||||
EnumTypeName :%s"enum" / %s"intEnum"
|
||||
EnumShapeMembers :"{" *`WS` 1*(`TraitStatements` `Identifier` [`ValueAssignment`] `*WS`) "}"
|
||||
ValueAssignment :*`SP` "=" *`SP` `NodeValue` `BR`
|
||||
ShapeMembers :"{" *`WS` *(`TraitStatements` `ShapeMember` *`WS`) "}"
|
||||
ShapeMember :(`ShapeMemberKvp` / `ShapeMemberElided`) [`ValueAssignment`]
|
||||
ShapeMemberKvp :`Identifier` *`WS` ":" *`WS` `ShapeId`
|
||||
ShapeMemberElided :"$" `Identifier`
|
||||
ListStatement :%s"list" `SP` `Identifier` [`Mixins`] *`WS` `ShapeMembers`
|
||||
MapStatement :%s"map" `SP` `Identifier` [`Mixins`] *`WS` `ShapeMembers`
|
||||
StructureStatement :%s"structure" `SP` `Identifier` [`StructureResource`]
|
||||
: [`Mixins`] *`WS` `ShapeMembers`
|
||||
StructureResource :`SP` %s"for" `SP` `ShapeId`
|
||||
UnionStatement :%s"union" `SP` `Identifier` [`Mixins`] *`WS` `ShapeMembers`
|
||||
ServiceStatement :%s"service" `SP` `Identifier` [`Mixins`] *`WS` `NodeObject`
|
||||
ResourceStatement :%s"resource" `SP` `Identifier` [`Mixins`] *`WS` `NodeObject`
|
||||
OperationStatement :%s"operation" `SP` `Identifier` [`Mixins`] *`WS` `OperationBody`
|
||||
OperationBody :"{" *`WS`
|
||||
: [`OperationInput`]
|
||||
: [`OperationOutput`]
|
||||
: [`OperationErrors`]
|
||||
: *`WS` "}"
|
||||
OperationInput :%s"input" *WS (`InlineStructure` / `Identifier`) `BR`
|
||||
OperationOutput :%s"output" *WS (`InlineStructure` / `ShapeId`) `BR`
|
||||
OperationErrors :%s"errors" *WS ":" *WS "[" *(*`WS` `Identifier`) *`WS` "]" `BR`
|
||||
InlineStructure :":=" *`WS` `TraitStatements` [`Mixins`] *`WS` `ShapeMembers`
|
||||
ShapeSection :[`NamespaceStatement` `UseSection` `ShapeStatements`]
|
||||
NamespaceStatement :%s"namespace" `SP` `Namespace` `BR`
|
||||
UseSection :*(`UseStatement`)
|
||||
UseStatement :%s"use" `SP` `AbsoluteRootShapeId` `BR`
|
||||
ShapeStatements :*(`ShapeStatement` / `ApplyStatement`)
|
||||
ShapeStatement :`TraitStatements` `ShapeBody` `BR`
|
||||
ShapeBody :`SimpleShapeStatement`
|
||||
:/ `EnumShapeStatement`
|
||||
:/ `ListStatement`
|
||||
:/ `MapStatement`
|
||||
:/ `StructureStatement`
|
||||
:/ `UnionStatement`
|
||||
:/ `ServiceStatement`
|
||||
:/ `OperationStatement`
|
||||
:/ `ResourceStatement`
|
||||
SimpleShapeStatement :`SimpleTypeName` `SP` `Identifier` [`Mixins`]
|
||||
SimpleTypeName :%s"blob" / %s"boolean" / %s"document" / %s"string"
|
||||
:/ %s"byte" / %s"short" / %s"integer" / %s"long"
|
||||
:/ %s"float" / %s"double" / %s"bigInteger"
|
||||
:/ %s"bigDecimal" / %s"timestamp"
|
||||
Mixins :*`SP` %s"with" *`WS` "[" 1*(*`WS` `ShapeId`) *`WS` "]"
|
||||
EnumShapeStatement :`EnumTypeName` `SP` `Identifier` [`Mixins`] *`WS` `EnumShapeMembers`
|
||||
EnumTypeName :%s"enum" / %s"intEnum"
|
||||
EnumShapeMembers :"{" *`WS` 1*(`TraitStatements` `Identifier` [`ValueAssignment`] `*WS`) "}"
|
||||
ValueAssignment :*`SP` "=" *`SP` `NodeValue` `BR`
|
||||
ListStatement :%s"list" `SP` `Identifier` [`Mixins`] *`WS` `ListMembers`
|
||||
ListMembers :"{" *`WS` `ListMember` *`WS` "}"
|
||||
ListMember :[TraitStatements] (`ElidedListMember` / `ExplicitListMember`)
|
||||
ElidedListMember :%s"$member"
|
||||
ExplicitListMember :%s"member" *`SP` ":" *`SP` `ShapeId`
|
||||
MapStatement :%s"map" `SP` `Identifier` [`Mixins`] *`WS` `MapMembers`
|
||||
MapMembers :"{" *`WS` `MapKey` `BR` `MapValue` *`WS` "}"
|
||||
MapKey :[TraitStatements] (`ElidedMapKey` / `ExplicitMapKey`)
|
||||
MapValue :[TraitStatements] (`ElidedMapValue` / `ExplicitMapValue`)
|
||||
ElidedMapKey :%s"$key"
|
||||
ExplicitMapKey :%s"key" *`SP` ":" *`SP` `ShapeId`
|
||||
ElidedMapValue :%s"$value"
|
||||
ExplicitMapValue :%s"value" *`SP` ":" *`SP` `ShapeId`
|
||||
StructureStatement :%s"structure" `SP` `Identifier` [`StructureResource`]
|
||||
: [`Mixins`] *`WS` `StructureMembers`
|
||||
StructureResource :`SP` %s"for" `SP` `ShapeId`
|
||||
StructureMembers :"{" *`WS` *(`TraitStatements` `StructureMember` *`WS`) "}"
|
||||
StructureMember :(`ExplicitStructureMember` / `ElidedStructureMember`) [`ValueAssignment`]
|
||||
ExplicitStructureMember :`Identifier` *`SP` ":" *`SP` `ShapeId`
|
||||
ElidedStructureMember :"$" `Identifier`
|
||||
UnionStatement :%s"union" `SP` `Identifier` [`Mixins`] *`WS` `UnionMembers`
|
||||
UnionMembers :"{" *`WS` *(`TraitStatements` `UnionMember` *`WS`) "}"
|
||||
UnionMember :(`ExplicitStructureMember` / `ElidedStructureMember`)
|
||||
ServiceStatement :%s"service" `SP` `Identifier` [`Mixins`] *`WS` `NodeObject`
|
||||
ResourceStatement :%s"resource" `SP` `Identifier` [`Mixins`] *`WS` `NodeObject`
|
||||
OperationStatement :%s"operation" `SP` `Identifier` [`Mixins`] *`WS` `OperationBody`
|
||||
OperationBody :"{" *`WS`
|
||||
: [`OperationInput`]
|
||||
: [`OperationOutput`]
|
||||
: [`OperationErrors`]
|
||||
: *`WS` "}"
|
||||
OperationInput :%s"input" *WS (`InlineStructure` / (":" *`WS` `ShapeId`)) `BR`
|
||||
OperationOutput :%s"output" *WS (`InlineStructure` / (":" *`WS` `ShapeId`)) `BR`
|
||||
OperationErrors :%s"errors" *WS ":" *WS "[" *(*`WS` `Identifier`) *`WS` "]" `BR`
|
||||
InlineStructure :":=" *`WS` `TraitStatements` [`Mixins`] *`WS` `StructureMembers`
|
||||
|
||||
.. rubric:: Traits
|
||||
|
||||
.. productionlist:: smithy
|
||||
TraitStatements : *(*`WS` `Trait`) *`WS`
|
||||
TraitStatements :*(*`WS` `Trait`) *`WS`
|
||||
Trait :"@" `ShapeId` [`TraitBody`]
|
||||
TraitBody :"(" *`WS` [`TraitBodyValue`] *`WS` ")"
|
||||
TraitBodyValue :`TraitStructure` / `NodeValue`
|
||||
|
@ -1491,12 +1504,11 @@ Target Elision
|
|||
|
||||
Having to completely redefine a :ref:`resource identifier <resource-identifiers>`
|
||||
to use it in a structure or redefine a member from a :ref:`mixin <mixins>` to add
|
||||
additional traits can be cumbersome and potentially error-prone. The
|
||||
:token:`type elision syntax <smithy:ShapeMemberElided>` can be used to cut
|
||||
down on that repetition by prefixing the member name with a ``$``. If a member
|
||||
is prefixed this way, its target will automatically be set to the target of a
|
||||
mixin member with the same name. The following example shows how to elide the
|
||||
target for a member inherited from a mixin:
|
||||
additional traits can be cumbersome and potentially error-prone. Target elision
|
||||
syntax can be used to cut down on that repetition by prefixing the member name
|
||||
with a ``$``. If a member is prefixed this way, its target will automatically be
|
||||
set to the target of a mixin member with the same name. The following example
|
||||
shows how to elide the target for a member inherited from a mixin:
|
||||
|
||||
.. code-block:: smithy
|
||||
|
||||
|
|
|
@ -549,7 +549,7 @@ Shape IDs are formally defined by the following ABNF:
|
|||
RootShapeId :`AbsoluteRootShapeId` / `Identifier`
|
||||
AbsoluteRootShapeId :`Namespace` "#" `Identifier`
|
||||
Namespace :`Identifier` *("." `Identifier`)
|
||||
Identifier :IdentifierStart *IdentifierChars
|
||||
Identifier :`IdentifierStart` *`IdentifierChars`
|
||||
IdentifierStart :*"_" ALPHA
|
||||
IdentifierChars :ALPHA / DIGIT / "_"
|
||||
ShapeIdMember :"$" `Identifier`
|
||||
|
|
|
@ -11,8 +11,7 @@ structure PrimitiveBearer {
|
|||
long: PrimitiveLong,
|
||||
short: PrimitiveShort,
|
||||
|
||||
handlesComments: // Nobody actually does this right?
|
||||
PrimitiveShort,
|
||||
handlesComments: PrimitiveShort, // comment
|
||||
|
||||
@required
|
||||
handlesRequired: PrimitiveLong,
|
||||
|
|
|
@ -19,8 +19,7 @@ structure PrimitiveBearer {
|
|||
short: PrimitiveShort,
|
||||
|
||||
@default(0)
|
||||
handlesComments: // Nobody actually does this right?
|
||||
PrimitiveShort,
|
||||
handlesComments: PrimitiveShort, // comment
|
||||
|
||||
@required
|
||||
handlesRequired: PrimitiveLong,
|
||||
|
|
|
@ -75,7 +75,6 @@ import software.amazon.smithy.model.validation.Severity;
|
|||
import software.amazon.smithy.model.validation.ValidationEvent;
|
||||
import software.amazon.smithy.model.validation.Validator;
|
||||
import software.amazon.smithy.utils.ListUtils;
|
||||
import software.amazon.smithy.utils.SetUtils;
|
||||
import software.amazon.smithy.utils.SimpleParser;
|
||||
import software.amazon.smithy.utils.StringUtils;
|
||||
|
||||
|
@ -233,7 +232,7 @@ final class IdlModelParser extends SimpleParser {
|
|||
|
||||
@Override
|
||||
public void sp() {
|
||||
while (!eof() && isSpaceOrComma(peek())) {
|
||||
while (isSpaceOrComma(peek())) {
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +245,7 @@ final class IdlModelParser extends SimpleParser {
|
|||
public void br() {
|
||||
int line = line();
|
||||
ws();
|
||||
if (line == line() && peek() != Character.MIN_VALUE) {
|
||||
if (line == line() && !eof()) {
|
||||
throw syntax("Expected a line break");
|
||||
}
|
||||
}
|
||||
|
@ -517,7 +516,7 @@ final class IdlModelParser extends SimpleParser {
|
|||
parseSimpleShape(id, location, StringShape.builder());
|
||||
break;
|
||||
case "enum":
|
||||
parseEnumShape(id, location, EnumShape.builder(), MemberParsing.PARSING_ENUM);
|
||||
parseEnumShape(id, location, EnumShape.builder());
|
||||
break;
|
||||
case "blob":
|
||||
parseSimpleShape(id, location, BlobShape.builder());
|
||||
|
@ -532,7 +531,7 @@ final class IdlModelParser extends SimpleParser {
|
|||
parseSimpleShape(id, location, IntegerShape.builder());
|
||||
break;
|
||||
case "intEnum":
|
||||
parseEnumShape(id, location, IntEnumShape.builder(), MemberParsing.PARSING_INT_ENUM);
|
||||
parseEnumShape(id, location, IntEnumShape.builder());
|
||||
break;
|
||||
case "long":
|
||||
parseSimpleShape(id, location, LongShape.builder());
|
||||
|
@ -585,15 +584,41 @@ final class IdlModelParser extends SimpleParser {
|
|||
operations.accept(operation);
|
||||
}
|
||||
|
||||
private void parseEnumShape(
|
||||
ShapeId id,
|
||||
SourceLocation location,
|
||||
AbstractShapeBuilder<?, ?> builder,
|
||||
MemberParsing memberParsing
|
||||
) {
|
||||
private void parseEnumShape(ShapeId id, SourceLocation location, AbstractShapeBuilder<?, ?> builder) {
|
||||
LoadOperation.DefineShape operation = createShape(builder.id(id).source(location));
|
||||
parseMixins(operation);
|
||||
parseMembers(operation, Collections.emptySet(), memberParsing);
|
||||
|
||||
ws();
|
||||
expect('{');
|
||||
clearPendingDocs();
|
||||
ws();
|
||||
|
||||
while (!eof() && peek() != '}') {
|
||||
List<TraitEntry> memberTraits = parseDocsAndTraits();
|
||||
SourceLocation memberLocation = currentLocation();
|
||||
String memberName = ParserUtils.parseIdentifier(this);
|
||||
MemberShape.Builder memberBuilder = MemberShape.builder()
|
||||
.id(id.withMember(memberName))
|
||||
.source(memberLocation)
|
||||
.target(UnitTypeTrait.UNIT);
|
||||
operation.addMember(memberBuilder);
|
||||
addTraits(memberBuilder.getId(), memberTraits);
|
||||
|
||||
// Check for optional value assignment.
|
||||
sp();
|
||||
if (peek() == '=') {
|
||||
expect('=');
|
||||
sp();
|
||||
Node value = IdlNodeParser.parseNode(this);
|
||||
memberBuilder.addTrait(new EnumValueTrait.Provider().createTrait(memberBuilder.getId(), value));
|
||||
br();
|
||||
}
|
||||
|
||||
clearPendingDocs();
|
||||
ws();
|
||||
}
|
||||
|
||||
expect('}');
|
||||
clearPendingDocs();
|
||||
operations.accept(operation);
|
||||
}
|
||||
|
@ -603,223 +628,69 @@ final class IdlModelParser extends SimpleParser {
|
|||
private void parseCollection(ShapeId id, SourceLocation location, CollectionShape.Builder<?, ?> builder) {
|
||||
LoadOperation.DefineShape operation = createShape(builder.id(id).source(location));
|
||||
parseMixins(operation);
|
||||
parseMembers(operation, SetUtils.of("member"));
|
||||
ws();
|
||||
expect('{');
|
||||
clearPendingDocs();
|
||||
ws();
|
||||
parsePossiblyElidedMember(operation, "member");
|
||||
ws();
|
||||
expect('}');
|
||||
|
||||
clearPendingDocs();
|
||||
operations.accept(operation);
|
||||
}
|
||||
|
||||
private void parseMembers(LoadOperation.DefineShape operation, Set<String> requiredMembers) {
|
||||
parseMembers(operation, requiredMembers, MemberParsing.PARSING_MEMBER);
|
||||
}
|
||||
|
||||
private enum MemberParsing {
|
||||
PARSING_INT_ENUM {
|
||||
@Override
|
||||
boolean supportsAssignment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
Trait createAssignmentTrait(ShapeId id, Node value) {
|
||||
NumberNode number = value.asNumberNode().orElseThrow(() -> ModelSyntaxException.builder()
|
||||
.shapeId(id)
|
||||
.sourceLocation(value)
|
||||
.message("intEnum shapes require integer values but found: " + Node.printJson(value))
|
||||
.build());
|
||||
if (number.isFloatingPointNumber()) {
|
||||
throw ModelSyntaxException.builder()
|
||||
.shapeId(id)
|
||||
.message("intEnum shapes do not support floating point values: " + value)
|
||||
.sourceLocation(value)
|
||||
.build();
|
||||
}
|
||||
long longValue = number.getValue().longValue();
|
||||
if (longValue > Integer.MAX_VALUE || longValue < Integer.MIN_VALUE) {
|
||||
throw ModelSyntaxException.builder()
|
||||
.shapeId(id)
|
||||
.message("intEnum must fit within an integer, but found: " + longValue)
|
||||
.sourceLocation(value)
|
||||
.build();
|
||||
}
|
||||
return EnumValueTrait.builder()
|
||||
.sourceLocation(value.getSourceLocation())
|
||||
.intValue(number.getValue().intValue())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean targetsUnit() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
PARSING_ENUM {
|
||||
@Override
|
||||
boolean supportsAssignment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
Trait createAssignmentTrait(ShapeId id, Node value) {
|
||||
String stringValue = value.asStringNode().orElseThrow(() -> ModelSyntaxException.builder()
|
||||
.shapeId(id)
|
||||
.sourceLocation(value)
|
||||
.message("enum shapes require string values but found: " + Node.printJson(value))
|
||||
.build())
|
||||
.getValue();
|
||||
return EnumValueTrait.builder()
|
||||
.sourceLocation(value.getSourceLocation())
|
||||
.stringValue(stringValue)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean targetsUnit() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
PARSING_STRUCTURE_MEMBER {
|
||||
@Override
|
||||
boolean supportsAssignment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
Trait createAssignmentTrait(ShapeId id, Node value) {
|
||||
return new DefaultTrait(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean targetsUnit() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
PARSING_MEMBER {
|
||||
@Override
|
||||
boolean supportsAssignment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
Trait createAssignmentTrait(ShapeId id, Node value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean targetsUnit() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
abstract boolean supportsAssignment();
|
||||
|
||||
abstract Trait createAssignmentTrait(ShapeId id, Node value);
|
||||
|
||||
abstract boolean targetsUnit();
|
||||
}
|
||||
|
||||
private void parseMembers(LoadOperation.DefineShape op, Set<String> requiredMembers, MemberParsing memberParsing) {
|
||||
Set<String> definedMembers = new HashSet<>();
|
||||
|
||||
ws();
|
||||
expect('{');
|
||||
ws();
|
||||
|
||||
while (!eof()) {
|
||||
if (peek() == '}') {
|
||||
break;
|
||||
}
|
||||
|
||||
parseMember(op, requiredMembers, definedMembers, memberParsing);
|
||||
|
||||
// Clears out any previously captured documentation
|
||||
// comments that may have been found when parsing the member.
|
||||
clearPendingDocs();
|
||||
|
||||
ws();
|
||||
}
|
||||
|
||||
if (eof()) {
|
||||
expect('}');
|
||||
}
|
||||
|
||||
expect('}');
|
||||
}
|
||||
|
||||
private void parseMember(
|
||||
LoadOperation.DefineShape operation,
|
||||
Set<String> allowed,
|
||||
Set<String> defined,
|
||||
MemberParsing memberParsing
|
||||
) {
|
||||
ShapeId parent = operation.toShapeId();
|
||||
|
||||
// Parse optional member traits.
|
||||
// Parsed list, set, and map members.
|
||||
private void parsePossiblyElidedMember(LoadOperation.DefineShape operation, String memberName) {
|
||||
boolean isElided = false;
|
||||
List<TraitEntry> memberTraits = parseDocsAndTraits();
|
||||
SourceLocation memberLocation = currentLocation();
|
||||
|
||||
boolean isTargetElided = !memberParsing.targetsUnit() && peek() == '$';
|
||||
if (isTargetElided) {
|
||||
if (peek() == '$') {
|
||||
isElided = true;
|
||||
if (!modelVersion.supportsTargetElision()) {
|
||||
throw syntax(operation.toShapeId().withMember(memberName),
|
||||
"Members can only elide targets in IDL version 2 or later");
|
||||
}
|
||||
expect('$');
|
||||
}
|
||||
|
||||
String memberName = ParserUtils.parseIdentifier(this);
|
||||
|
||||
if (defined.contains(memberName)) {
|
||||
// This is a duplicate member name.
|
||||
throw syntax(parent, "Duplicate member of " + parent + ": '" + memberName + '\'');
|
||||
}
|
||||
|
||||
defined.add(memberName);
|
||||
|
||||
// Only enforce "allowedMembers" if it isn't empty.
|
||||
if (!allowed.isEmpty() && !allowed.contains(memberName)) {
|
||||
throw syntax(parent, "Unexpected member of " + parent + ": '" + memberName + '\'');
|
||||
}
|
||||
|
||||
ShapeId memberId = parent.withMember(memberName);
|
||||
|
||||
if (isTargetElided && !modelVersion.supportsTargetElision()) {
|
||||
throw syntax(memberId, "Members can only elide targets in IDL version 2 or later. "
|
||||
+ "Attempted to elide a target with version `" + modelVersion + "`.");
|
||||
}
|
||||
|
||||
MemberShape.Builder memberBuilder = MemberShape.builder().id(memberId).source(memberLocation);
|
||||
|
||||
// Members whose targets are elided will have those targets resolved later,
|
||||
// for example by SetResourceBasedTargets
|
||||
if (!isTargetElided) {
|
||||
if (memberParsing.targetsUnit()) {
|
||||
addForwardReference(UnitTypeTrait.UNIT.toString(), memberBuilder::target);
|
||||
} else {
|
||||
ws();
|
||||
expect(':');
|
||||
ws();
|
||||
addForwardReference(ParserUtils.parseShapeId(this), memberBuilder::target);
|
||||
} else if (peek() != memberName.charAt(0)) {
|
||||
if (!memberTraits.isEmpty()) {
|
||||
throw syntax("Expected member definition to follow traits");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip spaces to check if there is default trait sugar.
|
||||
sp();
|
||||
MemberShape.Builder memberBuilder = MemberShape.builder()
|
||||
.id(operation.toShapeId().withMember(memberName))
|
||||
.source(currentLocation());
|
||||
|
||||
if (memberParsing.supportsAssignment() && peek() == '=') {
|
||||
if (!modelVersion.isDefaultSupported()) {
|
||||
throw syntax("@default assignment is only supported in IDL version 2 or later");
|
||||
}
|
||||
expect('=');
|
||||
for (int i = 0; i < memberName.length(); i++) {
|
||||
expect(memberName.charAt(i));
|
||||
}
|
||||
|
||||
if (!isElided) {
|
||||
sp();
|
||||
memberBuilder.addTrait(memberParsing.createAssignmentTrait(memberId, IdlNodeParser.parseNode(this)));
|
||||
br();
|
||||
expect(':');
|
||||
sp();
|
||||
addForwardReference(ParserUtils.parseShapeId(this), memberBuilder::target);
|
||||
}
|
||||
|
||||
// Only add the member once fully parsed.
|
||||
operation.addMember(memberBuilder);
|
||||
addTraits(memberBuilder.getId(), memberTraits);
|
||||
clearPendingDocs();
|
||||
}
|
||||
|
||||
private void parseMapStatement(ShapeId id, SourceLocation location) {
|
||||
LoadOperation.DefineShape operation = createShape(MapShape.builder().id(id).source(location));
|
||||
parseMixins(operation);
|
||||
parseMembers(operation, SetUtils.of("key", "value"));
|
||||
ws();
|
||||
expect('{');
|
||||
clearPendingDocs();
|
||||
ws();
|
||||
parsePossiblyElidedMember(operation, "key");
|
||||
ws();
|
||||
parsePossiblyElidedMember(operation, "value");
|
||||
ws();
|
||||
expect('}');
|
||||
clearPendingDocs();
|
||||
operations.accept(operation);
|
||||
}
|
||||
|
@ -840,7 +711,7 @@ final class IdlModelParser extends SimpleParser {
|
|||
|
||||
// Parse optional "with" statements to add mixins, but only if it's supported by the version.
|
||||
parseMixins(operation);
|
||||
parseMembers(operation, Collections.emptySet(), memberParsing);
|
||||
parseMembers(operation, memberParsing);
|
||||
clearPendingDocs();
|
||||
operations.accept(operation);
|
||||
}
|
||||
|
@ -878,6 +749,115 @@ final class IdlModelParser extends SimpleParser {
|
|||
clearPendingDocs();
|
||||
}
|
||||
|
||||
private enum MemberParsing {
|
||||
PARSING_STRUCTURE_MEMBER {
|
||||
@Override
|
||||
boolean supportsAssignment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
Trait createAssignmentTrait(ShapeId id, Node value) {
|
||||
return new DefaultTrait(value);
|
||||
}
|
||||
},
|
||||
PARSING_MEMBER {
|
||||
@Override
|
||||
boolean supportsAssignment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
Trait createAssignmentTrait(ShapeId id, Node value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
abstract boolean supportsAssignment();
|
||||
|
||||
abstract Trait createAssignmentTrait(ShapeId id, Node value);
|
||||
}
|
||||
|
||||
private void parseMembers(LoadOperation.DefineShape op, MemberParsing memberParsing) {
|
||||
Set<String> definedMembers = new HashSet<>();
|
||||
|
||||
ws();
|
||||
expect('{');
|
||||
ws();
|
||||
|
||||
while (!eof()) {
|
||||
if (peek() == '}') {
|
||||
break;
|
||||
}
|
||||
|
||||
parseMember(op, definedMembers, memberParsing);
|
||||
|
||||
// Clears out any previously captured documentation
|
||||
// comments that may have been found when parsing the member.
|
||||
clearPendingDocs();
|
||||
|
||||
ws();
|
||||
}
|
||||
|
||||
expect('}');
|
||||
}
|
||||
|
||||
private void parseMember(LoadOperation.DefineShape operation, Set<String> defined, MemberParsing memberParsing) {
|
||||
ShapeId parent = operation.toShapeId();
|
||||
|
||||
// Parse optional member traits.
|
||||
List<TraitEntry> memberTraits = parseDocsAndTraits();
|
||||
SourceLocation memberLocation = currentLocation();
|
||||
|
||||
boolean isTargetElided = peek() == '$';
|
||||
if (isTargetElided) {
|
||||
expect('$');
|
||||
}
|
||||
|
||||
String memberName = ParserUtils.parseIdentifier(this);
|
||||
|
||||
if (defined.contains(memberName)) {
|
||||
// This is a duplicate member name.
|
||||
throw syntax(parent, "Duplicate member of " + parent + ": '" + memberName + '\'');
|
||||
}
|
||||
|
||||
defined.add(memberName);
|
||||
|
||||
ShapeId memberId = parent.withMember(memberName);
|
||||
|
||||
if (isTargetElided && !modelVersion.supportsTargetElision()) {
|
||||
throw syntax(memberId, "Members can only elide targets in IDL version 2 or later");
|
||||
}
|
||||
|
||||
MemberShape.Builder memberBuilder = MemberShape.builder().id(memberId).source(memberLocation);
|
||||
|
||||
// Members whose targets are elided will have those targets resolved later,
|
||||
// for example by SetResourceBasedTargets
|
||||
if (!isTargetElided) {
|
||||
sp();
|
||||
expect(':');
|
||||
sp();
|
||||
addForwardReference(ParserUtils.parseShapeId(this), memberBuilder::target);
|
||||
}
|
||||
|
||||
// Skip spaces to check if there is default trait sugar.
|
||||
sp();
|
||||
|
||||
if (memberParsing.supportsAssignment() && peek() == '=') {
|
||||
if (!modelVersion.isDefaultSupported()) {
|
||||
throw syntax("@default assignment is only supported in IDL version 2 or later");
|
||||
}
|
||||
expect('=');
|
||||
sp();
|
||||
memberBuilder.addTrait(memberParsing.createAssignmentTrait(memberId, IdlNodeParser.parseNode(this)));
|
||||
br();
|
||||
}
|
||||
|
||||
// Only add the member once fully parsed.
|
||||
operation.addMember(memberBuilder);
|
||||
addTraits(memberBuilder.getId(), memberTraits);
|
||||
}
|
||||
|
||||
private void parseOperationStatement(ShapeId id, SourceLocation location) {
|
||||
OperationShape.Builder builder = OperationShape.builder().id(id).source(location);
|
||||
LoadOperation.DefineShape operation = createShape(builder);
|
||||
|
@ -926,8 +906,6 @@ final class IdlModelParser extends SimpleParser {
|
|||
parseIdList(builder::addError);
|
||||
br();
|
||||
expect('}');
|
||||
} else if (next != '}') {
|
||||
expect('}');
|
||||
}
|
||||
|
||||
clearPendingDocs();
|
||||
|
@ -966,7 +944,7 @@ final class IdlModelParser extends SimpleParser {
|
|||
LoadOperation.DefineShape operation = createShape(builder);
|
||||
parseMixins(operation);
|
||||
parseForResource(operation);
|
||||
parseMembers(operation, Collections.emptySet(), MemberParsing.PARSING_STRUCTURE_MEMBER);
|
||||
parseMembers(operation, MemberParsing.PARSING_STRUCTURE_MEMBER);
|
||||
addTraits(id, traits);
|
||||
clearPendingDocs();
|
||||
operations.accept(operation);
|
||||
|
|
|
@ -169,7 +169,7 @@ final class LoadOperationProcessor implements Consumer<LoadOperation> {
|
|||
} else {
|
||||
// Try to find a prelude shape by ID if no ID exists in the namespace with this name.
|
||||
ShapeId preludeId = ShapeId.fromOptionalNamespace(Prelude.NAMESPACE, reference.name);
|
||||
if (prelude.getShapeIds().contains(preludeId)) {
|
||||
if (prelude != null && prelude.getShapeIds().contains(preludeId)) {
|
||||
reference.resolve(preludeId, test -> prelude.expectShape(test).getType());
|
||||
} else {
|
||||
reference.resolve(inNamespace, test -> null);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package software.amazon.smithy.model.traits;
|
||||
|
||||
import java.util.Optional;
|
||||
import software.amazon.smithy.model.SourceException;
|
||||
import software.amazon.smithy.model.node.ExpectationNotMetException;
|
||||
import software.amazon.smithy.model.node.Node;
|
||||
import software.amazon.smithy.model.node.NumberNode;
|
||||
|
@ -31,19 +30,14 @@ import software.amazon.smithy.utils.ToSmithyBuilder;
|
|||
public final class EnumValueTrait extends AbstractTrait implements ToSmithyBuilder<EnumValueTrait> {
|
||||
public static final ShapeId ID = ShapeId.from("smithy.api#enumValue");
|
||||
|
||||
private final String string;
|
||||
private final Integer integer;
|
||||
private final Node value;
|
||||
|
||||
private EnumValueTrait(Builder builder) {
|
||||
super(ID, builder.sourceLocation);
|
||||
string = builder.string;
|
||||
integer = builder.integer;
|
||||
if (string == null && integer == null) {
|
||||
throw new SourceException(
|
||||
"Either a string value or an integer value must be set for the enumValue trait.",
|
||||
getSourceLocation()
|
||||
);
|
||||
if (builder.value == null) {
|
||||
throw new IllegalStateException("No integer or string value set on EnumValueTrait");
|
||||
}
|
||||
value = builder.value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,7 +46,7 @@ public final class EnumValueTrait extends AbstractTrait implements ToSmithyBuild
|
|||
* @return Optionally returns the string value.
|
||||
*/
|
||||
public Optional<String> getStringValue() {
|
||||
return Optional.ofNullable(string);
|
||||
return value.asStringNode().map(StringNode::getValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +67,7 @@ public final class EnumValueTrait extends AbstractTrait implements ToSmithyBuild
|
|||
* @return Returns the set int value.
|
||||
*/
|
||||
public Optional<Integer> getIntValue() {
|
||||
return Optional.ofNullable(integer);
|
||||
return value.asNumberNode().map(NumberNode::getValue).map(Number::intValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,41 +90,20 @@ public final class EnumValueTrait extends AbstractTrait implements ToSmithyBuild
|
|||
@Override
|
||||
public Trait createTrait(ShapeId target, Node value) {
|
||||
Builder builder = builder().sourceLocation(value);
|
||||
value.asStringNode().ifPresent(node -> builder.stringValue(node.getValue()));
|
||||
value.asNumberNode().ifPresent(node -> {
|
||||
if (node.isNaturalNumber()) {
|
||||
builder.intValue(node.getValue().intValue());
|
||||
} else {
|
||||
throw new SourceException(
|
||||
"Enum values may not use fractional numbers.",
|
||||
value.getSourceLocation()
|
||||
);
|
||||
}
|
||||
});
|
||||
EnumValueTrait result = builder.build();
|
||||
result.setNodeCache(value);
|
||||
return result;
|
||||
builder.value = value;
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNode() {
|
||||
if (getIntValue().isPresent()) {
|
||||
return new NumberNode(integer, getSourceLocation());
|
||||
} else {
|
||||
return new StringNode(string, getSourceLocation());
|
||||
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmithyBuilder<EnumValueTrait> toBuilder() {
|
||||
Builder builder = builder().sourceLocation(getSourceLocation());
|
||||
if (getIntValue().isPresent()) {
|
||||
builder.intValue(getIntValue().get());
|
||||
} else if (getStringValue().isPresent()) {
|
||||
builder.stringValue(getStringValue().get());
|
||||
}
|
||||
builder.value = value;
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -139,8 +112,7 @@ public final class EnumValueTrait extends AbstractTrait implements ToSmithyBuild
|
|||
}
|
||||
|
||||
public static final class Builder extends AbstractTraitBuilder<EnumValueTrait, Builder> {
|
||||
private String string;
|
||||
private Integer integer;
|
||||
private Node value;
|
||||
|
||||
@Override
|
||||
public EnumValueTrait build() {
|
||||
|
@ -148,14 +120,12 @@ public final class EnumValueTrait extends AbstractTrait implements ToSmithyBuild
|
|||
}
|
||||
|
||||
public Builder stringValue(String string) {
|
||||
this.string = string;
|
||||
this.integer = null;
|
||||
this.value = Node.from(string);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder intValue(int integer) {
|
||||
this.integer = integer;
|
||||
this.string = null;
|
||||
this.value = Node.from(integer);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.util.Set;
|
|||
import java.util.regex.Pattern;
|
||||
import software.amazon.smithy.model.FromSourceLocation;
|
||||
import software.amazon.smithy.model.Model;
|
||||
import software.amazon.smithy.model.node.Node;
|
||||
import software.amazon.smithy.model.node.NumberNode;
|
||||
import software.amazon.smithy.model.shapes.EnumShape;
|
||||
import software.amazon.smithy.model.shapes.IntEnumShape;
|
||||
import software.amazon.smithy.model.shapes.MemberShape;
|
||||
|
@ -60,16 +62,16 @@ public final class EnumShapeValidator extends AbstractValidator {
|
|||
private void validateEnumShape(List<ValidationEvent> events, EnumShape shape) {
|
||||
Set<String> values = new HashSet<>();
|
||||
for (MemberShape member : shape.members()) {
|
||||
Optional<String> value = member.expectTrait(EnumValueTrait.class).getStringValue();
|
||||
EnumValueTrait trait = member.expectTrait(EnumValueTrait.class);
|
||||
Optional<String> value = trait.getStringValue();
|
||||
if (!value.isPresent()) {
|
||||
events.add(error(member, member.expectTrait(EnumValueTrait.class),
|
||||
"The enumValue trait must use the string option when applied to enum shapes."));
|
||||
"enum members can only be assigned string values, but found: "
|
||||
+ Node.printJson(trait.toNode())));
|
||||
} else {
|
||||
if (!values.add(value.get())) {
|
||||
events.add(error(member, String.format(
|
||||
"Multiple enum members found with duplicate value `%s`",
|
||||
value.get()
|
||||
)));
|
||||
events.add(error(member, String.format("Multiple enum members found with duplicate value `%s`",
|
||||
value.get())));
|
||||
}
|
||||
if (value.get().equals("")) {
|
||||
events.add(error(member, "enum values may not be empty."));
|
||||
|
@ -82,25 +84,49 @@ public final class EnumShapeValidator extends AbstractValidator {
|
|||
private void validateIntEnumShape(List<ValidationEvent> events, IntEnumShape shape) {
|
||||
Set<Integer> values = new HashSet<>();
|
||||
for (MemberShape member : shape.members()) {
|
||||
// intEnum must all have the EnumValueTrait.
|
||||
if (!member.hasTrait(EnumValueTrait.ID)) {
|
||||
events.add(missingIntEnumValue(member, member));
|
||||
} else if (!member.expectTrait(EnumValueTrait.class).getIntValue().isPresent()) {
|
||||
events.add(missingIntEnumValue(member, member.expectTrait(EnumValueTrait.class)));
|
||||
} else {
|
||||
int value = member.expectTrait(EnumValueTrait.class).getIntValue().get();
|
||||
if (!values.add(value)) {
|
||||
events.add(error(member, String.format(
|
||||
"Multiple enum members found with duplicate value `%s`",
|
||||
value
|
||||
)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
EnumValueTrait trait = member.expectTrait(EnumValueTrait.class);
|
||||
|
||||
// The EnumValueTrait must point to a number.
|
||||
if (!trait.getIntValue().isPresent()) {
|
||||
ValidationEvent event = error(member, trait, "intEnum members require integer values, but found: "
|
||||
+ Node.printJson(trait.toNode()));
|
||||
events.add(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
NumberNode number = trait.toNode().asNumberNode().get();
|
||||
|
||||
// Validate the it is an integer.
|
||||
if (number.isFloatingPointNumber()) {
|
||||
events.add(error(member, trait, "intEnum members do not support floating point values: "
|
||||
+ number.getValue()));
|
||||
continue;
|
||||
}
|
||||
|
||||
long longValue = number.getValue().longValue();
|
||||
if (longValue > Integer.MAX_VALUE || longValue < Integer.MIN_VALUE) {
|
||||
events.add(error(member, trait, "intEnum members must fit within an integer, but found: "
|
||||
+ longValue));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!values.add(number.getValue().intValue())) {
|
||||
events.add(error(member, String.format("Multiple intEnum members found with duplicate value `%d`",
|
||||
number.getValue().intValue())));
|
||||
}
|
||||
|
||||
validateEnumMemberName(events, member);
|
||||
}
|
||||
}
|
||||
|
||||
private ValidationEvent missingIntEnumValue(Shape shape, FromSourceLocation sourceLocation) {
|
||||
return error(shape, sourceLocation, "intEnum members must have the enumValue trait with the `int` member set");
|
||||
return error(shape, sourceLocation, "intEnum members must be assigned an integer value");
|
||||
}
|
||||
|
||||
private void validateEnumMemberName(List<ValidationEvent> events, MemberShape member) {
|
||||
|
|
|
@ -873,4 +873,17 @@ public class ModelAssemblerTest {
|
|||
|
||||
assertThat(createdMember.getAllTraits(), not(hasKey(BoxTrait.ID)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canResolveTargetsWithoutPrelude() {
|
||||
ValidatedResult<Model> model = Model.assembler()
|
||||
.disablePrelude()
|
||||
.addUnparsedModel("foo.smithy", "$version: \"2.0\"\n"
|
||||
+ "namespace smithy.example\n"
|
||||
+ "list Foo { member: String }\n")
|
||||
.assemble();
|
||||
|
||||
assertThat(model.getValidationEvents(), hasSize(1));
|
||||
assertThat(model.getValidationEvents().get(0).getMessage(), containsString("unresolved shape"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
[ERROR] com.foo#List: Parse error at line 7, column 11 near `: `: Duplicate member of com.foo#List: 'member' | Model
|
||||
[ERROR] -: Parse error at line 7, column 5 near `member`: Expected: '}', but found 'm' | Model
|
||||
|
|
|
@ -1 +1 @@
|
|||
[ERROR] com.foo#Map: Parse error at line 7, column 8 near `: `: Duplicate member of com.foo#Map: 'key' | Model
|
||||
[ERROR] -: Parse error at line 7, column 5 near `key`: Expected: '}', but found 'k' | Model
|
||||
|
|
|
@ -1 +1 @@
|
|||
[ERROR] com.foo#Set: Parse error at line 7, column 11 near `: `: Duplicate member of com.foo#Set: 'member' | Model
|
||||
[ERROR] -: Parse error at line 7, column 5 near `member`: Expected: '}', but found 'm' | Model
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[ERROR] smithy.example#IntEnum$FLOAT: Error creating trait `enumValue`: Enum values may not use fractional numbers. | Model
|
||||
[ERROR] smithy.example#IntEnum$ARRAY: Error creating trait `enumValue`: Either a string value or an integer value must be set for the enumValue trait. | Model
|
||||
[ERROR] smithy.example#IntEnum$MAP: Error creating trait `enumValue`: Either a string value or an integer value must be set for the enumValue trait. | Model
|
||||
[ERROR] smithy.example#IntEnum$NULL: Error creating trait `enumValue`: Either a string value or an integer value must be set for the enumValue trait. | Model
|
||||
[ERROR] smithy.example#IntEnum$BOOLEAN: Error creating trait `enumValue`: Either a string value or an integer value must be set for the enumValue trait. | Model
|
|
@ -1,9 +1,9 @@
|
|||
[ERROR] ns.foo#StringEnum$INT_VALUE: The enumValue trait must use the string option when applied to enum shapes. | EnumShape
|
||||
[ERROR] ns.foo#StringEnum$INT_VALUE: enum members can only be assigned string values, but found: 1 | EnumShape
|
||||
[ERROR] ns.foo#StringEnum$DUPLICATE_VALUE: Multiple enum members found with duplicate value `explicit` | EnumShape
|
||||
[WARNING] ns.foo#StringEnum$undesirableName: The name `undesirableName` does not match the recommended enum name format of beginning with an uppercase letter, followed by any number of uppercase letters, numbers, or underscores. | EnumShape
|
||||
[ERROR] ns.foo#IntEnum$IMPLICIT_VALUE: intEnum members must have the enumValue trait with the `int` member set | EnumShape
|
||||
[ERROR] ns.foo#IntEnum$STRING_VALUE: intEnum members must have the enumValue trait with the `int` member set | EnumShape
|
||||
[ERROR] ns.foo#IntEnum$DUPLICATE_VALUE: Multiple enum members found with duplicate value `1` | EnumShape
|
||||
[ERROR] ns.foo#IntEnum$IMPLICIT_VALUE: intEnum members must be assigned an integer value | EnumShape
|
||||
[ERROR] ns.foo#IntEnum$STRING_VALUE: intEnum members require integer values, but found: "foo" | EnumShape
|
||||
[ERROR] ns.foo#IntEnum$DUPLICATE_VALUE: Multiple intEnum members found with duplicate value `1` | EnumShape
|
||||
[WARNING] ns.foo#IntEnum$undesirableName: The name `undesirableName` does not match the recommended enum name format of beginning with an uppercase letter, followed by any number of uppercase letters, numbers, or underscores. | EnumShape
|
||||
[WARNING] ns.foo#EnumWithEnumTrait: This shape applies a trait that is deprecated: smithy.api#enum | DeprecatedTrait
|
||||
[ERROR] ns.foo#EnumWithEnumTrait: Trait `enum` cannot be applied to `ns.foo#EnumWithEnumTrait`. This trait may only be applied to shapes that match the following selector: string :not(enum) | TraitTarget
|
|
@ -0,0 +1,5 @@
|
|||
[ERROR] smithy.example#IntEnum$FLOAT: intEnum members do not support floating point values: 1.1 | EnumShape
|
||||
[ERROR] smithy.example#IntEnum$ARRAY: intEnum members require integer values, but found: [1] | EnumShape
|
||||
[ERROR] smithy.example#IntEnum$MAP: intEnum members require integer values, but found: {"foo":"bar"} | EnumShape
|
||||
[ERROR] smithy.example#IntEnum$NULL: intEnum members require integer values, but found: null | EnumShape
|
||||
[ERROR] smithy.example#IntEnum$BOOLEAN: intEnum members require integer values, but found: true | EnumShape
|
|
@ -0,0 +1,7 @@
|
|||
// Parse error at line 6, column 17 near `= `: @default assignment is only supported in IDL version 2 or later | Model
|
||||
$version: "1.0"
|
||||
namespace smithy.example
|
||||
|
||||
structure Foo {
|
||||
baz: String = "hello"
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Members can only elide targets in IDL version 2 or later. Attempted to elide a target with version `1.0`.
|
||||
// Members can only elide targets in IDL version 2 or later
|
||||
$version: "1.0"
|
||||
|
||||
namespace smithy.example
|
|
@ -0,0 +1,7 @@
|
|||
// Parse error at line 6, column 5 near `$m`: Members can only elide targets in IDL version 2 or later
|
||||
$version: "1.0"
|
||||
namespace smithy.example
|
||||
|
||||
list Foos {
|
||||
$member
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// Parse error at line 6, column 9 near `\n}`: Members can only elide targets in IDL version 2 or later
|
||||
$version: "1.0"
|
||||
namespace smithy.example
|
||||
|
||||
structure Foo {
|
||||
$bar
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// smithy.example#Foo$BAR: enum shapes require string values but found: 10
|
||||
// smithy.example#Foo$BAR: enum members can only be assigned string values, but found: 10 | EnumShape
|
||||
$version: "2.0"
|
||||
|
||||
namespace smithy.example
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// [ERROR] smithy.example#Foo$BAR: intEnum shapes require integer values but found: "Abc"
|
||||
// smithy.example#Foo$BAR: intEnum members require integer values, but found: "Abc"
|
||||
$version: "2.0"
|
||||
|
||||
namespace smithy.example
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// [ERROR] smithy.example#Foo$BAR: intEnum shapes do not support floating point values
|
||||
// smithy.example#Foo$BAR: intEnum members do not support floating point values
|
||||
$version: "2.0"
|
||||
|
||||
namespace smithy.example
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// smithy.example#Foo$BAR: intEnum must fit within an integer, but found: 2147483648
|
||||
// smithy.example#Foo$BAR: intEnum members must fit within an integer, but found: 2147483648
|
||||
$version: "2.0"
|
||||
|
||||
namespace smithy.example
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// enum must have at least one entry
|
||||
// smithy.example#Enum: enum must have at least one entry | Model
|
||||
|
||||
$version: "2.0"
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Parse error at line 5, column 6 near `: `: Unexpected member of com.foo#MyList: 'foo' | Model
|
||||
// Parse error at line 5, column 3 near `foo`: Expected: '}', but found 'f' | Model
|
||||
namespace com.foo
|
||||
|
||||
list MyList {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Parse error at line 7, column 7 near `: `: Unexpected member of com.foo#MyMap: 'fuzz'
|
||||
// Parse error at line 7, column 3 near `fuzz`: Expected: '}', but found 'f' | Model
|
||||
namespace com.foo
|
||||
|
||||
map MyMap {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Parse error at line 5, column 6 near `: `: Unexpected member of com.foo#MySet: 'foo'
|
||||
// Parse error at line 5, column 3 near `foo`: Expected: '}', but found 'f' | Model
|
||||
namespace com.foo
|
||||
|
||||
set MySet {
|
||||
|
|
|
@ -51,10 +51,7 @@ structure L {
|
|||
structure M {
|
||||
@deprecated
|
||||
@since("2.0")
|
||||
foo:
|
||||
E,
|
||||
foo:E,
|
||||
@deprecated
|
||||
baz
|
||||
:
|
||||
H
|
||||
baz:H
|
||||
}
|
||||
|
|
|
@ -55,10 +55,7 @@ union L {
|
|||
union M {
|
||||
@deprecated
|
||||
@since("2.0")
|
||||
foo:
|
||||
E,
|
||||
foo:E,
|
||||
@deprecated
|
||||
baz
|
||||
:
|
||||
H
|
||||
baz:H
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue