Repository: smithy-lang/smithy
Pull request #2276 (2024-05-06 17:12:53)

Bump version to 1.49.0
#### Background
- Bumps version to 1.49.0

---
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2778a445c..b95d0290f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,28 @@
 # Smithy Changelog
 
+## 1.49.0 (2024-05-08)
+
+### Features
+
+* Added list and map shapes to directed codegen. ([#2273](https://github.com/smithy-lang/smithy/pull/2273))
+* Added support for defaults on trait member values in trait-codegen. ([#2267](https://github.com/smithy-lang/smithy/pull/2267))
+* Added support for string-arrays for as parameters in rules-engine. ([#2266](https://github.com/smithy-lang/smithy/pull/2266))
+* Added new trait (`@operationContextParams`) for binding array parameters to nested operation inputs in the rules-engine. ([#2264](https://github.com/smithy-lang/smithy/pull/2264))
+
+### Bug Fixes
+
+* Fixed bug in the formatter where comments in operation errors were not being handled correctly. ([#2283](https://github.com/smithy-lang/smithy/pull/2283))
+* Fixed bug in the formatter where certain documentation comments were being converted to line-comments. ([#2277](https://github.com/smithy-lang/smithy/pull/2277))
+* Fixed incorrect status code in restXml response protocol test. ([#2272](https://github.com/smithy-lang/smithy/pull/2272))
+* Fixed ec2query protocol test for empty list serialization. ([#2269](https://github.com/smithy-lang/smithy/pull/2269))
+* Fixed Javadoc integration for documentation in members and enum variants in trait-codegen. ([#2265](https://github.com/smithy-lang/smithy/pull/2265)) 
+* Fixed timing issue in the delay calculation for waiter retries. ([#2259](https://github.com/smithy-lang/smithy/pull/2259))
+
+### Documentation
+
+* Corrected a typo in RPCv2 CBOR specificaiton. ([#2278](https://github.com/smithy-lang/smithy/pull/2278))
+* Updated casing of aggregate shape names in shape type table. ([#2271](https://github.com/smithy-lang/smithy/pull/2271))
+
 ## 1.48.0 (2024-04-24)
 
 ### Features
diff --git a/VERSION b/VERSION
index 9db5ea12f..7f3a46a84 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-1.48.0
+1.49.0
diff --git a/docs/source-2.0/additional-specs/protocols/smithy-rpc-v2.rst b/docs/source-2.0/additional-specs/protocols/smithy-rpc-v2.rst
index 2b2e4664b..40479383a 100644
--- a/docs/source-2.0/additional-specs/protocols/smithy-rpc-v2.rst
+++ b/docs/source-2.0/additional-specs/protocols/smithy-rpc-v2.rst
@@ -298,8 +298,8 @@ headers for responses:
       - Required
       - The value of ``rpc-v2-cbor``.
     * - ``Content-Type``
-      - Required with request bodies
-      - The value of ``application/cbor``. For event streaming requests, this
+      - Required with response bodies
+      - The value of ``application/cbor``. For event streaming responses, this
         is ``application/vnd.amazon.eventstream``.
     * - ``Content-Length``
       - Conditional
diff --git a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/BracketFormatter.java b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/BracketFormatter.java
index 68c494ff8..03899eac2 100644
--- a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/BracketFormatter.java
+++ b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/BracketFormatter.java
@@ -38,7 +38,37 @@ final class BracketFormatter {
             Function<TreeCursor, Doc> visitor,
             Function<TreeCursor, Stream<TreeCursor>> mapper
     ) {
-        return new Extractor(visitor, mapper);
+        return extractor(visitor, mapper, TreeCursor::children);
+    }
+
+    static Function<TreeCursor, Stream<Doc>> extractor(
+            Function<TreeCursor, Doc> visitor,
+            Function<TreeCursor, Stream<TreeCursor>> mapper,
+            Function<TreeCursor, Stream<TreeCursor>> childrenSupplier
+    ) {
+        return (cursor) -> childrenSupplier.apply(cursor)
+                .flatMap(c -> {
+                    TreeType type = c.getTree().getType();
+                    return type == TreeType.WS || type == TreeType.COMMENT ? Stream.of(c) : mapper.apply(c);
+                })
+                .flatMap(c -> {
+                    // If the child extracts WS, then filter it down to just comments.
+                    return c.getTree().getType() == TreeType.WS
+                            ? c.getChildrenByType(TreeType.COMMENT).stream()
+                            : Stream.of(c);
+                })
+                .map(visitor)
+                .filter(doc -> doc != Doc.empty());
+    }
+
+    static Function<TreeCursor, Stream<TreeCursor>> byTypeMapper(TreeType childType) {
+        return child -> child.getTree().getType() == childType
+                ? Stream.of(child)
+                : Stream.empty();
+    }
+
+    static Function<TreeCursor, Stream<TreeCursor>> siblingChildrenSupplier() {
+        return cursor -> cursor.remainingSiblings().stream();
     }
 
     // Brackets children of childType between open and closed brackets. If the children can fit together
@@ -47,9 +77,7 @@ final class BracketFormatter {
             TreeType childType,
             Function<TreeCursor, Doc> visitor
     ) {
-        return extractor(visitor, child -> child.getTree().getType() == childType
-                                           ? Stream.of(child)
-                                           : Stream.empty());
+        return extractor(visitor, byTypeMapper(childType));
     }
 
     BracketFormatter open(Doc open) {
@@ -138,36 +166,4 @@ final class BracketFormatter {
         }
         return lineDoc;
     }
-
-    private static final class Extractor implements Function<TreeCursor, Stream<Doc>> {
-        private final Function<TreeCursor, Stream<TreeCursor>> mapper;
-        private final Function<TreeCursor, Doc> visitor;
-
-        private Extractor(
-                Function<TreeCursor, Doc> visitor,
-                Function<TreeCursor, Stream<TreeCursor>> mapper
-        ) {
-            this.visitor = visitor;
-            this.mapper = mapper;
-        }
-
-        @Override
-        public Stream<Doc> apply(TreeCursor cursor) {
-            SmithyBuilder.requiredState("childExtractor", mapper);
-            SmithyBuilder.requiredState("visitor", visitor);
-            return cursor.children()
-                    .flatMap(c -> {
-                        TreeType type = c.getTree().getType();
-                        return type == TreeType.WS || type == TreeType.COMMENT ? Stream.of(c) : mapper.apply(c);
-                    })
-                    .flatMap(c -> {
-                        // If the child extracts WS, then filter it down to just comments.
-                        return c.getTree().getType() == TreeType.WS
-                               ? c.getChildrenByType(TreeType.COMMENT).stream()
-                               : Stream.of(c);
-                    })
-                    .map(visitor)
-                    .filter(doc -> doc != Doc.empty());
-        }
-    }
 }
diff --git a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FixBadDocComments.java b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FixBadDocComments.java
index 7a62655cf..d4e180f6c 100644
--- a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FixBadDocComments.java
+++ b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FixBadDocComments.java
@@ -67,6 +67,11 @@ final class FixBadDocComments implements Function<TokenTree, TokenTree> {
             updateNestedChildren(cursor);
         }
 
+        // Doc comments should not appear in TRAIT_STATEMENTS.
+        for (TreeCursor cursor : shapeSection.findChildrenByType(TreeType.TRAIT_STATEMENTS)) {
+            updateNestedChildren(cursor);
+        }
+
         // Fix doc comments that come before apply statements.
         TreeCursor shapeStatements = shapeSection.getFirstChild(TreeType.SHAPE_STATEMENTS);
         if (shapeStatements != null) {
@@ -77,11 +82,15 @@ final class FixBadDocComments implements Function<TokenTree, TokenTree> {
                     updateNestedChildren(br);
                 }
             }
-
-            // Remove trailing doc comments in member bodies.
+            // Fix any trailing doc comments in shape bodies
             for (TreeCursor members : shapeStatements.findChildrenByType(TreeType.SHAPE_MEMBERS)) {
-                TreeCursor ws = members.getLastChild(TreeType.WS);
-                updateDirectChildren(ws);
+                TreeCursor closeBrace = members.getLastChild();
+                if (closeBrace != null) {
+                    TreeCursor possibleTrailingWs = closeBrace.getPreviousSibling();
+                    if (possibleTrailingWs != null && possibleTrailingWs.getTree().getType() == TreeType.WS) {
+                        updateDirectChildren(possibleTrailingWs);
+                    }
+                }
             }
         }
 
diff --git a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java
index a9b1a47f6..2f8eadfe4 100644
--- a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java
+++ b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/FormatVisitor.java
@@ -263,15 +263,29 @@ final class FormatVisitor {
             }
 
             case OPERATION_ERRORS: {
-                return skippedComments(cursor, false)
-                        .append(Doc.text("errors: "))
+                // Pull out any comments that come after 'errors' but before the opening '[' so they
+                // can be placed before 'errors: ['
+                Doc comments = Doc.empty();
+                TreeCursor child = cursor.getFirstChild(); // 'errors'
+                if (child.getNextSibling().getTree().getType() == TreeType.WS) {
+                    comments = comments.append(skippedComments(child.getNextSibling(), false));
+                    child = child.getNextSibling(); // skip ws
+                }
+                child = child.getNextSibling(); // ':'
+                if (child.getNextSibling().getTree().getType() == TreeType.WS) {
+                    comments = comments.append(skippedComments(child.getNextSibling(), false));
+                    child = child.getNextSibling();
+                }
+                return comments.append(Doc.text("errors: ")
                         .append(new BracketFormatter()
                                         .open(Formatter.LBRACKET)
                                         .close(Formatter.RBRACKET)
-                                        .extractChildren(cursor, BracketFormatter.extractByType(TreeType.SHAPE_ID,
-                                                                                                this::visit))
+                                        .extractChildren(child, BracketFormatter.extractor(
+                                                this::visit,
+                                                BracketFormatter.byTypeMapper(TreeType.SHAPE_ID),
+                                                BracketFormatter.siblingChildrenSupplier()))
                                         .forceLineBreaks() // always put each error on separate lines.
-                                        .write());
+                                        .write()));
             }
 
             case MIXINS: {
diff --git a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/TreeCursor.java b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/TreeCursor.java
index 1e2536133..3174f64eb 100644
--- a/smithy-syntax/src/main/java/software/amazon/smithy/syntax/TreeCursor.java
+++ b/smithy-syntax/src/main/java/software/amazon/smithy/syntax/TreeCursor.java
@@ -318,6 +318,19 @@ public final class TreeCursor implements FromSourceLocation {
         }
     }
 
+    /**
+     * @return All siblings after this cursor, in order.
+     */
+    public List<TreeCursor> remainingSiblings() {
+        List<TreeCursor> remaining = new ArrayList<>();
+        TreeCursor nextSibling = getNextSibling();
+        while (nextSibling != null) {
+            remaining.add(nextSibling);
+            nextSibling = nextSibling.getNextSibling();
+        }
+        return remaining;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {


Test patch:
diff --git a/smithy-syntax/src/test/java/software/amazon/smithy/syntax/ParseAndFormatTest.java b/smithy-syntax/src/test/java/software/amazon/smithy/syntax/ParseAndFormatTest.java
index 3eb26da41..e87105486 100644
--- a/smithy-syntax/src/test/java/software/amazon/smithy/syntax/ParseAndFormatTest.java
+++ b/smithy-syntax/src/test/java/software/amazon/smithy/syntax/ParseAndFormatTest.java
@@ -1,7 +1,6 @@
 package software.amazon.smithy.syntax;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.io.IOException;
 import java.nio.file.Files;
@@ -54,7 +53,7 @@ public class ParseAndFormatTest {
         String formatted = Formatter.format(tree, 120);
         String expected = IoUtils.readUtf8File(formattedFile);
 
-        assertThat(formatted, equalTo(expected));
+        assertEquals(expected, formatted);
     }
 
     public static List<Path> tests() throws Exception {
diff --git a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.formatted.smithy b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.formatted.smithy
index b6984e10c..e7e8d0d29 100644
--- a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.formatted.smithy
+++ b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.formatted.smithy
@@ -23,19 +23,24 @@ use smithy.api#String
 
 /// 12 (keep)
 @deprecated
-/// 13 (change)
+// 13 (change)
 structure Foo {
     /// 14 (keep)
     @length(
         // 15 (change)
         min: 1
     )
-    /// 16 (change)
+    // 16 (change)
     @since("1.x")
-    /// 17 (TODO: change)
+    // 17 (change)
     bar: String
 
-    // 18 (change)
+    /// 17a (keep)
+    @length(min: 1)
+    baz: String = ""
+
+    /// 18 (TODO: Fix trailing comment after VALUE_ASSIGNMENT)
+
 }
 
 // 19 (change)
@@ -45,4 +50,10 @@ apply Foo @tags(["a"])
 list Baz {
     member: Integer
 }
-// 21 (change)
+
+structure Foo2 {
+    foo2: String
+
+    // 21 (change)
+}
+// 22 (change)
diff --git a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.smithy b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.smithy
index e16e23e1c..5aeb29841 100644
--- a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.smithy
+++ b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/fixes-invalid-doc-comments.smithy
@@ -32,9 +32,14 @@ structure Foo {
     )
     /// 16 (change)
     @since("1.x")
-    /// 17 (TODO: change)
+    /// 17 (change)
     bar: String
-    /// 18 (change)
+
+    /// 17a (keep)
+    @length(min: 1)
+    baz: String = ""
+
+    /// 18 (TODO: Fix trailing comment after VALUE_ASSIGNMENT)
 }
 
 /// 19 (change)
@@ -45,4 +50,10 @@ list Baz {
     member: Integer
 }
 
-/// 21 (change)
+structure Foo2 {
+    foo2: String
+
+    /// 21 (change)
+}
+
+/// 22 (change)
diff --git a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/operation-errors-comments.formatted.smithy b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/operation-errors-comments.formatted.smithy
new file mode 100644
index 000000000..76fbcdd0c
--- /dev/null
+++ b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/operation-errors-comments.formatted.smithy
@@ -0,0 +1,27 @@
+$version: "2.0"
+
+namespace smithy.example
+
+operation Foo {
+    errors: [
+        // a
+        E1
+        // c
+        E2
+        // d
+    ]
+}
+
+operation Bar {
+    // a
+    // b
+    errors: [
+        // c
+    ]
+}
+
+@error("client")
+structure E1 {}
+
+@error("client")
+structure E2 {}
diff --git a/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/operation-errors-comments.smithy b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/operation-errors-comments.smithy
new file mode 100644
index 000000000..6b66e0448
--- /dev/null
+++ b/smithy-syntax/src/test/resources/software/amazon/smithy/syntax/formatter/operation-errors-comments.smithy
@@ -0,0 +1,27 @@
+$version: "2.0"
+
+namespace smithy.example
+
+operation Foo {
+    errors: [
+        // a
+        E1
+        // c
+        E2
+        // d
+    ]
+}
+
+operation Bar {
+    errors // a
+    : // b
+    [
+        // c
+    ]
+}
+
+@error("client")
+structure E1 {}
+
+@error("client")
+structure E2 {}