diff --git a/shared/namebinding/codeql/namebinding/LocalNameBinding.qll b/shared/namebinding/codeql/namebinding/LocalNameBinding.qll index 2dd12bbc8abc..f70ced92fc98 100644 --- a/shared/namebinding/codeql/namebinding/LocalNameBinding.qll +++ b/shared/namebinding/codeql/namebinding/LocalNameBinding.qll @@ -34,8 +34,10 @@ signature module LocalNameBindingInputSig { AstNode getChild(AstNode n, int index); /** - * A conditional where any local declarations in the condition are in scope - * in the then-branch but not the else-branch. + * A conditional with a condition, a then-branch, and an optional else-branch. + * + * Unless `isUnscopedConditional` holds, local declarations in the condition + * are in scope in the then-branch but not the else-branch. * * Example: * @@ -62,6 +64,25 @@ signature module LocalNameBindingInputSig { AstNode getElse(); } + /** + * Holds if local declarations in a branch of `cond` are in scope in the + * remainder of the branch _and_ following the conditional itself. + * + * Example: + * + * ```swift + * #if DEBUG + * let x = def1 + * // x is in scope here and refers to `def1` + * #else + * let x = def2 + * // x is in scope here and refers to `def2` + * #endif + * // x is in scope here and refers to either `def1` or `def2` + * ``` + */ + default predicate isUnscopedConditional(Conditional cond) { none() } + /** * A declaration where all local declarations in the left-hand side are in * scope _after_ the declaration, and where any sibling declarations with @@ -195,17 +216,23 @@ module LocalNameBinding */ private AstNode getChildAdj(AstNode parent, int index) { result = getChild(parent, index) and - not exists(Conditional cond | result = [cond.getElse(), cond.getThen()]) + not exists(Conditional cond | + not isUnscopedConditional(cond) and + result = [cond.getElse(), cond.getThen()] + ) or exists(Conditional cond | - parent = cond and - result = cond.getElse() and - index = -1 - or - exists(int last | - result = cond.getThen() and - last = max(int i | conditionHasChildAt(cond, parent, i)) and - index = last + 1 + not isUnscopedConditional(cond) and + ( + parent = cond and + result = cond.getElse() and + index = -1 + or + exists(int last | + result = cond.getThen() and + last = max(int i | conditionHasChildAt(cond, parent, i)) and + index = last + 1 + ) ) ) } @@ -217,31 +244,132 @@ module LocalNameBinding int getRank(C parent, Ranked child) { child = getChildAdj(parent, result) and - getChildAdj(parent, _) instanceof SiblingShadowingDecl + // Only rank children when necessary, i.e., when there are sibling + // shadowing declarations or unscoped conditionals among the children. + ( + getChildAdj(parent, _) instanceof SiblingShadowingDecl + or + isUnscopedConditional(getChildAdj(parent, _)) + ) } } private predicate getRankedChild = DenseRank1::denseRank/2; + /** Holds if `decl` is a possible final declaration introduced by `node`. */ + private predicate lastDeclAfter(AstNode node, SiblingShadowingDecl decl) { + node = decl + or + node = any(Conditional cond | isUnscopedConditional(cond) and lastDeclInBranch(cond, decl)) + } + + private predicate lastDeclInBranch(Conditional cond, SiblingShadowingDecl decl) { + exists(AstNode branch | branch = [cond.getThen(), cond.getElse()] | + // An `#elseif` is itself a conditional rather than a block of statements. + isUnscopedConditional(branch) and lastDeclAfter(branch, decl) + or + not isUnscopedConditional(branch) and + lastDeclInPrefix(branch, max(int i | exists(getRankedChild(branch, i))), decl) + ) + } + /** - * Holds if `n` is the `i`th child of `parent`, but should instead be considered - * a child of a shadowing sibling declaration `decl` when resolving accesses. + * Holds if the unscoped conditional `cond` might not introduce any shadowing + * declarations. We need this property to determine whether declarations prior + * to the conditional are still in scope following the conditional. * - * This is the case when `decl` is the nearest shadowing sibling declaration - * preceding `n` amongst all the children of `parent`. + * For instance, in + * ```swift + * let x = 1 + * #if DEBUG + * let x = 2 + * #else + * let x = 3 + * #endif + * print(x) // prints 2 or 3 + * ``` + * the initial declaration of `x` is certain to not be shadowed after the + * conditional. * - * Note that `decl` may itself also have to be nested under another shadowing - * sibling declaration. + * If the conditional instead had no else branch or had one of the branches + * not declared any shadowing declarations, then the initial declaration of + * `x` might not have been shadowed after the conditional. */ - private predicate shouldBeShadowingDeclChild( - AstNode parent, SiblingShadowingDecl decl, int i, AstNode n - ) { - n = getRankedChild(parent, i) and + private predicate unscopedConditionalPreservesIncoming(Conditional cond) { + isUnscopedConditional(cond) and ( - decl = getRankedChild(parent, i - 1) + not exists(cond.getElse()) or - shouldBeShadowingDeclChild(parent, decl, i - 1, - any(AstNode prev | not prev instanceof SiblingShadowingDecl)) + branchPreservesIncoming([cond.getThen(), cond.getElse()]) + ) + } + + private predicate branchPreservesIncoming(AstNode branch) { + unscopedConditionalPreservesIncoming(branch) + or + not isUnscopedConditional(branch) and + ( + // If the children are not ranked, then they do not contain any sibling + // shadowing declarations. + not exists(getRankedChild(branch, _)) + or + prefixPreservesIncoming(branch, max(int i | exists(getRankedChild(branch, i)))) + ) + } + + /** + * Holds if `node` might not introduce any shadowing declaration. + * + * For example, the following satisfies the predicate since the declaration + * might be removed at compile time. + * ```swift + * #if DEBUG + * let value = 1 + * #endif + * ``` + */ + private predicate preservesIncoming(AstNode node) { + // A shadowing declaration always introduces a shadowing declaration. + not node instanceof SiblingShadowingDecl and + not isUnscopedConditional(node) + or + unscopedConditionalPreservesIncoming(node) + } + + /** Summarizes the first `i` ranked children without entering ordinary nested scopes. */ + private predicate lastDeclInPrefix(AstNode parent, int i, SiblingShadowingDecl decl) { + exists(AstNode child | child = getRankedChild(parent, i) | + lastDeclAfter(child, decl) + or + preservesIncoming(child) and lastDeclInPrefix(parent, i - 1, decl) + ) + } + + /** + * Holds if the first `i` children might not introduce any shadowing + * declarations. + */ + private predicate prefixPreservesIncoming(AstNode parent, int i) { + i = 0 and exists(getRankedChild(parent, _)) + or + prefixPreservesIncoming(parent, i - 1) and + preservesIncoming(getRankedChild(parent, i)) + } + + /** + * Holds if, instead of its parent, `n` should be considered a child of a + * shadowing sibling declaration `decl` when resolving accesses. + * + * Each alternative through preceding unscoped conditionals contributes its own + * nearest shadowing declaration. + * + * Note that `decl` may itself also have to be nested under another shadowing + * sibling declaration. + */ + private predicate shouldBeShadowingDeclChild(SiblingShadowingDecl decl, AstNode n) { + exists(AstNode parent, int i | + n = getRankedChild(parent, i) and + lastDeclInPrefix(parent, i - 1, decl) ) } @@ -289,11 +417,18 @@ module LocalNameBinding * ``` */ private AstNode getParentForScoping(AstNode n) { - not shouldBeShadowingDeclChild(_, _, _, n) and not exists(SiblingShadowingDecl decl | n = [decl.getRhs(), decl.getElse()]) and - n = getChildAdj(result, _) + n = getChildAdj(result, _) and + ( + not n = getRankedChild(result, _) + or + exists(int i | + n = getRankedChild(result, i) and + prefixPreservesIncoming(result, i - 1) + ) + ) or - shouldBeShadowingDeclChild(_, result, _, n) + shouldBeShadowingDeclChild(result, n) or exists(SiblingShadowingDecl decl | result = getParentForScoping(decl) and diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index fb9d88333087..e7e2d33a7562 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -68,6 +68,8 @@ supertypes: - do_while_stmt - defer_stmt - labeled_stmt + - associated_type_declaration + - initializer_declaration callable: subtypes: - top_level @@ -93,6 +95,7 @@ supertypes: - class_like_declaration - type_alias_declaration - associated_type_declaration + - if_expr # For conditional compilation - unsupported_node - unhandled_node fields: @@ -261,7 +264,10 @@ named: block: stmt*: stmt + # An if expression or a conditional compilation directive (which we view as a + # compile-time evaluated if expression). if_expr: + modifier*: modifier condition: expr then?: expr else?: expr @@ -499,7 +505,7 @@ named: type_parameter*: type_parameter type_constraint*: type_constraint base_type*: base_type - member*: member + direct_member*: member # One of the base types of a class declaration. # diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 3cdfb0e12a99..94fc123f9171 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -38,6 +38,9 @@ struct SwiftContext { /// True while translating a pattern. Optional chaining in this context is /// represented as an `Optional.some` call rather than being unwrapped. in_pattern: bool, + /// When iterating over the clauses of a conditional compilation block, this + /// holds the tail of the clauses that have already been translated. + conditional_compilation_tail: Option, } impl SwiftContext { @@ -64,11 +67,29 @@ fn implicit_parameterless_initializer( ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>, members: &[yeast::OutputId], ) -> Option { - let has_explicit_initializer = members.iter().any(|member| { - ctx.output - .get_node(*member) - .is_some_and(|node| node.kind_name() == "constructor_declaration") - }); + // Search for an initializer while recursing into nodes for conditional + // compilation blocks. + fn find_initializer(ast: &yeast::OutputAst, id: yeast::OutputId) -> Option { + let node = ast.get_node(id)?; + match node.kind_name() { + "constructor_declaration" => Some(id), + name => { + let fields: &[&str] = match name { + "if_expr" => &["then", "else"], // look into conditional compilation + "block" => &["stmt"], // look into blocks in branches + _ => &[], + }; + fields.iter().find_map(|field| { + node.field_children(ast.field_id_for_name(field)?) + .iter() + .find_map(|child| find_initializer(ast, *child)) + }) + } + } + } + let has_explicit_initializer = members + .iter() + .any(|member| find_initializer(ctx.output, *member).is_some()); (!has_explicit_initializer).then(|| { tree!((constructor_declaration modifier: (modifier "generated") @@ -529,7 +550,7 @@ fn translation_rules() -> Vec> { modifier: [(modifier "chained_declaration") if { ctx.is_chained }] modifier: (modifier "enum_case") name_node: (identifier #{name}) - member: {constructor})) + direct_member: {constructor})) } ), rule!( @@ -872,6 +893,53 @@ fn translation_rules() -> Vec> { => (parameter pattern: (identifier #{name})) ), + // ---- Conditioal compilation blocks ---- + // Conditional compilation blocks (`#if … #elseif … #else … #endif`) are + // represented with a `ifConfigDecl` node for the entire block and an + // `ifConfigClause` for each branch. We translate them into nested + // `if_expr` nodes. + + // When a conditional compilation block contains a switch case it + // appears within a `switch` -- we do not support this yet. + rule!( + (ifConfigDecl clauses: (ifConfigClause elements: (switchCase))) + => + (unsupported_node) + ), + // A conditional compilation block is translated into `if_expr` nodes + // with branches occurring nested within the else branch. + rule!( + (ifConfigDecl clauses: _* @@clauses) @@declaration + => + expr { + ctx.conditional_compilation_tail = None; + for clause in clauses.into_iter().rev() { + ctx.conditional_compilation_tail = ctx.translate(clause)?.into_iter().next(); + } + let result = ctx.conditional_compilation_tail.ok_or("conditional compilation has no clauses")?; + ctx.set_node_source_range(result, ctx.source_range(declaration)) + } + ), + // A single clause is mapped to an if expression with the tail of + // already processed clauses (stored in `conditional_compilation_tail`) + // inserted into the else branch. + rule!( + (ifConfigClause condition: @condition elements: _* @items) + => + (if_expr + // Mark this `if_expr` as coming from conditional compilation. + modifier: (modifier "conditional_compilation") + condition: {condition} + then: (block stmt: {items}) + else: {ctx.conditional_compilation_tail}) + ), + // A conditional compilation clause without a condition (a final `#else` + // block). + rule!( + (ifConfigClause elements: _* @items) + => + (block stmt: {items}) + ), // ---- Control flow ---- // An `if`/`else` expression. Conditions are joined via `and_chain`; the // then-body and optional else-body (another block, or an `ifExpr` for an @@ -1333,8 +1401,8 @@ fn translation_rules() -> Vec> { type_constraint: {parameter_constraints} type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} - member: {members} - member: {implicit_initializer})) + direct_member: {members} + direct_member: {implicit_initializer})) } ), // Enum class declaration: same as a regular class but with an enum body. @@ -1358,7 +1426,7 @@ fn translation_rules() -> Vec> { type_constraint: {parameter_constraints} type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} - member: {members}) + direct_member: {members}) ), // A `struct` declaration, including an implicit parameter-less `init` // when no explicit initializer is present. @@ -1384,8 +1452,8 @@ fn translation_rules() -> Vec> { type_constraint: {parameter_constraints} type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} - member: {members} - member: {implicit_initializer})) + direct_member: {members} + direct_member: {implicit_initializer})) } ), // Protocol declaration @@ -1406,7 +1474,7 @@ fn translation_rules() -> Vec> { type_parameter: {params} type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} - member: {members}) + direct_member: {members}) ), // An `extension Foo.Bar { … }` is likewise a `class_like_declaration`. rule!( @@ -1422,7 +1490,7 @@ fn translation_rules() -> Vec> { modifier: {mods} extension_target: {extendedType} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} - member: {members}) + direct_member: {members}) ), // A member of a type declaration unwraps to the contained declaration. rule!((memberBlockItem decl: _* @d) => member* { d }), @@ -1504,7 +1572,6 @@ fn translation_rules() -> Vec> { rule!((copyExpr) => (unsupported_node)), rule!((discardStmt) => (unsupported_node)), rule!((fallThroughStmt) => (unsupported_node)), - rule!((ifConfigDecl) => (unsupported_node)), rule!((implicitlyUnwrappedOptionalType) => (unsupported_node)), rule!((inlineArrayType) => (unsupported_node)), rule!((keyPathExpr) => (unsupported_node)), diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-attributes.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-attributes.output new file mode 100644 index 000000000000..d2648597c7c9 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-attributes.output @@ -0,0 +1,116 @@ +#if DEBUG + @available(*, deprecated) +#endif +func f() {} + +#if DEBUG + @available(*, deprecated) +#endif +let x = 1 + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + functionDecl + attributes: + ifConfigDecl + clauses: + ifConfigClause + elements: + attribute + leftParen: ( + rightParen: ) + arguments: + availabilityArgument + trailingComma: , + argument: binaryOperator "*" + availabilityArgument + argument: deprecated + atSign: @ + attributeName: + identifierType + name: identifier "available" + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + poundEndif: #endif + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + name: identifier "f" + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + funcKeyword: func + codeBlockItem + item: + variableDecl + attributes: + ifConfigDecl + clauses: + ifConfigClause + elements: + attribute + leftParen: ( + rightParen: ) + arguments: + availabilityArgument + trailingComma: , + argument: binaryOperator "*" + availabilityArgument + argument: deprecated + atSign: @ + attributeName: + identifierType + name: identifier "available" + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + poundEndif: #endif + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "1" + pattern: + identifierPattern + identifier: identifier "x" + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩\n\n⟨stmt⟩" + stmt: + function_declaration source="#if DEBUG\n @available(*, deprecated)\n#endif\nfunc ⟨name_node⟩() ⟨body⟩" + name_node: identifier "f" source="f" + body: block "{}" source="{}" + variable_declaration source="⟨modifier⟩\n⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: + modifier "let" source="let" + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n#endif" <-- ERROR: The field variable_declaration.modifier should contain modifier, but got if_expr + modifier: modifier "conditional_compilation" source="" + condition: identifier "DEBUG" source="DEBUG" + then: + block source="⟨stmt⟩" + stmt: modifier "@available(*, deprecated)" source="@available(*, deprecated)" <-- ERROR: The field block.stmt should contain stmt, but got modifier + pattern: identifier "x" source="x" + value: int_literal "1" source="1" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-attributes.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-attributes.swift new file mode 100644 index 000000000000..00310f5ab5c1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-attributes.swift @@ -0,0 +1,9 @@ +#if DEBUG + @available(*, deprecated) +#endif +func f() {} + +#if DEBUG + @available(*, deprecated) +#endif +let x = 1 diff --git a/unified/extractor/tests/corpus/swift/types/conditional-compilation-in-class-body.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-class-body.output similarity index 71% rename from unified/extractor/tests/corpus/swift/types/conditional-compilation-in-class-body.output rename to unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-class-body.output index 4d6fdc40564a..9d2ffafbedee 100644 --- a/unified/extractor/tests/corpus/swift/types/conditional-compilation-in-class-body.output +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-class-body.output @@ -1,12 +1,9 @@ -// Conditional compilation is not yet supported: swift-syntax reports a -// structured `ifConfigDecl` (whose branches hold ordinary member items), but -// the mapping has no rule for it, so the whole block becomes one -// `unsupported_node` and its members are not extracted. +// Conditional compilation branches hold ordinary member declarations. class C { -#if DEBUG - init(x: Int) {} - deinit {} -#endif + #if DEBUG + init(x: Int) {} + deinit {} + #endif } --- @@ -81,12 +78,23 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "C" source="C" - member: - unsupported_node "#if DEBUG\n init(x: Int) {}\n deinit {}\n#endif" source="#if DEBUG\n init(x: Int) {}\n deinit {}\n#endif" - constructor_declaration source="" - modifier: modifier "generated" source="" - name_node: identifier "init" source="" - body: block source="" + direct_member: + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n #endif" + modifier: modifier "conditional_compilation" source="" + condition: identifier "DEBUG" source="DEBUG" + then: + block source="⟨stmt⟩\n ⟨stmt⟩" + stmt: + constructor_declaration source="⟨name_node⟩(⟨parameter⟩) ⟨body⟩" + name_node: identifier "init" source="init" + parameter: + parameter source="⟨external_name_node⟩⟨pattern⟩: ⟨type⟩" + external_name_node: identifier "x" source="x" + type: identifier "Int" source="Int" + pattern: identifier "x" source="x" + body: block "{}" source="{}" + destructor_declaration source="deinit ⟨body⟩" + body: block "{}" source="{}" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-class-body.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-class-body.swift new file mode 100644 index 000000000000..09e6f7852f4e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-class-body.swift @@ -0,0 +1,7 @@ +// Conditional compilation branches hold ordinary member declarations. +class C { + #if DEBUG + init(x: Int) {} + deinit {} + #endif +} diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-switch-cases.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-switch-cases.output new file mode 100644 index 000000000000..11cbfd836c30 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-switch-cases.output @@ -0,0 +1,133 @@ +func f(_ x: Int) { + switch x { + #if DEBUG + case 0: break + case 1: break + #endif + default: break + } +} + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + functionDecl + attributes: + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + expressionStmt + expression: + switchExpr + leftBrace: { + rightBrace: } + cases: + ifConfigDecl + clauses: + ifConfigClause + elements: + switchCase + label: + switchCaseLabel + colon: : + caseKeyword: case + caseItems: + switchCaseItem + pattern: + expressionPattern + expression: + integerLiteralExpr + literal: integerLiteral "0" + statements: + codeBlockItem + item: + breakStmt + breakKeyword: break + switchCase + label: + switchCaseLabel + colon: : + caseKeyword: case + caseItems: + switchCaseItem + pattern: + expressionPattern + expression: + integerLiteralExpr + literal: integerLiteral "1" + statements: + codeBlockItem + item: + breakStmt + breakKeyword: break + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + poundEndif: #endif + switchCase + label: + switchDefaultLabel + colon: : + defaultKeyword: default + statements: + codeBlockItem + item: + breakStmt + breakKeyword: break + subject: + declReferenceExpr + baseName: identifier "x" + switchKeyword: switch + name: identifier "f" + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + functionParameter + colon: : + attributes: + modifiers: + type: + identifierType + name: identifier "Int" + firstName: _ + secondName: identifier "x" + funcKeyword: func + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩" + stmt: + function_declaration source="func ⟨name_node⟩(⟨parameter⟩) ⟨body⟩" + name_node: identifier "f" source="f" + parameter: + parameter source="⟨external_name_node⟩ ⟨pattern⟩: ⟨type⟩" + external_name_node: identifier "_" source="_" + type: identifier "Int" source="Int" + pattern: identifier "x" source="x" + body: + block source="{\n ⟨stmt⟩\n}" + stmt: + switch_expr source="switch ⟨value⟩ {\n ⟨case⟩\n ⟨case⟩\n }" + value: identifier "x" source="x" + case: + unsupported_node "#if DEBUG\n case 0: break\n case 1: break\n #endif" source="#if DEBUG\n case 0: break\n case 1: break\n #endif" <-- ERROR: The field switch_expr.case should contain switch_case, but got unsupported_node + switch_case source="default: ⟨body⟩" + body: + block source="⟨stmt⟩" + stmt: break_expr "break" source="break" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-switch-cases.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-switch-cases.swift new file mode 100644 index 000000000000..ac09c5660fb9 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-in-switch-cases.swift @@ -0,0 +1,9 @@ +func f(_ x: Int) { + switch x { + #if DEBUG + case 0: break + case 1: break + #endif + default: break + } +} diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-nested.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-nested.output new file mode 100644 index 000000000000..b37d173cab97 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-nested.output @@ -0,0 +1,354 @@ +#if DEBUG + let first = 1 + let second = 2 + #if EXTRA + func f() {} + #elseif OTHER + #else + let third = 3 + #endif +#else +#endif + +protocol P { + #if DEBUG + associatedtype T + #endif +} + +func use() { + #if DEBUG + let value = 1 + #else + let value = 2 + #endif + if true { + let nested = 3 + } +} + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + ifConfigDecl + clauses: + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "1" + pattern: + identifierPattern + identifier: identifier "first" + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "2" + pattern: + identifierPattern + identifier: identifier "second" + codeBlockItem + item: + ifConfigDecl + clauses: + ifConfigClause + elements: + codeBlockItem + item: + functionDecl + attributes: + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + name: identifier "f" + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + funcKeyword: func + condition: + declReferenceExpr + baseName: identifier "EXTRA" + poundKeyword: #if + ifConfigClause + elements: + condition: + declReferenceExpr + baseName: identifier "OTHER" + poundKeyword: #elseif + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "3" + pattern: + identifierPattern + identifier: identifier "third" + poundKeyword: #else + poundEndif: #endif + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + ifConfigClause + elements: + poundKeyword: #else + poundEndif: #endif + codeBlockItem + item: + protocolDecl + attributes: + name: identifier "P" + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + memberBlockItem + decl: + ifConfigDecl + clauses: + ifConfigClause + elements: + memberBlockItem + decl: + associatedTypeDecl + attributes: + name: identifier "T" + modifiers: + associatedtypeKeyword: associatedtype + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + poundEndif: #endif + modifiers: + protocolKeyword: protocol + codeBlockItem + item: + functionDecl + attributes: + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + ifConfigDecl + clauses: + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "1" + pattern: + identifierPattern + identifier: identifier "value" + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "2" + pattern: + identifierPattern + identifier: identifier "value" + poundKeyword: #else + poundEndif: #endif + codeBlockItem + item: + expressionStmt + expression: + ifExpr + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "3" + pattern: + identifierPattern + identifier: identifier "nested" + conditions: + conditionElement + condition: + booleanLiteralExpr + literal: true + ifKeyword: if + name: identifier "use" + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + funcKeyword: func + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩\n\n⟨stmt⟩\n\n⟨stmt⟩" + stmt: + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n⟨else⟩\n#endif" + modifier: modifier "conditional_compilation" source="" + condition: identifier "DEBUG" source="DEBUG" + then: + block source="⟨stmt⟩\n ⟨stmt⟩\n ⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "first" source="first" + value: int_literal "1" source="1" + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "second" source="second" + value: int_literal "2" source="2" + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n ⟨else⟩\n #endif" + modifier: modifier "conditional_compilation" source="" + condition: identifier "EXTRA" source="EXTRA" + then: + block source="⟨stmt⟩" + stmt: + function_declaration source="func ⟨name_node⟩() ⟨body⟩" + name_node: identifier "f" source="f" + body: block "{}" source="{}" + else: + if_expr source="#elseif ⟨condition⟩\n ⟨else⟩" + modifier: modifier "conditional_compilation" source="" + condition: identifier "OTHER" source="OTHER" + then: block source="" + else: + block source="#else\n ⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "third" source="third" + value: int_literal "3" source="3" + else: block "#else" source="#else" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n}" + modifier: modifier "protocol" source="protocol" + name_node: identifier "P" source="P" + direct_member: + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n #endif" + modifier: modifier "conditional_compilation" source="" + condition: identifier "DEBUG" source="DEBUG" + then: + block source="⟨stmt⟩" + stmt: + associated_type_declaration source="associatedtype ⟨name_node⟩" + name_node: identifier "T" source="T" + function_declaration source="func ⟨name_node⟩() ⟨body⟩" + name_node: identifier "use" source="use" + body: + block source="{\n ⟨stmt⟩\n ⟨stmt⟩\n}" + stmt: + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n ⟨else⟩\n #endif" + modifier: modifier "conditional_compilation" source="" + condition: identifier "DEBUG" source="DEBUG" + then: + block source="⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "value" source="value" + value: int_literal "1" source="1" + else: + block source="#else\n ⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "value" source="value" + value: int_literal "2" source="2" + if_expr source="if ⟨condition⟩ ⟨then⟩" + condition: boolean_literal "true" source="true" + then: + block source="{\n ⟨stmt⟩\n }" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "nested" source="nested" + value: int_literal "3" source="3" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-nested.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-nested.swift new file mode 100644 index 000000000000..b65c5b16506e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-nested.swift @@ -0,0 +1,28 @@ +#if DEBUG + let first = 1 + let second = 2 + #if EXTRA + func f() {} + #elseif OTHER + #else + let third = 3 + #endif +#else +#endif + +protocol P { + #if DEBUG + associatedtype T + #endif +} + +func use() { + #if DEBUG + let value = 1 + #else + let value = 2 + #endif + if true { + let nested = 3 + } +} diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-postfix-expression.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-postfix-expression.output new file mode 100644 index 000000000000..ba310a0faffe --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-postfix-expression.output @@ -0,0 +1,89 @@ +let result = value + #if DEBUG + .foo(1) + #else + .bar("hi") + #endif + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + postfixIfConfigExpr + base: + declReferenceExpr + baseName: identifier "value" + config: + ifConfigDecl + clauses: + ifConfigClause + elements: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + labeledExpr + expression: + integerLiteralExpr + literal: integerLiteral "1" + additionalTrailingClosures: + calledExpression: + memberAccessExpr + period: . + declName: + declReferenceExpr + baseName: identifier "foo" + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + ifConfigClause + elements: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + labeledExpr + expression: + stringLiteralExpr + closingQuote: " + openingQuote: " + segments: + stringSegment + content: stringSegment "hi" + additionalTrailingClosures: + calledExpression: + memberAccessExpr + period: . + declName: + declReferenceExpr + baseName: identifier "bar" + poundKeyword: #else + poundEndif: #endif + pattern: + identifierPattern + identifier: identifier "result" + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "result" source="result" + value: unsupported_node "value\n #if DEBUG\n .foo(1)\n #else\n .bar(\"hi\")\n #endif" source="value\n #if DEBUG\n .foo(1)\n #else\n .bar(\"hi\")\n #endif" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-postfix-expression.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-postfix-expression.swift new file mode 100644 index 000000000000..a947fd60ded9 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-postfix-expression.swift @@ -0,0 +1,6 @@ +let result = value + #if DEBUG + .foo(1) + #else + .bar("hi") + #endif diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif-in-class-body.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif-in-class-body.output new file mode 100644 index 000000000000..d898dd7df693 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif-in-class-body.output @@ -0,0 +1,163 @@ +class C { + #if DEBUG + init(x: Int) {} + #elseif RELEASE + init(x: String) {} + #else + init() {} + #endif +} + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + classDecl + attributes: + name: identifier "C" + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + memberBlockItem + decl: + ifConfigDecl + clauses: + ifConfigClause + elements: + memberBlockItem + decl: + initializerDecl + attributes: + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + functionParameter + colon: : + attributes: + modifiers: + type: + identifierType + name: identifier "Int" + firstName: identifier "x" + initKeyword: init + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + ifConfigClause + elements: + memberBlockItem + decl: + initializerDecl + attributes: + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + functionParameter + colon: : + attributes: + modifiers: + type: + identifierType + name: identifier "String" + firstName: identifier "x" + initKeyword: init + condition: + declReferenceExpr + baseName: identifier "RELEASE" + poundKeyword: #elseif + ifConfigClause + elements: + memberBlockItem + decl: + initializerDecl + attributes: + body: + codeBlock + leftBrace: { + rightBrace: } + statements: + modifiers: + signature: + functionSignature + parameterClause: + functionParameterClause + leftParen: ( + rightParen: ) + parameters: + initKeyword: init + poundKeyword: #else + poundEndif: #endif + modifiers: + classKeyword: class + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩" + stmt: + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n}" + modifier: modifier "class" source="class" + name_node: identifier "C" source="C" + direct_member: + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n ⟨else⟩\n #endif" + modifier: modifier "conditional_compilation" source="" + condition: identifier "DEBUG" source="DEBUG" + then: + block source="⟨stmt⟩" + stmt: + constructor_declaration source="⟨name_node⟩(⟨parameter⟩) ⟨body⟩" + name_node: identifier "init" source="init" + parameter: + parameter source="⟨external_name_node⟩⟨pattern⟩: ⟨type⟩" + external_name_node: identifier "x" source="x" + type: identifier "Int" source="Int" + pattern: identifier "x" source="x" + body: block "{}" source="{}" + else: + if_expr source="#elseif ⟨condition⟩\n ⟨then⟩\n ⟨else⟩" + modifier: modifier "conditional_compilation" source="" + condition: identifier "RELEASE" source="RELEASE" + then: + block source="⟨stmt⟩" + stmt: + constructor_declaration source="⟨name_node⟩(⟨parameter⟩) ⟨body⟩" + name_node: identifier "init" source="init" + parameter: + parameter source="⟨external_name_node⟩⟨pattern⟩: ⟨type⟩" + external_name_node: identifier "x" source="x" + type: identifier "String" source="String" + pattern: identifier "x" source="x" + body: block "{}" source="{}" + else: + block source="#else\n ⟨stmt⟩" + stmt: + constructor_declaration source="⟨name_node⟩() ⟨body⟩" + name_node: identifier "init" source="init" + body: block "{}" source="{}" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif-in-class-body.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif-in-class-body.swift new file mode 100644 index 000000000000..8422a35d69b6 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif-in-class-body.swift @@ -0,0 +1,9 @@ +class C { + #if DEBUG + init(x: Int) {} + #elseif RELEASE + init(x: String) {} + #else + init() {} + #endif +} diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif.output new file mode 100644 index 000000000000..145be9553002 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif.output @@ -0,0 +1,119 @@ +#if DEBUG +let x = 1 +#elseif RELEASE +let x = 2 +#else +let x = 3 +#endif + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + ifConfigDecl + clauses: + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "1" + pattern: + identifierPattern + identifier: identifier "x" + condition: + declReferenceExpr + baseName: identifier "DEBUG" + poundKeyword: #if + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "2" + pattern: + identifierPattern + identifier: identifier "x" + condition: + declReferenceExpr + baseName: identifier "RELEASE" + poundKeyword: #elseif + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "3" + pattern: + identifierPattern + identifier: identifier "x" + poundKeyword: #else + poundEndif: #endif + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩" + stmt: + if_expr source="#if ⟨condition⟩\n⟨then⟩\n⟨else⟩\n#endif" + modifier: modifier "conditional_compilation" source="" + condition: identifier "DEBUG" source="DEBUG" + then: + block source="⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "x" source="x" + value: int_literal "1" source="1" + else: + if_expr source="#elseif ⟨condition⟩\n⟨then⟩\n⟨else⟩" + modifier: modifier "conditional_compilation" source="" + condition: identifier "RELEASE" source="RELEASE" + then: + block source="⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "x" source="x" + value: int_literal "2" source="2" + else: + block source="#else\n⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "x" source="x" + value: int_literal "3" source="3" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif.swift new file mode 100644 index 000000000000..f96cda7a9a6c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-elseif.swift @@ -0,0 +1,7 @@ +#if DEBUG +let x = 1 +#elseif RELEASE +let x = 2 +#else +let x = 3 +#endif diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-operators.output b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-operators.output new file mode 100644 index 000000000000..d7268f066d72 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-operators.output @@ -0,0 +1,111 @@ +#if (DEBUG && !RELEASE) || swift(<6.0) + let x = 1 +#endif + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + ifConfigDecl + clauses: + ifConfigClause + elements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + integerLiteralExpr + literal: integerLiteral "1" + pattern: + identifierPattern + identifier: identifier "x" + condition: + infixOperatorExpr + operator: + binaryOperatorExpr + operator: binaryOperator "||" + leftOperand: + tupleExpr + leftParen: ( + rightParen: ) + elements: + labeledExpr + expression: + infixOperatorExpr + operator: + binaryOperatorExpr + operator: binaryOperator "&&" + leftOperand: + declReferenceExpr + baseName: identifier "DEBUG" + rightOperand: + prefixOperatorExpr + expression: + declReferenceExpr + baseName: identifier "RELEASE" + operator: prefixOperator "!" + rightOperand: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + labeledExpr + expression: + prefixOperatorExpr + expression: + floatLiteralExpr + literal: floatLiteral "6.0" + operator: prefixOperator "<" + additionalTrailingClosures: + calledExpression: + declReferenceExpr + baseName: identifier "swift" + poundKeyword: #if + poundEndif: #endif + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩" + stmt: + if_expr source="#if ⟨condition⟩\n ⟨then⟩\n#endif" + modifier: modifier "conditional_compilation" source="" + condition: + binary_expr source="(⟨left⟩) ⟨operator⟩ ⟨right⟩" + left: + binary_expr source="⟨left⟩ ⟨operator⟩ ⟨right⟩" + left: identifier "DEBUG" source="DEBUG" + operator: infix_operator "&&" source="&&" + right: + unary_expr source="⟨operator⟩⟨operand⟩" + operand: identifier "RELEASE" source="RELEASE" + operator: prefix_operator "!" source="!" + operator: infix_operator "||" source="||" + right: + call_expr source="⟨callee⟩(⟨argument⟩)" + callee: identifier "swift" source="swift" + argument: + argument source="⟨value⟩" + value: + unary_expr source="⟨operator⟩⟨operand⟩" + operand: float_literal "6.0" source="6.0" + operator: prefix_operator "<" source="<" + then: + block source="⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "x" source="x" + value: int_literal "1" source="1" diff --git a/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-operators.swift b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-operators.swift new file mode 100644 index 000000000000..9a8eb0b5eeb6 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/conditional-compilation/conditional-compilation-with-operators.swift @@ -0,0 +1,3 @@ +#if (DEBUG && !RELEASE) || swift(<6.0) + let x = 1 +#endif diff --git a/unified/extractor/tests/corpus/swift/control-flow/discard-statement.output b/unified/extractor/tests/corpus/swift/control-flow/discard-statement.output index 50628cfc354b..0145a124b29a 100644 --- a/unified/extractor/tests/corpus/swift/control-flow/discard-statement.output +++ b/unified/extractor/tests/corpus/swift/control-flow/discard-statement.output @@ -67,13 +67,13 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨base_type⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨base_type⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "struct" source="struct" name_node: identifier "Resource" source="Resource" base_type: base_type source="⟨type⟩" type: unsupported_node "~Copyable" source="~Copyable" - member: + direct_member: function_declaration source="⟨modifier⟩ func ⟨name_node⟩() ⟨body⟩" modifier: modifier "consuming" source="consuming" name_node: identifier "close" source="close" diff --git a/unified/extractor/tests/corpus/swift/expressions/super-expression.output b/unified/extractor/tests/corpus/swift/expressions/super-expression.output index 2c7d9739e658..015d786a0bef 100644 --- a/unified/extractor/tests/corpus/swift/expressions/super-expression.output +++ b/unified/extractor/tests/corpus/swift/expressions/super-expression.output @@ -146,10 +146,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩\n\n⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Base" source="Base" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" modifier: modifier "var" source="var" pattern: identifier "value" source="value" @@ -161,13 +161,13 @@ top_level source="⟨body⟩" modifier: modifier "generated" source="" name_node: identifier "init" source="" body: block source="" - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨base_type⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨base_type⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Derived" source="Derived" base_type: base_type source="⟨type⟩" type: identifier "Base" source="Base" - member: + direct_member: function_declaration source="⟨modifier⟩ func ⟨name_node⟩() ⟨body⟩" modifier: modifier "override" source="override" name_node: identifier "update" source="update" diff --git a/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output b/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output index afc5234b2f71..746dbf56df07 100644 --- a/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output +++ b/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output @@ -83,7 +83,7 @@ top_level source="⟨body⟩" class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}" modifier: modifier "class" source="class" name_node: identifier "Foo" source="Foo" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" @@ -94,7 +94,7 @@ top_level source="⟨body⟩" type_parameter: type_parameter source="⟨name_node⟩" name_node: identifier "T" source="T" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" diff --git a/unified/extractor/tests/corpus/swift/types/class-function.output b/unified/extractor/tests/corpus/swift/types/class-function.output index 6f1bb29365f0..6159dee1cb7e 100644 --- a/unified/extractor/tests/corpus/swift/types/class-function.output +++ b/unified/extractor/tests/corpus/swift/types/class-function.output @@ -47,10 +47,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Factory" source="Factory" - member: + direct_member: function_declaration source="⟨modifier⟩ func ⟨name_node⟩() ⟨body⟩" modifier: modifier "class" source="class" name_node: identifier "make" source="make" diff --git a/unified/extractor/tests/corpus/swift/types/class-inheritance.output b/unified/extractor/tests/corpus/swift/types/class-inheritance.output index 82426d8df74d..0ad540eb2728 100644 --- a/unified/extractor/tests/corpus/swift/types/class-inheritance.output +++ b/unified/extractor/tests/corpus/swift/types/class-inheritance.output @@ -38,7 +38,7 @@ top_level source="⟨body⟩" base_type: base_type source="⟨type⟩" type: identifier "Animal" source="Animal" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" diff --git a/unified/extractor/tests/corpus/swift/types/class-with-initializer.output b/unified/extractor/tests/corpus/swift/types/class-with-initializer.output index 708558af870e..184dba7275a9 100644 --- a/unified/extractor/tests/corpus/swift/types/class-with-initializer.output +++ b/unified/extractor/tests/corpus/swift/types/class-with-initializer.output @@ -90,10 +90,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Point" source="Point" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩: ⟨type⟩" modifier: modifier "var" source="var" pattern: identifier "x" source="x" diff --git a/unified/extractor/tests/corpus/swift/types/class-with-method.output b/unified/extractor/tests/corpus/swift/types/class-with-method.output index 831912cd8437..5eb7376061ee 100644 --- a/unified/extractor/tests/corpus/swift/types/class-with-method.output +++ b/unified/extractor/tests/corpus/swift/types/class-with-method.output @@ -77,10 +77,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Counter" source="Counter" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" modifier: modifier "var" source="var" pattern: identifier "n" source="n" diff --git a/unified/extractor/tests/corpus/swift/types/class-with-multiple-base-types.output b/unified/extractor/tests/corpus/swift/types/class-with-multiple-base-types.output index 3c410fb1a45a..71bf3dff560b 100644 --- a/unified/extractor/tests/corpus/swift/types/class-with-multiple-base-types.output +++ b/unified/extractor/tests/corpus/swift/types/class-with-multiple-base-types.output @@ -45,7 +45,7 @@ top_level source="⟨body⟩" type: identifier "Control" source="Control" base_type source="⟨type⟩" type: identifier "Drawable" source="Drawable" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" diff --git a/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output b/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output index 37df537cb74f..112b04db18ff 100644 --- a/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output +++ b/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output @@ -61,10 +61,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Point" source="Point" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩: ⟨type⟩" modifier: modifier "var" source="var" pattern: identifier "x" source="x" diff --git a/unified/extractor/tests/corpus/swift/types/computed-property.output b/unified/extractor/tests/corpus/swift/types/computed-property.output index 30a496762bd1..cc574ebd152e 100644 --- a/unified/extractor/tests/corpus/swift/types/computed-property.output +++ b/unified/extractor/tests/corpus/swift/types/computed-property.output @@ -101,10 +101,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n ⟨member⟩\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n ⟨direct_member⟩\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Rect" source="Rect" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩: ⟨type⟩" modifier: modifier "var" source="var" pattern: identifier "w" source="w" diff --git a/unified/extractor/tests/corpus/swift/types/conditional-compilation-in-class-body.swift b/unified/extractor/tests/corpus/swift/types/conditional-compilation-in-class-body.swift deleted file mode 100644 index fe50583118c9..000000000000 --- a/unified/extractor/tests/corpus/swift/types/conditional-compilation-in-class-body.swift +++ /dev/null @@ -1,10 +0,0 @@ -// Conditional compilation is not yet supported: swift-syntax reports a -// structured `ifConfigDecl` (whose branches hold ordinary member items), but -// the mapping has no rule for it, so the whole block becomes one -// `unsupported_node` and its members are not extracted. -class C { -#if DEBUG - init(x: Int) {} - deinit {} -#endif -} diff --git a/unified/extractor/tests/corpus/swift/types/constructor-with-parameters.output b/unified/extractor/tests/corpus/swift/types/constructor-with-parameters.output index fe8a523b72cc..f7ef9786b099 100644 --- a/unified/extractor/tests/corpus/swift/types/constructor-with-parameters.output +++ b/unified/extractor/tests/corpus/swift/types/constructor-with-parameters.output @@ -63,10 +63,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "struct" source="struct" name_node: identifier "Size" source="Size" - member: + direct_member: constructor_declaration source="⟨name_node⟩(⟨parameter⟩, ⟨parameter⟩) ⟨body⟩" name_node: identifier "init" source="init" parameter: diff --git a/unified/extractor/tests/corpus/swift/types/empty-class.output b/unified/extractor/tests/corpus/swift/types/empty-class.output index 39b4708f49ea..f9e71abcf673 100644 --- a/unified/extractor/tests/corpus/swift/types/empty-class.output +++ b/unified/extractor/tests/corpus/swift/types/empty-class.output @@ -27,7 +27,7 @@ top_level source="⟨body⟩" class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}" modifier: modifier "class" source="class" name_node: identifier "Foo" source="Foo" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output b/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output index 655bc8e33040..675dfe9b4fb5 100644 --- a/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output +++ b/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output @@ -69,24 +69,24 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n case ⟨member⟩\n case ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n case ⟨direct_member⟩\n case ⟨direct_member⟩\n}" modifier: modifier "enum" source="enum" name_node: identifier "Shape" source="Shape" - member: - class_like_declaration source="⟨name_node⟩⟨member⟩" + direct_member: + class_like_declaration source="⟨name_node⟩⟨direct_member⟩" modifier: modifier "enum_case" source="" name_node: identifier "circle" source="circle" - member: + direct_member: constructor_declaration source="circle(⟨parameter⟩)" parameter: parameter source="⟨pattern⟩: ⟨type⟩" type: identifier "Double" source="Double" pattern: identifier "radius" source="radius" body: block source="" - class_like_declaration source="⟨name_node⟩⟨member⟩" + class_like_declaration source="⟨name_node⟩⟨direct_member⟩" modifier: modifier "enum_case" source="" name_node: identifier "square" source="square" - member: + direct_member: constructor_declaration source="square(⟨parameter⟩)" parameter: parameter source="⟨pattern⟩: ⟨type⟩" diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-cases.output b/unified/extractor/tests/corpus/swift/types/enum-with-cases.output index 1be440f832f2..f35a0ccfdf4d 100644 --- a/unified/extractor/tests/corpus/swift/types/enum-with-cases.output +++ b/unified/extractor/tests/corpus/swift/types/enum-with-cases.output @@ -65,10 +65,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n case ⟨member⟩\n case ⟨member⟩\n case ⟨member⟩\n case ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n case ⟨direct_member⟩\n case ⟨direct_member⟩\n case ⟨direct_member⟩\n case ⟨direct_member⟩\n}" modifier: modifier "enum" source="enum" name_node: identifier "Direction" source="Direction" - member: + direct_member: variable_declaration source="⟨pattern⟩" modifier: modifier "enum_case" source="" pattern: identifier "north" source="north" diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output b/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output index 5476a9e835bb..8fd4e72b67c3 100644 --- a/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output +++ b/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output @@ -44,10 +44,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n case ⟨member⟩, ⟨member⟩, ⟨member⟩, ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n case ⟨direct_member⟩, ⟨direct_member⟩, ⟨direct_member⟩, ⟨direct_member⟩\n}" modifier: modifier "enum" source="enum" name_node: identifier "Suit" source="Suit" - member: + direct_member: variable_declaration source="⟨pattern⟩" modifier: modifier "enum_case" source="" pattern: identifier "clubs" source="clubs" diff --git a/unified/extractor/tests/corpus/swift/types/extension.output b/unified/extractor/tests/corpus/swift/types/extension.output index 1f5ad4bf612b..11619895fcd8 100644 --- a/unified/extractor/tests/corpus/swift/types/extension.output +++ b/unified/extractor/tests/corpus/swift/types/extension.output @@ -68,10 +68,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨extension_target⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨extension_target⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "extension" source="extension" extension_target: identifier "Int" source="Int" - member: + direct_member: function_declaration source="func ⟨name_node⟩() -> ⟨return_type⟩ ⟨body⟩" name_node: identifier "squared" source="squared" return_type: identifier "Int" source="Int" diff --git a/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output b/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output index 6e748ceb2467..7fa4270c69d4 100644 --- a/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output +++ b/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output @@ -82,7 +82,7 @@ top_level source="⟨body⟩" equality_type_constraint source="⟨left⟩ == ⟨right⟩" left: identifier "U" source="U" right: identifier "T" source="T" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" diff --git a/unified/extractor/tests/corpus/swift/types/noncopyable-type.output b/unified/extractor/tests/corpus/swift/types/noncopyable-type.output index e2f96124a50a..8f7a30a4d26a 100644 --- a/unified/extractor/tests/corpus/swift/types/noncopyable-type.output +++ b/unified/extractor/tests/corpus/swift/types/noncopyable-type.output @@ -54,13 +54,13 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨base_type⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨base_type⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "struct" source="struct" name_node: identifier "FileHandle" source="FileHandle" base_type: base_type source="⟨type⟩" type: unsupported_node "~Copyable" source="~Copyable" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩: ⟨type⟩" modifier: modifier "let" source="let" pattern: identifier "descriptor" source="descriptor" diff --git a/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output index e4b3b0cd78b5..b7bb7fa1c082 100644 --- a/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output +++ b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output @@ -108,10 +108,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n var v: Int {\n ⟨member⟩\n ⟨member⟩\n }\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n var v: Int {\n ⟨direct_member⟩\n ⟨direct_member⟩\n }\n}" modifier: modifier "class" source="class" name_node: identifier "Box" source="Box" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" modifier: modifier "var" source="var" diff --git a/unified/extractor/tests/corpus/swift/types/protocol-declaration.output b/unified/extractor/tests/corpus/swift/types/protocol-declaration.output index d54c9b25d2f4..885b4dae5bcb 100644 --- a/unified/extractor/tests/corpus/swift/types/protocol-declaration.output +++ b/unified/extractor/tests/corpus/swift/types/protocol-declaration.output @@ -57,10 +57,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n ⟨direct_member⟩\n}" modifier: modifier "protocol" source="protocol" name_node: identifier "Drawable" source="Drawable" - member: + direct_member: function_declaration source="func ⟨name_node⟩()" name_node: identifier "draw" source="draw" body: block source="" diff --git a/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output index db8a677616f8..c337e3640874 100644 --- a/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output +++ b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output @@ -110,10 +110,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n var foo: Int { ⟨member⟩ }\n var bar: String { ⟨member⟩ ⟨member⟩ }\n var count: Int { ⟨member⟩ }\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n var foo: Int { ⟨direct_member⟩ }\n var bar: String { ⟨direct_member⟩ ⟨direct_member⟩ }\n var count: Int { ⟨direct_member⟩ }\n}" modifier: modifier "protocol" source="protocol" name_node: identifier "P" source="P" - member: + direct_member: accessor_declaration source="⟨accessor_kind⟩" modifier: modifier "var" source="var" (external) name_node: identifier "foo" source="foo" (external) diff --git a/unified/extractor/tests/corpus/swift/types/qualified-type.output b/unified/extractor/tests/corpus/swift/types/qualified-type.output index 9bf255e35fda..933e2eb15417 100644 --- a/unified/extractor/tests/corpus/swift/types/qualified-type.output +++ b/unified/extractor/tests/corpus/swift/types/qualified-type.output @@ -293,18 +293,18 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩\n\n⟨stmt⟩\n⟨stmt⟩\n\n⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n\n ⟨direct_member⟩\n}" modifier: modifier "struct" source="struct" name_node: identifier "Outer" source="Outer" - member: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n }" + direct_member: + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n }" modifier: modifier "struct" source="struct" name_node: identifier "Inner" source="Inner" - member: + direct_member: class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}" modifier: modifier "struct" source="struct" name_node: identifier "Deep" source="Deep" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" @@ -313,13 +313,13 @@ top_level source="⟨body⟩" modifier: modifier "generated" source="" name_node: identifier "init" source="" body: block source="" - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩<⟨type_parameter⟩> {\n ⟨member⟩\n ⟨member⟩\n }" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩<⟨type_parameter⟩> {\n ⟨direct_member⟩\n ⟨direct_member⟩\n }" modifier: modifier "struct" source="struct" name_node: identifier "Generic" source="Generic" type_parameter: type_parameter source="⟨name_node⟩" name_node: identifier "T" source="T" - member: + direct_member: class_like_declaration source="⟨modifier⟩ ⟨name_node⟩<⟨type_parameter⟩, ⟨type_parameter⟩> {}" modifier: modifier "struct" source="struct" name_node: identifier "Deep" source="Deep" @@ -328,7 +328,7 @@ top_level source="⟨body⟩" name_node: identifier "U" source="U" type_parameter source="⟨name_node⟩" name_node: identifier "V" source="V" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" @@ -336,7 +336,7 @@ top_level source="⟨body⟩" class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}" modifier: modifier "struct" source="struct" name_node: identifier "Plain" source="Plain" - member: + direct_member: constructor_declaration source="" modifier: modifier "generated" source="" name_node: identifier "init" source="" diff --git a/unified/extractor/tests/corpus/swift/types/static-function.output b/unified/extractor/tests/corpus/swift/types/static-function.output index c5ffb34f027e..67cfa6aeb822 100644 --- a/unified/extractor/tests/corpus/swift/types/static-function.output +++ b/unified/extractor/tests/corpus/swift/types/static-function.output @@ -47,10 +47,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "Factory" source="Factory" - member: + direct_member: function_declaration source="⟨modifier⟩ func ⟨name_node⟩() ⟨body⟩" modifier: modifier "static" source="static" name_node: identifier "make" source="make" diff --git a/unified/extractor/tests/corpus/swift/types/struct.output b/unified/extractor/tests/corpus/swift/types/struct.output index 45c841255d82..eee3dc7d80fc 100644 --- a/unified/extractor/tests/corpus/swift/types/struct.output +++ b/unified/extractor/tests/corpus/swift/types/struct.output @@ -61,10 +61,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩\n ⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩\n ⟨direct_member⟩\n}" modifier: modifier "struct" source="struct" name_node: identifier "Point" source="Point" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩: ⟨type⟩" modifier: modifier "let" source="let" pattern: identifier "x" source="x" diff --git a/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output index 12ddc3af1623..e4675b0dcd1a 100644 --- a/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output +++ b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output @@ -103,10 +103,10 @@ top_level source="⟨body⟩" body: block source="⟨stmt⟩" stmt: - class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨member⟩⟨member⟩⟨member⟩\n}" + class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n ⟨direct_member⟩⟨direct_member⟩⟨direct_member⟩\n}" modifier: modifier "class" source="class" name_node: identifier "C" source="C" - member: + direct_member: variable_declaration source="⟨modifier⟩ ⟨pattern⟩: ⟨type⟩ = ⟨value⟩ {\n willSet { print(newValue) }\n didSet { print(oldValue) }\n }" modifier: modifier "var" source="var" pattern: identifier "x" source="x" diff --git a/unified/ql/lib/codeql/unified/internal/Ast.qll b/unified/ql/lib/codeql/unified/internal/Ast.qll index 5f410460536e..94a331bd11a3 100644 --- a/unified/ql/lib/codeql/unified/internal/Ast.qll +++ b/unified/ql/lib/codeql/unified/internal/Ast.qll @@ -186,7 +186,7 @@ module Unified { } /** A class representing `associated_type_declaration` nodes. */ - class AssociatedTypeDeclaration extends @unified_associated_type_declaration, F::Member { + class AssociatedTypeDeclaration extends @unified_associated_type_declaration, F::Member, F::Stmt { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AssociatedTypeDeclaration" } @@ -414,17 +414,19 @@ module Unified { /** Gets the node corresponding to the field `base_type`. */ final F::BaseType getABaseType() { result = this.getBaseType(_) } + /** Gets the node corresponding to the field `direct_member`. */ + final F::Member getDirectMember(int i) { + unified_class_like_declaration_direct_member(this, i, result) + } + + /** Gets the node corresponding to the field `direct_member`. */ + final F::Member getADirectMember() { result = this.getDirectMember(_) } + /** Gets the node corresponding to the field `extension_target`. */ final F::Expr getExtensionTarget() { unified_class_like_declaration_extension_target(this, result) } - /** Gets the node corresponding to the field `member`. */ - final F::Member getMember(int i) { unified_class_like_declaration_member(this, i, result) } - - /** Gets the node corresponding to the field `member`. */ - final F::Member getAMember() { result = this.getMember(_) } - /** Gets the node corresponding to the field `modifier`. */ final F::Modifier getModifier(int i) { unified_class_like_declaration_modifier(this, i, result) @@ -457,8 +459,8 @@ module Unified { /** Gets a field or child node of this node. */ final override F::AstNode getAFieldOrChild() { unified_class_like_declaration_base_type(this, _, result) or + unified_class_like_declaration_direct_member(this, _, result) or unified_class_like_declaration_extension_target(this, result) or - unified_class_like_declaration_member(this, _, result) or unified_class_like_declaration_modifier(this, _, result) or unified_class_like_declaration_name_node(this, result) or unified_class_like_declaration_type_constraint(this, _, result) or @@ -848,7 +850,7 @@ module Unified { } /** A class representing `if_expr` nodes. */ - class IfExpr extends @unified_if_expr, F::Expr { + class IfExpr extends @unified_if_expr, F::Expr, F::Member { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IfExpr" } @@ -858,6 +860,12 @@ module Unified { /** Gets the node corresponding to the field `else`. */ final F::Expr getElse() { unified_if_expr_else(this, result) } + /** Gets the node corresponding to the field `modifier`. */ + final F::Modifier getModifier(int i) { unified_if_expr_modifier(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final F::Modifier getAModifier() { result = this.getModifier(_) } + /** Gets the node corresponding to the field `then`. */ final F::Expr getThen() { unified_if_expr_then(this, result) } @@ -865,6 +873,7 @@ module Unified { final override F::AstNode getAFieldOrChild() { unified_if_expr_def(this, result) or unified_if_expr_else(this, result) or + unified_if_expr_modifier(this, _, result) or unified_if_expr_then(this, result) } } @@ -915,7 +924,9 @@ module Unified { } /** A class representing `initializer_declaration` nodes. */ - class InitializerDeclaration extends @unified_initializer_declaration, F::Callable, F::Member { + class InitializerDeclaration extends @unified_initializer_declaration, F::Callable, F::Member, + F::Stmt + { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InitializerDeclaration" } @@ -1676,12 +1687,12 @@ module Unified { or result = node.(ClassLikeDeclaration).getBaseType(i) and name = "getBaseType" or + result = node.(ClassLikeDeclaration).getDirectMember(i) and name = "getDirectMember" + or result = node.(ClassLikeDeclaration).getExtensionTarget() and i = -1 and name = "getExtensionTarget" or - result = node.(ClassLikeDeclaration).getMember(i) and name = "getMember" - or result = node.(ClassLikeDeclaration).getModifier(i) and name = "getModifier" or result = node.(ClassLikeDeclaration).getNameNode() and i = -1 and name = "getNameNode" @@ -1772,6 +1783,8 @@ module Unified { or result = node.(IfExpr).getElse() and i = -1 and name = "getElse" or + result = node.(IfExpr).getModifier(i) and name = "getModifier" + or result = node.(IfExpr).getThen() and i = -1 and name = "getThen" or result = node.(ImportDeclaration).getImportedExpr() and i = -1 and name = "getImportedExpr" diff --git a/unified/ql/lib/codeql/unified/internal/AstExtra.qll b/unified/ql/lib/codeql/unified/internal/AstExtra.qll index f2182698f6d0..12b216754bdf 100644 --- a/unified/ql/lib/codeql/unified/internal/AstExtra.qll +++ b/unified/ql/lib/codeql/unified/internal/AstExtra.qll @@ -94,7 +94,7 @@ module Public { /** A `Stmt` at the top-level. */ final class TopLevelStmt extends Stmt { - TopLevelStmt() { this = any(TopLevel t).getBody().getAStmt() } + TopLevelStmt() { this = any(TopLevel t).getBody().getASemanticStmt() } } /** An identifier appearing in the context of a break/continue label, argument/parameter name, or name of a member lookup. */ diff --git a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll index 8d40d3f6b099..648533c70ccd 100644 --- a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll +++ b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll @@ -99,6 +99,9 @@ module Unified { /** A block statement. */ class Block extends G::Block { + /** Gets a statement, looking through conditional-compilation branches. */ + Stmt getASemanticStmt() { result = compilationExpandedStmt(this.getAStmt()) } + /** Gets the last statement in this block. */ Stmt getLastStmt() { exists(int i | result = this.getStmt(i) and not exists(this.getStmt(i + 1))) @@ -236,6 +239,24 @@ module Unified { QualifiedName::getQualifiedName/1; class ClassLikeDeclaration extends G::ClassLikeDeclaration { + /** Gets a member, looking through conditional-compilation branches. */ + Member getAMember() { result = compilationExpandedStmt(this.getADirectMember()) } + + /** + * Gets the `i`th member in source order across all conditional-compilation alternatives, + * indexed from zero. + */ + Member getMember(int i) { + result = + rank[i + 1](Member m | + m = this.getAMember() + | + m + order by + m.getLocation().getStartLine(), m.getLocation().getStartColumn(), m.getParentIndex() + ) + } + /** Gets the name of this declaration. */ string getName() { result = this.getNameNode().getValue() } @@ -470,7 +491,24 @@ module Unified { /** An `if` statement or conditional expression. */ class IfExpr extends G::IfExpr { + /** Holds if this expression represents conditional compilation. */ + predicate isConditionalCompilation() { this.hasModifier("conditional_compilation") } + /** Gets then `then` or `else` branch. */ Expr getABranch() { result = [this.getThen(), this.getElse()] } } + + private Stmt compilationExpandedStmt(Stmt stmt) { + not stmt.(IfExpr).isConditionalCompilation() and + result = stmt + or + stmt = + any(IfExpr conditional | + conditional.isConditionalCompilation() and + result = + compilationExpandedStmt([ + conditional.getABranch().(Block).getAStmt(), conditional.getABranch().(IfExpr) + ]) + ) + } } diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index 9db28d702497..d751a6011d9a 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -129,6 +129,8 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig a -> Argument -V kpGet_mayB_x -^ a(...)' | | 477 | cfg.swift:477:1:504:1 | func testIfConfig | 'func testIfConfig -V testIfConfig' | | 477 | cfg.swift:477:21:504:1 | Block | 'Block' | -| 478 | cfg.swift:478:1:484:6 | | '' | +| 478 | cfg.swift:478:1:484:6 | IfExpr | 'IfExpr -V FOO' | +| 479 | cfg.swift:479:3:480:3 | Block | 'Block -V 1' | +| 480 | cfg.swift:480:3:480:3 | 2 | '2' | +| 481 | cfg.swift:481:1:483:3 | Block | 'Block' | +| 482 | cfg.swift:482:3:482:3 | 3 | '3' | +| 483 | cfg.swift:483:3:483:3 | 4 | '4' | | 486 | cfg.swift:486:3:486:3 | 5 | '5' | -| 488 | cfg.swift:488:1:491:6 | | '' | +| 488 | cfg.swift:488:1:491:6 | IfExpr | 'IfExpr -V BAR' | +| 489 | cfg.swift:489:3:490:3 | Block | 'Block -V 6' | +| 490 | cfg.swift:490:3:490:3 | 7 | '7' | | 493 | cfg.swift:493:3:493:3 | 8 | '8' | -| 495 | cfg.swift:495:1:501:6 | | '' | +| 495 | cfg.swift:495:1:501:6 | IfExpr | 'IfExpr -V FOO' | +| 496 | cfg.swift:496:3:497:4 | Block | 'Block -V 9' | +| 497 | cfg.swift:497:3:497:4 | 10 | '10' | +| 498 | cfg.swift:498:1:500:4 | IfExpr | 'IfExpr -V true' | +| 499 | cfg.swift:499:3:500:4 | Block | 'Block -V 11' | +| 500 | cfg.swift:500:3:500:4 | 12 | '12' | | 503 | cfg.swift:503:3:503:4 | 13 | '13' | | 506 | cfg.swift:506:1:531:1 | func testAvailable | 'func testAvailable -V testAvailable' | | 506 | cfg.swift:506:29:531:1 | Block | 'Block' | diff --git a/unified/ql/test/library-tests/controlflow/cfg.expected b/unified/ql/test/library-tests/controlflow/cfg.expected index a2f876ffabff..1de217988ce3 100644 --- a/unified/ql/test/library-tests/controlflow/cfg.expected +++ b/unified/ql/test/library-tests/controlflow/cfg.expected @@ -114,6 +114,17 @@ bbStep | cfg.swift:369:9:369:14 | ... < ... | '... < ... : true -> Block(+0)' | | cfg.swift:370:5:370:10 | ... += ... | '... += ... : successor -> x(-1)' | | cfg.swift:407:9:407:24 | mightThrow(...) | 'mightThrow(...) : successor -> try ...(+0)' | +| cfg.swift:478:5:478:7 | FOO | 'FOO : false -> Block(+3)' | +| cfg.swift:478:5:478:7 | FOO | 'FOO : true -> Block(+1)' | +| cfg.swift:480:3:480:3 | 2 | '2 : successor -> 5(+6)' | +| cfg.swift:483:3:483:3 | 4 | '4 : successor -> 5(+3)' | +| cfg.swift:488:5:488:7 | BAR | 'BAR : false -> 8(+5)' | +| cfg.swift:488:5:488:7 | BAR | 'BAR : true -> Block(+1)' | +| cfg.swift:490:3:490:3 | 7 | '7 : successor -> 8(+3)' | +| cfg.swift:495:5:495:7 | FOO | 'FOO : false -> IfExpr(+3)' | +| cfg.swift:495:5:495:7 | FOO | 'FOO : true -> Block(+1)' | +| cfg.swift:497:3:497:4 | 10 | '10 : successor -> 13(+6)' | +| cfg.swift:500:3:500:4 | 12 | '12 : successor -> 13(+3)' | | cfg.swift:509:6:509:28 | | ' : false -> IfExpr(+4)' | | cfg.swift:509:6:509:28 | | ' : true -> Block(+0)' | | cfg.swift:510:5:510:10 | ... += ... | '... += ... : successor -> IfExpr(+3)' | diff --git a/unified/ql/test/library-tests/controlflow/cfg.swift b/unified/ql/test/library-tests/controlflow/cfg.swift index cbd54f5bdc4f..2bba0441a4bf 100644 --- a/unified/ql/test/library-tests/controlflow/cfg.swift +++ b/unified/ql/test/library-tests/controlflow/cfg.swift @@ -475,29 +475,29 @@ func test(a : A) { } func testIfConfig() { -#if FOO +#if FOO // $ bbStep='FOO : false -> Block(+3)' bbStep='FOO : true -> Block(+1)' 1 - 2 + 2 // $ bbStep='2 : successor -> 5(+6)' #else 3 - 4 + 4 // $ bbStep='4 : successor -> 5(+3)' #endif 5 -#if BAR +#if BAR // $ bbStep='BAR : false -> 8(+5)' bbStep='BAR : true -> Block(+1)' 6 - 7 + 7 // $ bbStep='7 : successor -> 8(+3)' #endif 8 -#if FOO +#if FOO // $ bbStep='FOO : false -> IfExpr(+3)' bbStep='FOO : true -> Block(+1)' 9 - 10 + 10 // $ bbStep='10 : successor -> 13(+6)' #elseif true 11 - 12 + 12 // $ bbStep='12 : successor -> 13(+3)' #endif 13 diff --git a/unified/ql/test/library-tests/local-name-binding/conditional-compilation.swift b/unified/ql/test/library-tests/local-name-binding/conditional-compilation.swift new file mode 100644 index 000000000000..57ebf09d7aef --- /dev/null +++ b/unified/ql/test/library-tests/local-name-binding/conditional-compilation.swift @@ -0,0 +1,108 @@ +func conditionalIfElse() { + // The same variable is declared in _two_ branches. + #if DEBUG + let inCond = 1 // name=debugValue + #else + let inCond = 2 // name=releaseValue + print("release") + #endif + print(inCond) // $ access=debugValue access=releaseValue +} + +func conditionalIfElseifElse() { + // The same variable is declared in _three_ branches. + #if DEBUG + let alternative = 1 // name=debugValue + #elseif RELEASE + let alternative = 2 // name=releaseValue + print(alternative) // $ access=releaseValue + #else + let alternative = 3 // name=fallbackValue + print(alternative) // $ access=fallbackValue + #endif + print(alternative) // $ access=debugValue access=releaseValue access=fallbackValue +} + +func conditionalNoElse(value: Int) { // name=outerValue + // The variable is declared only in one skipable branch. + #if DEBUG + let value = value + 1 // $ access=outerValue // name=conditionalValue + print(value) // $ access=conditionalValue + #endif + print(value) // $ access=outerValue access=conditionalValue +} + +func conditionalIfNesting(nesting: Int) { // name=nestingFirst + // Nested conditional compilation. + #if DEBUG + #if EXTRA + print(nesting) // $ access=nestingFirst + let nesting = nesting + 1 // $ access=nestingFirst // name=nestingExtra + #else + let nesting = 2 // name=nestingNotExtra + #endif + print(nesting) // $ access=nestingExtra access=nestingNotExtra + #else + let nesting = 3 // name=outerNotDebug + #endif + print(nesting) // $ access=nestingExtra access=nestingNotExtra access=outerNotDebug +} + +func conditionalRealScopes() { + // Conditional compilation, but variables are declared in a real scope. + let value = 0 // name=realOuterValue + #if DEBUG + if true { + let value = 1 // name=runtimeValue + print(value) // $ access=runtimeValue + } + #endif + print(value) // $ access=realOuterValue +} + +func conditionalGuard(opt: Int?) { // name=guardOpt + #if DEBUG + guard let value = opt else { return } // $ access=guardOpt // name=guardValue + print(value) // $ access=guardValue + #else + guard let value = opt else { return } // $ access=guardOpt // name=fallbackGuardValue + #endif + print(value) // $ access=guardValue access=fallbackGuardValue +} + +func conditionalSequentialBindings(value: Int) { // name=sequentialOuter + #if DEBUG + print(value) // $ access=sequentialOuter + let first = 1 // name=sequentialFirst + print(first) // $ access=sequentialFirst + let value = first + 1 // $ access=sequentialFirst // name=sequentialSecond + print(value) // $ access=sequentialSecond + #else + print(value) // $ access=sequentialOuter + #endif + print(value) // $ access=sequentialSecond access=sequentialOuter +} + +func conditionalCapture() { + #if DEBUG + let value = 1 // $ captured=capturedDebug // name=capturedDebug + #else + let value = 2 // $ captured=capturedElse // name=capturedElse + #endif + let closure = { // name=conditionalClosure + print(value) // $ access=capturedDebug access=capturedElse + } + closure() // $ access=conditionalClosure +} + +func conditionalNestedFallback(value: Int) { // name=nestedFallbackOuter + #if DEBUG + #if EXTRA + let value = 1 // name=nestedFallbackExtra + #else + #endif + #else + print(value) // $ access=nestedFallbackOuter + #endif + print(value) // $ access=nestedFallbackOuter access=nestedFallbackExtra +}