Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
61 changes: 34 additions & 27 deletions unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,24 @@ private ClassLikeDeclaration tryResolveExtensionTarget(ClassLikeDeclaration cls)
result = cls
}

private predicate unqualifiedMemberAccessLocalScopingCand(
PotentialLocalNameAccess access, boolean instanceAccess, NameBinding target,
ClassLikeDeclaration accessingClass
) {
not access instanceof NameBinding and
exists(LocalName local |
target.getLocalName() = local and
access.getLocalName() = local and
target.getDeclaration() = accessingClass.getAMember()
|
instanceAccess = true and
isInstanceMember(target.getDeclaration())
or
instanceAccess = false and
isStaticMember(target.getDeclaration())
)
}

/**
* Holds if `access` may resolve to `target` through the enclosing `accessingClass`.
*
Expand All @@ -702,35 +720,24 @@ private predicate unqualifiedMemberAccessCand(
PotentialLocalNameAccess access, boolean instanceAccess, NameBinding target,
ClassLikeDeclaration accessingClass
) {
// Resolved by local scoping
unqualifiedMemberAccessLocalScopingCand(access, instanceAccess, target, accessingClass)
or
// Resolved in an uncertain scope
not access instanceof NameBinding and
(
// Resolved by local scoping
exists(LocalName local |
target.getLocalName() = local and
access.getLocalName() = local and
target.getDeclaration() = accessingClass.getAMember()
|
instanceAccess = true and
isInstanceMember(target.getDeclaration())
or
instanceAccess = false and
isStaticMember(target.getDeclaration())
)
not unqualifiedMemberAccessLocalScopingCand(access, instanceAccess, target, _) and
exists(NamespaceNode namespace, string name |
name = access.getName() and
accessingClass =
tryResolveExtensionTarget(LocalNameBindingOutput::getAnUncertainScope(access, name))
|
instanceAccess = true and
namespace.isInstanceMemberNamespace(accessingClass) and
namespace.getMember(name).isIdentifier(target)
or
// Resolved in an uncertain scope
exists(NamespaceNode namespace, string name |
name = access.getName() and
accessingClass =
tryResolveExtensionTarget(LocalNameBindingOutput::getAnUncertainScope(access, name))
|
instanceAccess = true and
namespace.isInstanceMemberNamespace(accessingClass) and
namespace.getMember(name).isIdentifier(target)
or
instanceAccess = false and
namespace.isStaticMemberNamespace(accessingClass) and
namespace.getMember(name).isIdentifier(target)
)
instanceAccess = false and
namespace.isStaticMemberNamespace(accessingClass) and
namespace.getMember(name).isIdentifier(target)
)
}

Expand Down
36 changes: 36 additions & 0 deletions unified/ql/test/library-tests/static-name-binding/test.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
testFailures
unqualifiedMemberAccess
| extensions.swift:3:9:3:17 | ownMethod | extensions.swift:1:1:7:1 | class A |
| extensions.swift:4:9:4:24 | extensionMethod1 | extensions.swift:1:1:7:1 | class A |
| extensions.swift:5:9:5:24 | extensionMethod2 | extensions.swift:1:1:7:1 | class A |
| extensions.swift:11:9:11:17 | ownMethod | extensions.swift:1:1:7:1 | class A |
| extensions.swift:12:9:12:24 | extensionMethod1 | extensions.swift:9:1:15:1 | extension A |
| extensions.swift:13:9:13:24 | extensionMethod2 | extensions.swift:1:1:7:1 | class A |
| extensions.swift:19:9:19:17 | ownMethod | extensions.swift:1:1:7:1 | class A |
| extensions.swift:20:9:20:24 | extensionMethod1 | extensions.swift:1:1:7:1 | class A |
| extensions.swift:21:9:21:24 | extensionMethod2 | extensions.swift:17:1:23:1 | extension A |
| extensions.swift:34:20:34:20 | C | extensions.swift:25:1:26:1 | class B |
| extensions.swift:35:17:35:17 | D | extensions.swift:34:5:36:5 | class B.Nested |
| extensions.swift:51:9:51:18 | baseMethod | extensions.swift:49:1:58:1 | class X |
| extensions.swift:52:9:52:24 | baseMethodNoImpl | extensions.swift:49:1:58:1 | class X |
| extensions.swift:53:9:53:21 | baseMethodExt | extensions.swift:49:1:58:1 | class X |
| extensions.swift:56:9:56:29 | baseMethodDefaultImpl | extensions.swift:49:1:58:1 | class X |
| extensions.swift:66:9:66:18 | baseMethod | extensions.swift:64:1:69:1 | class Y |
| extensions.swift:67:9:67:29 | baseMethodDefaultImpl | extensions.swift:64:1:69:1 | class Y |
| implicit-instance-field-access.swift:5:16:5:16 | x | implicit-instance-field-access.swift:1:1:7:1 | class A |
| implicit-instance-field-access.swift:11:16:11:16 | x | implicit-instance-field-access.swift:9:1:13:1 | class B |
| not-a-package/Main/Runner.swift:9:14:9:19 | Nested | not-a-package/Main/Runner.swift:8:1:10:1 | class MyDriver |
| not-a-package/Mock/Runner.swift:9:14:9:19 | Nested | not-a-package/Mock/Runner.swift:8:1:10:1 | class MyDriver |
| package1/Sources/Target1/File1.swift:6:9:6:30 | target2ExtensionMethod | package1/Sources/Target1/File1.swift:4:1:8:1 | class ScopedExtensionTarget |
| test.swift:8:24:8:24 | B | test.swift:3:1:11:1 | class A |
| test.swift:37:13:37:13 | A | test.swift:31:1:39:1 | class E |
| test.swift:38:13:38:13 | A | test.swift:31:1:39:1 | class E |
| unqualified-access.swift:8:13:8:13 | B | unqualified-access.swift:7:1:20:1 | class ASub |
| unqualified-access.swift:9:13:9:13 | B | unqualified-access.swift:7:1:20:1 | class ASub |
| unqualified-access.swift:11:17:11:17 | B | unqualified-access.swift:7:1:20:1 | class ASub |
| unqualified-access.swift:12:17:12:17 | B | unqualified-access.swift:7:1:20:1 | class ASub |
| unqualified-access.swift:13:17:13:17 | C | unqualified-access.swift:11:5:14:5 | class ASub.BSub |
| unqualified-access.swift:16:18:16:18 | B | unqualified-access.swift:7:1:20:1 | class ASub |
| unqualified-access.swift:18:17:18:17 | C | unqualified-access.swift:16:5:19:5 | class ASub.BSub2 |
| unqualified-access.swift:24:16:24:19 | Leaf | unqualified-access.swift:22:1:27:1 | class Node |
| unqualified-access.swift:24:23:24:26 | Leaf | unqualified-access.swift:22:1:27:1 | class Node |
6 changes: 6 additions & 0 deletions unified/ql/test/library-tests/static-name-binding/test.ql
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ module StaticDeclAccess implements TestSig {
}

import MakeTest<StaticDeclAccess>

query predicate unqualifiedMemberAccess(
UnqualifiedMemberAccess uma, ClassLikeDeclaration accessingClass
) {
accessingClass = uma.getAccessingClass()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@ class A {
}
}

class ASub : A { // $ access=A
let x1: B = nil; // $ access=A.B
let x2: B.C = nil; // $ access=A.B access=A.B.C
class ASub: A { // $ access=A
let x1: B = nil // $ access=A.B
let x2: B.C = nil // $ access=A.B access=A.B.C

class BSub : B { // $ access=A.B
let x3: B = nil; // $ access=A.B
let x4: C = nil; // $ access=A.B.C
class BSub: B { // $ access=A.B
let x3: B = nil // $ access=A.B
let x4: C = nil // $ access=A.B.C
}

class BSub2 : B { // $ access=A.B
class C {} // shadow the inherited C
let x5: C = nil; // $ access=ASub.BSub2.C
class BSub2: B { // $ access=A.B
class C {} // shadow the inherited C
let x5: C = nil // $ access=ASub.BSub2.C
}
}

class Node {
class Leaf: Node { // $ access=Node
let x: Leaf = Leaf() // $ access=Node.Leaf
let y: Node.Leaf.Leaf.Leaf.Leaf = nil // $ access=Node access=Node.Leaf
}
}
Loading