Skip to content

Commit 310c78d

Browse files
committed
C++: Add PreprocBlock.qll library.
1 parent 41cca47 commit 310c78d

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import cpp
2+
3+
/**
4+
* Gets the line of the `ix`th `PreprocessorBranchDirective` in file `f`.
5+
*/
6+
private cached int getPreprocLineFromIndex(File f, int ix) {
7+
result = rank[ix](
8+
PreprocessorBranchDirective g |
9+
g.getFile() = f |
10+
g.getLocation().getStartLine()
11+
)
12+
}
13+
14+
/**
15+
* Gets the `ix`th `PreprocessorBranchDirective` in file `f`.
16+
*/
17+
private PreprocessorBranchDirective getPreprocFromIndex(File f, int ix) {
18+
result.getFile() = f and
19+
result.getLocation().getStartLine() = getPreprocLineFromIndex(f, ix)
20+
}
21+
22+
/**
23+
* Get the index of a `PreprocessorBranchDirective` in its `file`.
24+
*/
25+
private int getPreprocIndex(PreprocessorBranchDirective directive)
26+
{
27+
directive = getPreprocFromIndex(directive.getFile(), result)
28+
}
29+
30+
/**
31+
* A chunk of code from one preprocessor branch (`#if`, `#ifdef`,
32+
* `#ifndef`, `#elif` or `#else`) to the directive that closes it
33+
* (`#elif`, `#else` or `#endif`). The `getParent()` method
34+
* allows these blocks to be navigated as a tree, with the root
35+
* being the entire file.
36+
*/
37+
class PreprocessorBlock extends @element {
38+
PreprocessorBlock() {
39+
mkElement(this) instanceof File or
40+
mkElement(this) instanceof PreprocessorBranch or
41+
mkElement(this) instanceof PreprocessorElse
42+
}
43+
44+
/**
45+
* Holds if this element is at the specified location.
46+
* The location spans column `startcolumn` of line `startline` to
47+
* column `endcolumn` of line `endline` in file `filepath`.
48+
* For more information, see
49+
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
50+
*/
51+
predicate hasLocationInfo(string filepath,
52+
int startline, int startcolumn,
53+
int endline, int endcolumn) {
54+
filepath = this.getFile().toString() and
55+
startline = this.getStartLine() and
56+
startcolumn = 0 and
57+
endline = this.getEndLine() and
58+
endcolumn = 0
59+
}
60+
61+
string toString() {
62+
result = mkElement(this).toString()
63+
}
64+
65+
cached File getFile() {
66+
result = mkElement(this).getFile()
67+
}
68+
69+
cached int getStartLine() {
70+
result = mkElement(this).getLocation().getStartLine()
71+
}
72+
73+
cached int getEndLine() {
74+
result = mkElement(this).(File).getMetrics().getNumberOfLines() or
75+
result = mkElement(this).(PreprocessorBranchDirective).getNext().getLocation().getStartLine() - 1
76+
}
77+
78+
private cached PreprocessorBlock getParentInternal() {
79+
// find the `#ifdef` corresponding to this block and the
80+
// PreprocessorBranchDirective `prev` that came directly
81+
// before it in the source.
82+
exists(int ix, PreprocessorBranchDirective prev |
83+
ix = getPreprocIndex(mkElement(this).(PreprocessorBranchDirective).getIf()) and
84+
prev = getPreprocFromIndex(getFile(), ix - 1) |
85+
if (prev instanceof PreprocessorEndif) then (
86+
// if we follow an #endif, we have the same parent
87+
// as its corresponding `#if` has.
88+
result = unresolveElement(prev.getIf()).(PreprocessorBlock).getParentInternal()
89+
) else (
90+
// otherwise we directly follow an #if / #ifdef / #ifndef /
91+
// #elif / #else that must be a level above and our parent
92+
// block.
93+
mkElement(result) = prev
94+
)
95+
)
96+
}
97+
98+
/**
99+
* Gets the `PreprocessorBlock` that's directly surrounding this one.
100+
* Has no result if this is a file.
101+
*/
102+
PreprocessorBlock getParent() {
103+
not mkElement(this) instanceof File and
104+
(
105+
if exists(getParentInternal()) then (
106+
// found parent directive
107+
result = getParentInternal()
108+
) else (
109+
// top level directive
110+
mkElement(result) = getFile()
111+
)
112+
)
113+
}
114+
115+
/**
116+
* Gets a `PreprocessorBlock` that's directly inside this one.
117+
*/
118+
PreprocessorBlock getAChild() {
119+
result.getParent() = this
120+
}
121+
122+
private Include getAnEnclosedInclude() {
123+
result.getFile() = getFile() and
124+
result.getLocation().getStartLine() > getStartLine() and
125+
result.getLocation().getStartLine() <= getEndLine()
126+
}
127+
128+
/**
129+
* Gets an include directive that is directly in this
130+
* `PreprocessorBlock`.
131+
*/
132+
Include getAnInclude() {
133+
result = getAnEnclosedInclude() and
134+
not result = getAChild().getAnEnclosedInclude()
135+
}
136+
137+
private Macro getAnEnclosedMacro() {
138+
result.getFile() = getFile() and
139+
result.getLocation().getStartLine() > getStartLine() and
140+
result.getLocation().getStartLine() <= getEndLine()
141+
}
142+
143+
/**
144+
* Gets a macro definition that is directly in this
145+
* `PreprocessorBlock`.
146+
*/
147+
Macro getAMacro() {
148+
result = getAnEnclosedMacro() and
149+
not result = getAChild().getAnEnclosedMacro()
150+
}
151+
}

0 commit comments

Comments
 (0)