Skip to content

Commit 1f380d2

Browse files
committed
unified: Fix spurious resolution by refining isPrivateToLocalScope
1 parent 811932f commit 1f380d2

4 files changed

Lines changed: 28 additions & 11 deletions

File tree

unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
private import unified
22
private import codeql.util.Unit
3+
private import codeql.unified.internal.LocalNameBinding
34
private import codeql.unified.internal.NameBindingPluginSwift // ensure overrides are seen
45

56
/** Extension point for language-specific inputs to name binding. */
@@ -14,13 +15,16 @@ class NameBindingPlugin extends Unit {
1415
predicate isInstanceMember(ClassLikeDeclaration cls, Member member) { none() }
1516

1617
/**
17-
* Holds if `member` is only visible in its local scope, and can thus be entirely resolved
18+
* Holds if `binding`, declared by `member` is only visible in its local scope, and can thus be entirely resolved
1819
* by local name-binding, suppressing any store-steps that would otherwise be induced from the member.
1920
*
2021
* Need only be implemented for members that occur in the context of class or top-level, as other
2122
* contexts are considered local already.
23+
*
24+
* `binding` refers to an `Identifier` or `BulkImportingPattern` bound by the member.
2225
*/
23-
predicate isPrivateToLocalScope(Stmt member) { none() }
26+
bindingset[member, binding]
27+
predicate isPrivateToLocalScope(Stmt member, AstNode binding) { none() }
2428
}
2529

2630
/** Holds if `member` is an instance member. */
@@ -31,9 +35,18 @@ predicate isInstanceMember(Member member) {
3135
)
3236
}
3337

34-
/** Holds if `member` is only visible in its local scope. */
35-
predicate isPrivateToLocalScope(Stmt member) {
36-
any(NameBindingPlugin p).isPrivateToLocalScope(member)
38+
/** Holds if `binding` is only visible in its local scope. */
39+
pragma[nomagic]
40+
predicate isPrivateToLocalScope(AstNode binding) {
41+
exists(Stmt member |
42+
bindingContext(binding, _, member) and
43+
(
44+
member = any(ClassLikeDeclaration cls).getAMember() or
45+
member = any(TopLevel t).getBody().getAStmt()
46+
) and
47+
(binding instanceof NameDeclaration or binding instanceof BulkImportingPattern) and
48+
any(NameBindingPlugin p).isPrivateToLocalScope(member, binding)
49+
)
3750
}
3851

3952
/**

unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ class NameBindingPluginSwift extends NameBindingPlugin {
1313
not member.hasModifier(["static", "class", "enum_case"])
1414
}
1515

16-
override predicate isPrivateToLocalScope(Stmt member) {
16+
bindingset[member, binding]
17+
override predicate isPrivateToLocalScope(Stmt member, AstNode binding) {
1718
// Private top-level members
1819
member = any(TopLevel top).getBody().getAStmt() and
1920
member.hasModifier(["private", "fileprivate"])
2021
or
2122
// Imports are always file-local, except `@_exported` import which re-export everything
2223
member instanceof ImportDeclaration and
23-
not member.hasModifier("@_exported")
24+
not (
25+
member.hasModifier("@_exported") and
26+
binding instanceof BulkImportingPattern
27+
)
2428
//
2529
// Note: Private class members can be seen within type-extensions in the same file,
2630
// so we can't declare those private to their local scope.

unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ predicate storeStep(NameBindingNode node1, string name, NameBindingNode node2) {
152152
exists(ClassLikeDeclaration cls, Member member, NameDeclaration nameDecl |
153153
member = cls.getAMember() and
154154
not isInstanceMember(member) and
155-
not isPrivateToLocalScope(member) and
155+
not isPrivateToLocalScope(nameDecl) and
156156
nameDecl.getDeclaration() = member and
157157
node1.isIdentifier(nameDecl) and
158158
name = nameDecl.getName() and
@@ -161,7 +161,7 @@ predicate storeStep(NameBindingNode node1, string name, NameBindingNode node2) {
161161
or
162162
exists(TopLevel top, Stmt stmt, NameDeclaration nameDecl |
163163
stmt = top.getBody().getAStmt() and
164-
not isPrivateToLocalScope(stmt) and
164+
not isPrivateToLocalScope(nameDecl) and
165165
nameDecl.getDeclaration() = stmt and
166166
node1.isIdentifier(nameDecl) and
167167
name = nameDecl.getName() and
@@ -215,7 +215,7 @@ predicate valueStep(NameBindingNode node1, NameBindingNode node2) {
215215
// Bulk re-exporting declarations
216216
exists(TopLevel top |
217217
declaration = top.getBody().getAStmt() and
218-
not isPrivateToLocalScope(declaration) and
218+
not isPrivateToLocalScope(p) and
219219
node2 = getModuleNodeFromFile(top.getFile())
220220
)
221221
)

unified/ql/test/library-tests/static-name-binding/package1/Sources/Target4/UseReExport.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ private protocol P {
1010
let x6: Target3.C; // $ access=Target3.C
1111

1212
// `@_exported import Target2` should not re-export the local 'Target2' name, only its contents
13-
let x7: Target3.Target2.A; // $ SPURIOUS: access=Target2.A
13+
let x7: Target3.Target2.A; // should not resolve
1414
}

0 commit comments

Comments
 (0)