Repository: smithy-lang/smithy
Pull request #1947 (2023-08-24 18:25:09)

Catch exceptions when creating traits
Some traits like the http trait will throw exceptions that are not SourceExceptions. This previously would cause the CLI to stop on the first exception and not show pretty error output. This fixes that by catching RuntimeExceptions to turn them into ValidationEvents.

*Issue #, if available:*

*Description of changes:*


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/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderTraitMap.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderTraitMap.java
index 132d1661b..1e15e276c 100644
--- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderTraitMap.java
+++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/LoaderTraitMap.java
@@ -16,6 +16,8 @@
 package software.amazon.smithy.model.loader;
 
 import static java.lang.String.format;
+import static software.amazon.smithy.model.validation.Severity.ERROR;
+import static software.amazon.smithy.model.validation.Validator.MODEL_ERROR;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -107,6 +109,17 @@ final class LoaderTraitMap {
             String message = format("Error creating trait `%s`: ", Trait.getIdiomaticTraitName(traitId));
             events.add(ValidationEvent.fromSourceException(e, message, target));
             return null;
+        } catch (RuntimeException e) {
+            events.add(ValidationEvent.builder()
+                    .id(MODEL_ERROR)
+                    .severity(ERROR)
+                    .shapeId(target)
+                    .sourceLocation(traitValue)
+                    .message(format("Error creating trait `%s`: %s",
+                                    Trait.getIdiomaticTraitName(traitId),
+                                    e.getMessage()))
+                    .build());
+            return null;
         }
     }
 


Test patch:
diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/loader/ModelAssemblerTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/loader/ModelAssemblerTest.java
index c50afdaaa..6f3950fc0 100644
--- a/smithy-model/src/test/java/software/amazon/smithy/model/loader/ModelAssemblerTest.java
+++ b/smithy-model/src/test/java/software/amazon/smithy/model/loader/ModelAssemblerTest.java
@@ -1317,4 +1317,28 @@ public class ModelAssemblerTest {
         assertThat(result.getValidationEvents(Severity.ERROR), hasSize(1));
         assertThat(events, equalTo(result.getValidationEvents()));
     }
+
+    @Test
+    public void exceptionsThrownWhenCreatingTraitsDontCrashSmithy() {
+        String document = "{\n"
+                          + "\"smithy\": \"" + Model.MODEL_VERSION + "\",\n"
+                          + "    \"shapes\": {\n"
+                          + "        \"ns.foo#Test\": {\n"
+                          + "            \"type\": \"string\",\n"
+                          + "            \"traits\": {\"smithy.foo#baz\": true}\n"
+                          + "        }\n"
+                          + "    }\n"
+                          + "}";
+        ValidatedResult<Model> result = new ModelAssembler()
+                .addUnparsedModel(SourceLocation.NONE.getFilename(), document)
+                .putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true)
+                .traitFactory((traitId, target, value) -> {
+                    throw new RuntimeException("Oops!");
+                })
+                .assemble();
+
+        assertThat(result.getValidationEvents(Severity.ERROR), not(empty()));
+        assertThat(result.getValidationEvents(Severity.ERROR).get(0).getMessage(),
+                   equalTo("Error creating trait `smithy.foo#baz`: Oops!"));
+    }
 }