Skip to content

Commit 186e211

Browse files
committed
unified: Clean up API a bit
1 parent 9cd018a commit 186e211

4 files changed

Lines changed: 118 additions & 151 deletions

File tree

‎unified/ql/lib/codeql/unified/internal/FacadeAst.qll‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ module Unified {
5555
// just strip the quotes here and ignore escape sequences.
5656
result = this.(StringLiteral).getValue().regexpCapture("\"(.*)\"", 1)
5757
}
58+
59+
/** Gets the immediately-enclosing expression, skipping over intermediate sub-nodes like `Argument`, and without crossing a function boundary. */
60+
Expr getEnclosingExpr() {
61+
result = this.getParent() and
62+
not result instanceof Callable
63+
or
64+
result = this.getParent().(Argument).getParent()
65+
}
5866
}
5967

6068
/** A binary expression. */

‎unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll‎

Lines changed: 89 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -186,119 +186,94 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
186186
}
187187

188188
additional predicate bindingContext(AstNode pattern, AstNode scope, AstNode declaration) {
189-
exists(SiblingShadowingDecl decl |
190-
scope = decl and
191-
pattern = decl.getPattern() and
192-
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern) and
193-
declaration = decl
194-
)
195-
or
196-
exists(VariableDeclaration decl |
197-
not decl instanceof SiblingShadowingDecl and
198-
getChild(scope, _) = decl and
199-
pattern = decl.getPattern() and
200-
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern) and
201-
declaration = decl
202-
)
203-
or
204-
exists(FunctionDeclaration func |
205-
getChild(scope, _) = func and
206-
pattern = func.getName() and
207-
declaration = func
208-
)
209-
or
210-
exists(Parameter param |
211-
scope = param.getParent() and // TODO: add SourceCallable and use .getParameter() instead
212-
pattern = param.getPattern() and
213-
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern) and
214-
declaration = param
215-
)
216-
or
217-
exists(CatchClause catch |
218-
scope = catch and // ensure both body and pattern are in scope
219-
pattern = catch.getPattern() and
220-
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern) and
221-
declaration = catch
222-
)
223-
or
224-
exists(SwitchCase case |
225-
scope = case and // ensure both body and pattern are in scope
226-
pattern = case.getPattern() and
227-
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern) and
228-
declaration = case
229-
)
230-
or
231-
exists(ForEachStmt stmt |
232-
scope = stmt and // ensure both 'body' and 'guard' are in scope
233-
pattern = stmt.getPattern() and
234-
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern) and
235-
declaration = stmt
236-
)
237-
or
238-
exists(ClassLikeDeclaration cls |
239-
getChild(scope, _) = cls and
240-
pattern = cls.getName() and
241-
not cls.hasModifier("extension") and // TODO: Fix in the AST mapping: type extensions should reference their type, not declare it
242-
declaration = cls
243-
)
244-
or
245-
exists(TypeAliasDeclaration decl |
246-
getChild(scope, _) = decl and
247-
pattern = decl.getName() and
248-
declaration = decl
249-
)
250-
or
251-
exists(TypeParameter param |
252-
scope = param.getParent() and
253-
pattern = param.getName() and
254-
declaration = param
255-
)
256-
or
257-
exists(AssociatedTypeDeclaration decl |
258-
getChild(scope, _) = decl and
259-
pattern = decl.getName() and
260-
declaration = decl
261-
)
262-
or
263-
exists(AccessorDeclaration decl |
264-
getChild(scope, _) = decl and
265-
pattern = decl.getName() and
266-
declaration = decl
267-
)
268-
or
269-
exists(ImportDeclaration imprt |
270-
getChild(scope, _) = imprt and
271-
pattern = imprt.getPattern() and
272-
declaration = imprt
273-
)
274-
or
275-
exists(NamedPattern p |
276-
bindingContext(p, scope, declaration) and
277-
pattern = p.getIdentifier()
278-
)
279-
or
280-
bindingContext(getEnclosingPatternExpr(pattern.(Expr)), scope, declaration) and
281-
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern)
282-
}
283-
284-
private Expr getEnclosingPatternExpr(Expr child) {
285-
exists(OrPattern parent | result = parent and child = parent.getAPattern())
286-
or
287-
exists(ConditionalPattern parent | result = parent and child = parent.getPattern())
288-
or
289-
exists(Argument arg |
290-
child = arg.getValue() and
291-
result = arg.getParent().(Expr)
292-
)
293-
or
294-
exists(NamedPattern parent | result = parent and child = parent.getSubPattern())
295-
or
296-
exists(ExprPattern parent | result = parent and child = parent.getExpr())
297-
or
298-
exists(TypeTestExpr parent |
299-
result = parent and
300-
not exists(parent.getOperator()) and
301-
child = parent.getExpr()
189+
not any(NameBindingPlugin p).isNameReferenceInPatternContext(pattern) and
190+
(
191+
exists(SiblingShadowingDecl decl |
192+
scope = decl and
193+
pattern = decl.getPattern() and
194+
declaration = decl
195+
)
196+
or
197+
exists(VariableDeclaration decl |
198+
not decl instanceof SiblingShadowingDecl and
199+
getChild(scope, _) = decl and
200+
pattern = decl.getPattern() and
201+
declaration = decl
202+
)
203+
or
204+
exists(FunctionDeclaration func |
205+
getChild(scope, _) = func and
206+
pattern = func.getName() and
207+
declaration = func
208+
)
209+
or
210+
exists(Parameter param |
211+
scope = param.getParent() and // TODO: add SourceCallable and use .getParameter() instead
212+
pattern = param.getPattern() and
213+
declaration = param
214+
)
215+
or
216+
exists(CatchClause catch |
217+
scope = catch and // ensure both body and pattern are in scope
218+
pattern = catch.getPattern() and
219+
declaration = catch
220+
)
221+
or
222+
exists(SwitchCase case |
223+
scope = case and // ensure both body and pattern are in scope
224+
pattern = case.getPattern() and
225+
declaration = case
226+
)
227+
or
228+
exists(ForEachStmt stmt |
229+
scope = stmt and // ensure both 'body' and 'guard' are in scope
230+
pattern = stmt.getPattern() and
231+
declaration = stmt
232+
)
233+
or
234+
exists(ClassLikeDeclaration cls |
235+
getChild(scope, _) = cls and
236+
pattern = cls.getName() and
237+
not cls.hasModifier("extension") and // TODO: Fix in the AST mapping: type extensions should reference their type, not declare it
238+
declaration = cls
239+
)
240+
or
241+
exists(TypeAliasDeclaration decl |
242+
getChild(scope, _) = decl and
243+
pattern = decl.getName() and
244+
declaration = decl
245+
)
246+
or
247+
exists(TypeParameter param |
248+
scope = param.getParent() and
249+
pattern = param.getName() and
250+
declaration = param
251+
)
252+
or
253+
exists(AssociatedTypeDeclaration decl |
254+
getChild(scope, _) = decl and
255+
pattern = decl.getName() and
256+
declaration = decl
257+
)
258+
or
259+
exists(AccessorDeclaration decl |
260+
getChild(scope, _) = decl and
261+
pattern = decl.getName() and
262+
declaration = decl
263+
)
264+
or
265+
exists(ImportDeclaration imprt |
266+
getChild(scope, _) = imprt and
267+
pattern = imprt.getPattern() and
268+
declaration = imprt
269+
)
270+
or
271+
exists(NamedPattern p |
272+
bindingContext(p, scope, declaration) and
273+
pattern = p.getIdentifier()
274+
)
275+
or
276+
bindingContext(pattern.(Expr).getEnclosingExpr(), scope, declaration)
302277
)
303278
}
304279

@@ -315,7 +290,7 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
315290
p = result.getPattern(_)
316291
or
317292
not p instanceof OrPattern and
318-
result = getEnclosingOrPattern(getEnclosingPatternExpr(p))
293+
result = getEnclosingOrPattern(p.getEnclosingExpr())
319294
}
320295

321296
private OrPattern getEnclosingOrPatternFromIdentifier(Identifier id) {

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
private import codeql.files.FileSystem
1+
private import unified
22
private import codeql.util.Unit
3-
private import codeql.unified.internal.FacadeAst::Unified
43
private import codeql.unified.internal.NameBindingPluginSwift // ensure overrides are seen
54

65
/** Extension point for language-specific inputs to name binding. */
76
class NameBindingPlugin extends Unit {
8-
/** Holds if `identifier`, occurring in pattern context, refers to an existing name. */
7+
/**
8+
* Holds if `identifier`, occurring in pattern context, refers to an existing name.
9+
*
10+
* The caller has already restricted `identifier` to one that appears in pattern context.
11+
*/
12+
bindingset[identifier]
913
predicate isNameReferenceInPatternContext(Identifier identifier) { none() }
1014

1115
/**

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

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
* Provides Swift-specific name binding rules.
33
*/
44

5-
private import codeql.files.FileSystem
6-
private import codeql.unified.internal.FacadeAst::Unified
5+
private import unified
76
private import codeql.unified.internal.NameBindingPlugin
87

98
class NameBindingPluginSwift extends NameBindingPlugin {
9+
bindingset[identifier]
1010
override predicate isNameReferenceInPatternContext(Identifier identifier) {
11-
not isInsideBindingPattern(identifier)
11+
isUnboundPattern(identifier)
1212
}
1313

1414
// Note: For now we assume all code is Swift, but in the future we must restrict these rules to Swift-files
@@ -42,37 +42,17 @@ class NameBindingPluginSwift extends NameBindingPlugin {
4242
}
4343
}
4444

45-
private predicate isInsideBindingPattern(AstNode child) {
46-
exists(ExprPattern parent |
47-
child = parent.getExpr() and
48-
parent.hasModifier(["let", "var"])
49-
)
50-
or
51-
child = any(VariableDeclaration parent).getPattern()
52-
or
53-
child = any(Parameter parent).getPattern()
54-
or
55-
child = any(ForEachStmt parent).getPattern()
56-
or
57-
child = any(CatchClause parent).getPattern()
58-
or
59-
exists(OrPattern parent | child = parent.getAPattern() and isInsideBindingPattern(parent))
60-
or
61-
exists(ConditionalPattern parent | child = parent.getPattern() and isInsideBindingPattern(parent))
62-
or
63-
exists(Argument parent | child = parent.getValue() and isInsideBindingPattern(parent.getParent()))
64-
or
65-
exists(TupleExpr parent, Argument element |
66-
element = parent.getAnElement() and
67-
child = element.getValue() and
68-
isInsideBindingPattern(parent)
69-
)
70-
or
71-
exists(TypeTestExpr parent |
72-
not exists(parent.getOperator()) and
73-
child = parent.getExpr() and
74-
isInsideBindingPattern(parent)
75-
)
45+
/** Holds if `node` is in a context where a bare identifier should be seen as a reference rather than a declaration. */
46+
private predicate isUnboundPattern(Expr node) {
47+
// 'case/catch' start an unbound-pattern context, 'let/var' terminate it
48+
(
49+
node = any(SwitchCase c).getPattern()
50+
or
51+
node = any(CatchClause c).getPattern()
52+
or
53+
isUnboundPattern(node.(Expr).getEnclosingExpr())
54+
) and
55+
not node.(ExprPattern).hasModifier(["let", "var"])
7656
}
7757

7858
private predicate predefinedSourceFolders(string folder, int ordering) {

0 commit comments

Comments
 (0)