diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index c1e49181ea13..9185c541e454 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -297,14 +297,14 @@ fn translation_rules() -> Vec> { rule!((sequenceExpr elements: _* @els) => (unresolved_operator_sequence element: {els})), // Prefix unary operators (`!a`, `-x`). rule!((prefixOperatorExpr operator: @op expression: @operand) => (unary_expr operator: (prefix_operator #{op}) operand: {operand})), - // A `tupleExpr` is a tuple literal (`(a, b)`) or a parenthesised - // expression (`(x)`). For now it is kept as an opaque `tuple_expr` leaf - // (its source text); its elements are not descended into. - // - // TODO: a parenthesised single-element `tupleExpr` is really a grouping - // expression and should be elided (unwrapped to its inner expression) - // rather than modelled as a tuple. - rule!((tupleExpr) => (tuple_expr)), + // A parenthesised expression has a single tuple element; elide the + // grouping and preserve the expression itself. Actual tuple literals + // retain their translated labeled elements as `argument` children. + rule!((tupleExpr + elements: (labeledExpr label: _? @@lbl expression: @element) + elements: _* @@rest) + where rest.is_empty() && lbl.is_none() => expr { element }), + rule!((tupleExpr elements: _* @elements) => (tuple_expr element: {elements})), // A code block contains its statements directly. rule!((codeBlock statements: _* @stmts) => (block stmt: {stmts})), // ---- Properties with accessors ---- diff --git a/unified/extractor/tests/corpus/swift/collections/tuple-literal.output b/unified/extractor/tests/corpus/swift/collections/tuple-literal.output index 5d7742f744bc..4a8a54078d9c 100644 --- a/unified/extractor/tests/corpus/swift/collections/tuple-literal.output +++ b/unified/extractor/tests/corpus/swift/collections/tuple-literal.output @@ -52,4 +52,12 @@ top_level variable_declaration modifier: modifier "let" pattern: identifier "t" - value: tuple_expr "(1, \"two\", 3.0)" + value: + tuple_expr + element: + argument + value: int_literal "1" + argument + value: string_literal "\"two\"" + argument + value: float_literal "3.0" diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-expression-pattern.output b/unified/extractor/tests/corpus/swift/control-flow/switch-expression-pattern.output index 0a4fe7302b05..533f717b827e 100644 --- a/unified/extractor/tests/corpus/swift/control-flow/switch-expression-pattern.output +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-expression-pattern.output @@ -333,7 +333,12 @@ top_level member_access_expr base: inferred_type_expr "." member_name_node: identifier "inferred" - tuple_expr "(value, offset)" + tuple_expr + element: + argument + value: identifier "value" + argument + value: identifier "offset" array_literal element: identifier "value" map_literal "[key: value]" diff --git a/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output b/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output index 1c8de726f41a..33bb1599ff1a 100644 --- a/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output +++ b/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output @@ -39,6 +39,10 @@ top_level block stmt: binary_expr - left: tuple_expr "(a + b)" + left: + binary_expr + left: identifier "a" + operator: infix_operator "+" + right: identifier "b" operator: infix_operator "*" right: identifier "c" diff --git a/unified/ql/test/library-tests/BasicTest/test.expected b/unified/ql/test/library-tests/BasicTest/test.expected index 711b05c12d4c..5f8e2a323ad2 100644 --- a/unified/ql/test/library-tests/BasicTest/test.expected +++ b/unified/ql/test/library-tests/BasicTest/test.expected @@ -129,6 +129,7 @@ identifier | test.swift:74:10:74:17 | isSorted | isSorted | | test.swift:74:24:74:27 | Bool | Bool | | test.swift:75:13:75:13 | i | i | +| test.swift:75:23:75:27 | count | count | | test.swift:76:16:76:19 | self | self | | test.swift:76:21:76:21 | i | i | | test.swift:76:26:76:29 | self | self | @@ -152,6 +153,9 @@ identifier | test.swift:87:31:87:36 | reduce | reduce | | test.swift:87:38:87:43 | values | values | | test.swift:87:49:87:57 | transform | transform | +| test.swift:90:5:90:9 | tuple | tuple | +| test.swift:91:5:91:10 | unary1 | unary1 | +| test.swift:92:5:92:10 | unary2 | unary2 | namedPattern | test.swift:1:1:1:17 | NamedPattern | Foundation | unsupported @@ -163,6 +167,9 @@ rawStringValue | strings.swift:3:28:3:33 | world | world | | strings.swift:4:11:4:16 | hello | hello | | strings.swift:4:27:4:32 | world | world | +| test.swift:90:17:90:23 | "hello" | "hello" | +| test.swift:91:15:91:29 | "parenthesized" | "parenthesized" | +| test.swift:92:16:92:37 | "double-parenthesized" | "double-parenthesized" | exprStringValue | strings.swift:1:10:1:16 | "hello" | hello | | strings.swift:2:11:2:16 | hello | hello | @@ -171,3 +178,7 @@ exprStringValue | strings.swift:3:28:3:33 | world | world | | strings.swift:4:11:4:16 | hello | hello | | strings.swift:4:27:4:32 | world | world | +| test.swift:90:17:90:23 | "hello" | hello | +| test.swift:91:15:91:29 | "parenthesized" | parenthesized | +| test.swift:92:16:92:37 | "double-parenthesized" | double-parenthesized | +unexpectedUnaryTuple diff --git a/unified/ql/test/library-tests/BasicTest/test.ql b/unified/ql/test/library-tests/BasicTest/test.ql index 62f5ac850e6e..fa3c26183b08 100644 --- a/unified/ql/test/library-tests/BasicTest/test.ql +++ b/unified/ql/test/library-tests/BasicTest/test.ql @@ -9,3 +9,5 @@ query predicate unsupported(UnsupportedNode node, string value) { value = node.g query predicate rawStringValue(StringLiteral e, string value) { value = e.getValue() } query predicate exprStringValue(Expr e, string value) { value = e.getStringValue() } + +query predicate unexpectedUnaryTuple(TupleExpr tuple) { count(tuple.getAnElement()) = 1 } diff --git a/unified/ql/test/library-tests/BasicTest/test.swift b/unified/ql/test/library-tests/BasicTest/test.swift index 158ef26f598b..77e95074b383 100644 --- a/unified/ql/test/library-tests/BasicTest/test.swift +++ b/unified/ql/test/library-tests/BasicTest/test.swift @@ -86,3 +86,7 @@ func combine(_ values: [T], transform: (T, T) -> T) -> T? { guard !values.isEmpty else { return nil } return values.dropFirst().reduce(values[0], transform) } + +let tuple = (1, "hello", true) +let unary1 = ("parenthesized") +let unary2 = (("double-parenthesized"))