Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions unified/extractor/src/languages/swift/swift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,19 +1065,51 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
// A nominal type's `inheritanceClause` (`: Base, Proto`) becomes a list
// of `base_type`s, one per inherited type. Each declaration keyword
// gets its own rule; the bodies are identical but for the keyword.
rule!(
(genericParameter
attributes: _* @attrs
specifier: _? @@spec
name: @@name
inheritedType: _? @bound)
Comment thread
asgerf marked this conversation as resolved.
=>
(type_parameter
modifier: {attrs}
modifier: (modifier #{spec})?
name: (identifier #{name})
bound: {bound})
),
rule!(
(genericRequirement
requirement: (conformanceRequirement leftType: @ty rightType: @bound))
=>
(bound_type_constraint type: {ty} bound: {bound})
),
rule!(
(genericRequirement
requirement: (sameTypeRequirement leftType: @left rightType: @right))
=>
(equality_type_constraint left: {left} right: {right})
),
// Class declaration with body containing members
rule!(
(classDecl
classKeyword: @kind
modifiers: _* @mods
name: @name
genericParameterClause: (genericParameterClause
parameters: _* @params
genericWhereClause: (genericWhereClause requirements: _* @parameter_constraints)?)?
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
type_parameter: {params}
type_constraint: {parameter_constraints}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
Expand All @@ -1087,13 +1119,20 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
enumKeyword: @kind
modifiers: _* @mods
name: @name
genericParameterClause: (genericParameterClause
parameters: _* @params
genericWhereClause: (genericWhereClause requirements: _* @parameter_constraints)?)?
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
type_parameter: {params}
type_constraint: {parameter_constraints}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
Expand All @@ -1103,13 +1142,20 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
structKeyword: @kind
modifiers: _* @mods
name: @name
genericParameterClause: (genericParameterClause
parameters: _* @params
genericWhereClause: (genericWhereClause requirements: _* @parameter_constraints)?)?
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
type_parameter: {params}
type_constraint: {parameter_constraints}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
Expand All @@ -1119,13 +1165,17 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
protocolKeyword: @kind
modifiers: _* @mods
name: @name
genericParameterClause: (genericParameterClause parameters: _* @params)?
inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)?
genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name: (identifier #{name})
type_parameter: {params}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
class Box<T: Equatable, U> where U: Equatable, U == T {
}

---

sourceFile
endOfFileToken: endOfFile
statements:
codeBlockItem
item:
classDecl
attributes:
name: identifier "Box"
genericParameterClause:
genericParameterClause
parameters:
genericParameter
colon: :
attributes:
name: identifier "T"
trailingComma: ,
inheritedType:
identifierType
name: identifier "Equatable"
genericParameter
attributes:
name: identifier "U"
leftAngle: <
rightAngle: >
genericWhereClause:
genericWhereClause
requirements:
genericRequirement
trailingComma: ,
requirement:
conformanceRequirement
colon: :
leftType:
identifierType
name: identifier "U"
rightType:
identifierType
name: identifier "Equatable"
genericRequirement
requirement:
sameTypeRequirement
equal: binaryOperator "=="
leftType:
identifierType
name: identifier "U"
rightType:
identifierType
name: identifier "T"
whereKeyword: where
memberBlock:
memberBlock
leftBrace: {
rightBrace: }
members:
modifiers:
classKeyword: class

---

top_level
body:
block
stmt:
class_like_declaration
modifier: modifier "class"
name: identifier "Box"
type_parameter:
type_parameter
name: identifier "T"
bound:
named_type_expr
name: identifier "Equatable"
type_parameter
name: identifier "U"
type_constraint:
bound_type_constraint
type:
named_type_expr
name: identifier "U"
bound:
named_type_expr
name: identifier "Equatable"
equality_type_constraint
left:
named_type_expr
name: identifier "U"
right:
named_type_expr
name: identifier "T"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class Box<T: Equatable, U> where U: Equatable, U == T {
}
5 changes: 5 additions & 0 deletions unified/ql/lib/codeql/unified/internal/AstExtra.qll
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ module Public {
result = this.getValue().regexpCapture("(?s)/\\*(.*)\\*/", 1)
}
}

/** A `Stmt` at the top-level. */
Comment thread
hvitved marked this conversation as resolved.
final class TopLevelStmt extends Stmt {
TopLevelStmt() { this = any(TopLevel t).getBody().getAStmt() }
}
}
60 changes: 52 additions & 8 deletions unified/ql/lib/codeql/unified/internal/Variables.qll
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
}

abstract class SiblingShadowingDecl extends AstNode {
abstract AstNode getPattern();

/**
* Gets the right-hand side of this declaration.
*
Expand All @@ -149,26 +151,38 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio

private class LocalVariableDeclarationSiblingShadowingDecl extends SiblingShadowingDecl instanceof LocalVariableDeclaration
{
LocalVariableDeclarationSiblingShadowingDecl() { not this instanceof TopLevelStmt }

override Pattern getPattern() { result = LocalVariableDeclaration.super.getPattern() }

override AstNode getRhs() { result = LocalVariableDeclaration.super.getValue() }

override AstNode getElse() { none() }
}

private class PatternGuardExprSiblingShadowingDecl extends SiblingShadowingDecl instanceof PatternGuardExpr
{
override Pattern getPattern() { result = PatternGuardExpr.super.getPattern() }

override AstNode getRhs() { result = PatternGuardExpr.super.getValue() }

override AstNode getElse() { none() }
}

private predicate bindingContext(AstNode pattern, AstNode scope) {
exists(LocalVariableDeclaration decl |
scope = decl and // LocalVariableDeclaration is a ShadowingSiblingDecl, it must use itself as the scope
exists(SiblingShadowingDecl decl |
scope = decl and
pattern = decl.getPattern()
)
or
exists(LocalFunctionDeclaration func |
scope = func.getDeclaringBlock() and
exists(VariableDeclaration decl |
not decl instanceof SiblingShadowingDecl and
getChild(scope, _) = decl and
pattern = decl.getPattern()
)
or
exists(FunctionDeclaration func |
getChild(scope, _) = func and
pattern = func.getName()
)
or
Expand All @@ -192,12 +206,37 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
pattern = stmt.getPattern()
)
or
bindingContext(pattern.(Pattern).getEnclosingPattern(), scope)
exists(ClassLikeDeclaration cls |
getChild(scope, _) = cls and
pattern = cls.getName()
)
or
exists(TypeAliasDeclaration decl |
getChild(scope, _) = decl and
pattern = decl.getName()
)
or
exists(PatternGuardExpr expr |
pattern = expr.getPattern() and
scope = expr
exists(TypeParameter param |
scope = param.getParent() and
pattern = param.getName()
)
or
exists(AssociatedTypeDeclaration decl |
getChild(scope, _) = decl and
pattern = decl.getName()
)
or
exists(AccessorDeclaration decl |
getChild(scope, _) = decl and
pattern = decl.getName()
)
or
exists(ImportDeclaration imprt |
getChild(scope, _) = imprt and
pattern = imprt.getPattern()
)
or
bindingContext(pattern.(Pattern).getEnclosingPattern(), scope)
}

/**
Expand Down Expand Up @@ -243,6 +282,11 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
or
n.(NamePattern).getIdentifier().getValue() = name
or
exists(NamedTypeExpr expr | n = expr |
not exists(expr.getQualifier()) and
expr.getName().getValue() = name
)
or
n = any(LocalFunctionDeclaration f).getName() and
n.(Identifier).getValue() = name
}
Expand Down
73 changes: 73 additions & 0 deletions unified/ql/test/library-tests/variables/class_scope.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
class A {
static func static_before() {
print(staticVar) // $ access=staticVar
B(); // $ access=A.B
let b: B = nil // $ access=A.B
let c: C = nil // $ access=A.C
}
func instance_before() {
print(instanceVar) // $ access=instanceVar
B(); // $ access=A.B
let b: B = nil // $ access=A.B
let c: C = nil // $ access=A.C
}

private static let staticVar = 123
private let instanceVar = 456

class B {} // name=A.B
typealias C = B // $ access=A.B // name=A.C

static func static_after() {
print(staticVar) // $ access=staticVar
B(); // $ access=A.B
let b: B = nil // $ access=A.B
let c: C = nil // $ access=A.C

}
func instance_after() {
print(instanceVar) // $ access=instanceVar
B(); // $ access=A.B
let b: B = nil // $ access=A.B
let c: C = nil // $ access=A.C
}
}

class Base {} // name=top.Base

// Base types and type parameter bounds can't see members in the class body
class C : Base { // $ MISSING: access=top.Base SPURIOUS: access=C.Base
class Base {} // name=C.Base
}

class D<T : Base> { // $ MISSING: access=top.Base SPURIOUS: access=D.Base
class Base {} // name=D.Base
}

class E<T> where T : Base { // $ access=T MISSING: access=top.Base SPURIOUS: access=E.Base
class Base {} // name=E.Base
}

// Base types and type parameter bounds can see type parameters
class F<TypeParamF> :
D<TypeParamF> { // $ access=D access=TypeParamF
}

class G<TypeParamG :
D<TypeParamG>> { // $ access=D access=TypeParamG
}

class H<TypeParamH> where
TypeParamH : Base { // $ access=TypeParamH access=top.Base
}

// Type parameter bounds can see other type parameters, even if declared later
class I<
T1 : D<T2>, // $ access=D access=I.T2
T2> { // name=I.T2
}

// Members can see type parameters.
class J<TypeParamI> {
let x: TypeParamI; // $ access=TypeParamI
}
Loading