Skip to content

Commit 9545598

Browse files
authored
Merge pull request #10226 from github/redsun82/swift-generate-printast
Swift: add `PrintAst`
2 parents 2ec53bf + bb845c6 commit 9545598

33 files changed

+1907
-2700
lines changed

config/identical-files.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -584,19 +584,19 @@
584584
],
585585
"Swift declarations test file": [
586586
"swift/ql/test/extractor-tests/declarations/declarations.swift",
587-
"swift/ql/test/library-tests/parent/declarations.swift"
587+
"swift/ql/test/library-tests/ast/declarations.swift"
588588
],
589589
"Swift statements test file": [
590590
"swift/ql/test/extractor-tests/statements/statements.swift",
591-
"swift/ql/test/library-tests/parent/statements.swift"
591+
"swift/ql/test/library-tests/ast/statements.swift"
592592
],
593593
"Swift expressions test file": [
594594
"swift/ql/test/extractor-tests/expressions/expressions.swift",
595-
"swift/ql/test/library-tests/parent/expressions.swift"
595+
"swift/ql/test/library-tests/ast/expressions.swift"
596596
],
597597
"Swift patterns test file": [
598598
"swift/ql/test/extractor-tests/patterns/patterns.swift",
599-
"swift/ql/test/library-tests/parent/patterns.swift"
599+
"swift/ql/test/library-tests/ast/patterns.swift"
600600
],
601601
"IncompleteMultiCharacterSanitization JS/Ruby": [
602602
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",

swift/codegen/schema.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ Type:
6060
canonical_type: Type
6161

6262
IterableDeclContext:
63-
members: Decl*
63+
_children:
64+
members: Decl*
6465

6566
ExtensionDecl:
6667
extended_type_decl: NominalTypeDecl
@@ -293,8 +294,7 @@ TypeAliasType:
293294

294295
EnumCaseDecl:
295296
_extends: Decl
296-
_children:
297-
elements: EnumElementDecl*
297+
elements: EnumElementDecl*
298298

299299
IfConfigDecl:
300300
_extends: Decl
@@ -836,8 +836,7 @@ PrefixUnaryExpr:
836836

837837
SelfApplyExpr:
838838
_extends: ApplyExpr
839-
_children:
840-
base: Expr
839+
base: Expr
841840

842841
ArrayExpr:
843842
_extends: CollectionExpr
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
21
private import codeql.swift.generated.Raw
32

4-
predicate constructArgument(Raw::Argument id) { any() }
3+
predicate constructArgument(Raw::Argument id) {
4+
// exclude an argument that will be part of a DotSyntaxCallExpr
5+
// that will be transformed into a MethodRefCallExpr
6+
not exists(Raw::DotSyntaxCallExpr c |
7+
c.getFunction() instanceof Raw::DeclRefExpr and id = c.getArgument(0)
8+
)
9+
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
21
private import codeql.swift.generated.Raw
32

4-
predicate constructDeclRefExpr(Raw::DeclRefExpr id) { any() }
3+
predicate constructDeclRefExpr(Raw::DeclRefExpr id) {
4+
// exclude an argument that will be part of a DotSyntaxCallExpr
5+
// that will be transformed into a MethodRefCallExpr
6+
not exists(Raw::DotSyntaxCallExpr c | id = c.getFunction())
7+
}

swift/ql/lib/codeql/swift/generated/ParentChild.qll

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,18 @@ private module Impl {
6060
private Element getImmediateChildOfIterableDeclContext(
6161
IterableDeclContext e, int index, string partialPredicateCall
6262
) {
63-
exists(int b, int bElement, int n |
63+
exists(int b, int bElement, int n, int nMember |
6464
b = 0 and
6565
bElement = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfElement(e, i, _)) | i) and
6666
n = bElement and
67+
nMember = n + 1 + max(int i | i = -1 or exists(e.getImmediateMember(i)) | i) and
6768
(
6869
none()
6970
or
7071
result = getImmediateChildOfElement(e, index - b, partialPredicateCall)
72+
or
73+
result = e.getImmediateMember(index - n) and
74+
partialPredicateCall = "Member(" + (index - n).toString() + ")"
7175
)
7276
)
7377
}
@@ -1562,18 +1566,14 @@ private module Impl {
15621566
private Element getImmediateChildOfEnumCaseDecl(
15631567
EnumCaseDecl e, int index, string partialPredicateCall
15641568
) {
1565-
exists(int b, int bDecl, int n, int nElement |
1569+
exists(int b, int bDecl, int n |
15661570
b = 0 and
15671571
bDecl = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfDecl(e, i, _)) | i) and
15681572
n = bDecl and
1569-
nElement = n + 1 + max(int i | i = -1 or exists(e.getImmediateElement(i)) | i) and
15701573
(
15711574
none()
15721575
or
15731576
result = getImmediateChildOfDecl(e, index - b, partialPredicateCall)
1574-
or
1575-
result = e.getImmediateElement(index - n) and
1576-
partialPredicateCall = "Element(" + (index - n).toString() + ")"
15771577
)
15781578
)
15791579
}
@@ -3970,17 +3970,14 @@ private module Impl {
39703970
private Element getImmediateChildOfSelfApplyExpr(
39713971
SelfApplyExpr e, int index, string partialPredicateCall
39723972
) {
3973-
exists(int b, int bApplyExpr, int n, int nBase |
3973+
exists(int b, int bApplyExpr, int n |
39743974
b = 0 and
39753975
bApplyExpr = b + 1 + max(int i | i = -1 or exists(getImmediateChildOfApplyExpr(e, i, _)) | i) and
39763976
n = bApplyExpr and
3977-
nBase = n + 1 and
39783977
(
39793978
none()
39803979
or
39813980
result = getImmediateChildOfApplyExpr(e, index - b, partialPredicateCall)
3982-
or
3983-
index = n and result = e.getImmediateBase() and partialPredicateCall = "Base()"
39843981
)
39853982
)
39863983
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Provides queries to pretty-print a Go AST as a graph.
3+
*/
4+
5+
import PrintAstNode
6+
7+
cached
8+
private int getOrder(PrintAstNode node) {
9+
node =
10+
rank[result](PrintAstNode n, Location loc |
11+
loc = n.getLocation()
12+
|
13+
n
14+
order by
15+
loc.getFile().getName(), loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(),
16+
loc.getEndColumn()
17+
)
18+
}
19+
20+
/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */
21+
query predicate nodes(PrintAstNode node, string key, string value) {
22+
node.shouldBePrinted() and
23+
(
24+
key = "semmle.label" and value = node.toString()
25+
or
26+
key = "semmle.order" and value = getOrder(node).toString()
27+
or
28+
value = node.getProperty(key)
29+
)
30+
}
31+
32+
/**
33+
* Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the
34+
* given `value`.
35+
*/
36+
query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) {
37+
source.shouldBePrinted() and
38+
target.shouldBePrinted() and
39+
exists(int index, string accessor | source.hasChild(target, index, accessor) |
40+
key = "semmle.label" and value = accessor
41+
or
42+
key = "semmle.order" and value = index.toString()
43+
)
44+
}
45+
46+
/** Holds if property `key` of the graph has the given `value`. */
47+
query predicate graphProperties(string key, string value) {
48+
key = "semmle.graphKind" and value = "tree"
49+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* Provides classes used to pretty-print a Go AST as a graph.
3+
* This is factored out of `PrintAst.qll` for testing purposes.
4+
*/
5+
6+
import swift
7+
import codeql.swift.generated.ParentChild
8+
9+
private newtype TPrintAstConfiguration = TMakePrintAstConfiguration()
10+
11+
/**
12+
* The hook to customize the files and functions printed by this module.
13+
*/
14+
class PrintAstConfiguration extends TPrintAstConfiguration {
15+
/**
16+
* Gets the string representation of this singleton
17+
*/
18+
string toString() { result = "PrintAstConfiguration" }
19+
20+
/**
21+
* Holds if the AST for `e` should be printed. By default, holds for all.
22+
*/
23+
predicate shouldPrint(Locatable e) { any() }
24+
}
25+
26+
private predicate shouldPrint(Locatable e) { any(PrintAstConfiguration config).shouldPrint(e) }
27+
28+
/**
29+
* An AST node that should be printed.
30+
*/
31+
private newtype TPrintAstNode =
32+
TLocatable(Locatable ast) {
33+
// Only consider resolved nodes (that is not within the hidden conversion AST)
34+
ast = ast.resolve()
35+
}
36+
37+
/**
38+
* A node in the output tree.
39+
*/
40+
class PrintAstNode extends TPrintAstNode {
41+
/**
42+
* Gets a textual representation of this node.
43+
*/
44+
abstract string toString();
45+
46+
/**
47+
* Gets the child node at index `index`. Child indices must be unique,
48+
* but need not be contiguous.
49+
*/
50+
abstract predicate hasChild(PrintAstNode child, int index, string accessor);
51+
52+
/**
53+
* Holds if this node should be printed in the output.
54+
*/
55+
abstract predicate shouldBePrinted();
56+
57+
/**
58+
* Gets the location of this node in the source code.
59+
*/
60+
abstract Location getLocation();
61+
62+
/**
63+
* Gets the value of an additional property of this node, where the name of
64+
* the property is `key`.
65+
*/
66+
string getProperty(string key) { none() }
67+
}
68+
69+
/**
70+
* A graph node representing a real Locatable node.
71+
*/
72+
class PrintLocatable extends PrintAstNode, TLocatable {
73+
Locatable ast;
74+
75+
PrintLocatable() { this = TLocatable(ast) }
76+
77+
final override predicate shouldBePrinted() { shouldPrint(ast) }
78+
79+
override predicate hasChild(PrintAstNode child, int index, string accessor) {
80+
child = TLocatable(getChildAndAccessor(ast, index, accessor))
81+
}
82+
83+
override string toString() { result = "[" + concat(ast.getPrimaryQlClasses(), ", ") + "] " + ast }
84+
85+
final override Location getLocation() { result = ast.getLocation() }
86+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Provides shared predicates related to contextual queries in the code viewer.
3+
*/
4+
5+
import swift
6+
7+
/**
8+
* Returns the `File` matching the given source file name as encoded by the VS
9+
* Code extension.
10+
*/
11+
cached
12+
File getFileBySourceArchiveName(string name) {
13+
// The name provided for a file in the source archive by the VS Code extension
14+
// has some differences from the absolute path in the database:
15+
// 1. colons are replaced by underscores
16+
// 2. there's a leading slash, even for Windows paths: "C:/foo/bar" ->
17+
// "/C_/foo/bar"
18+
// 3. double slashes in UNC prefixes are replaced with a single slash
19+
// We can handle 2 and 3 together by unconditionally adding a leading slash
20+
// before replacing double slashes.
21+
name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/")
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @name Print AST
3+
* @description Outputs a representation of a file's Abstract Syntax Tree. This
4+
* query is used by the VS Code extension.
5+
* @id swift/print-ast
6+
* @kind graph
7+
* @tags ide-contextual-queries/print-ast
8+
*/
9+
10+
import swift
11+
import codeql.swift.printast.PrintAst
12+
import IDEContextual
13+
14+
/**
15+
* Gets the source file to generate an AST from.
16+
*/
17+
external string selectedSourceFile();
18+
19+
class PrintAstConfigurationOverride extends PrintAstConfiguration {
20+
/**
21+
* Holds if the location matches the selected file in the VS Code extension and
22+
* the element is `e`.
23+
*/
24+
override predicate shouldPrint(Locatable e) {
25+
super.shouldPrint(e) and
26+
e.getFile() = getFileBySourceArchiveName(selectedSourceFile())
27+
}
28+
}

0 commit comments

Comments
 (0)