private static ObjectNode parseStructuredTrait(IdlModelLoader loader, StringNode firstKey) {
        IdlInternalTokenizer tokenizer = loader.getTokenizer();
        loader.increaseNestingLevel();
        Map<StringNode, Node> entries = new LinkedHashMap<>();
        Node firstValue = IdlNodeParser.expectAndSkipNode(loader);

        // This put call can be done safely without checking for duplicates,
        // as it's always the first member of the trait.
        entries.put(firstKey, firstValue);
        tokenizer.skipWsAndDocs();

        while (tokenizer.getCurrentToken() != IdlToken.RPAREN) {
            tokenizer.expect(IdlToken.IDENTIFIER, IdlToken.STRING);
            String key = loader.internString(tokenizer.getCurrentTokenStringSlice());
            StringNode keyNode = new StringNode(key, tokenizer.getCurrentTokenLocation());
            tokenizer.next();
            tokenizer.skipWsAndDocs();
            tokenizer.expect(IdlToken.COLON);
            tokenizer.next();
            tokenizer.skipWsAndDocs();
            Node nextValue = IdlNodeParser.expectAndSkipNode(loader);
            Node previous = entries.put(keyNode, nextValue);
            if (previous != null) {
                throw new ModelSyntaxException("Duplicate member of trait: '" + keyNode.getValue() + '\'', keyNode);
            }
            tokenizer.skipWsAndDocs();
        }

        loader.decreaseNestingLevel();
        return new ObjectNode(entries, firstKey.getSourceLocation());
    }
Parses a single trait: "@" trait-id [(trait-body)].

@param loader   IDL parser.
@return Returns the parsed trait.
smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlTraitParser.java