Skip to content

Commit b46dfc0

Browse files
authored
QL: Merge pull request github#99 from github/missing-noinline
Add query: Missing `noinline`
2 parents 8e1494b + 7bcc906 commit b46dfc0

File tree

4 files changed

+289
-155
lines changed

4 files changed

+289
-155
lines changed

ql/src/codeql_ql/ast/Ast.qll

Lines changed: 115 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@ private string stringIndexedMember(string name, string index) {
1616
result = name + "(_)" and exists(index)
1717
}
1818

19-
/**
20-
* Holds if `node` has an annotation with `name`.
21-
*/
22-
private predicate hasAnnotation(AstNode node, string name) {
23-
exists(Generated::Annotation annotation | annotation.getName().getValue() = name |
24-
toGenerated(node).getParent() = annotation.getParent()
25-
)
26-
}
27-
2819
/** An AST node of a QL program */
2920
class AstNode extends TAstNode {
3021
string toString() { result = getAPrimaryQlClass() }
@@ -60,6 +51,12 @@ class AstNode extends TAstNode {
6051
/** Gets the QLDoc comment for this AST node, if any. */
6152
QLDoc getQLDoc() { none() }
6253

54+
/** Holds if `node` has an annotation with `name`. */
55+
predicate hasAnnotation(string name) { this.getAnAnnotation().getName() = name }
56+
57+
/** Gets an annotation of this AST node. */
58+
Annotation getAnAnnotation() { toGenerated(this).getParent() = toGenerated(result).getParent() }
59+
6360
/**
6461
* Gets the predicate that contains this AST node.
6562
*/
@@ -400,12 +397,12 @@ class ClassPredicate extends TClassPredicate, Predicate {
400397
/**
401398
* Holds if this predicate is private.
402399
*/
403-
predicate isPrivate() { hasAnnotation(this, "private") }
400+
predicate isPrivate() { hasAnnotation("private") }
404401

405402
/**
406403
* Holds if this predicate is annotated as overriding another predicate.
407404
*/
408-
predicate isOverride() { hasAnnotation(this, "override") }
405+
predicate isOverride() { hasAnnotation("override") }
409406

410407
override VarDecl getParameter(int i) {
411408
toGenerated(result) =
@@ -618,7 +615,7 @@ class Module extends TModule, ModuleDeclaration {
618615
*/
619616
class ModuleMember extends TModuleMember, AstNode {
620617
/** Holds if this member is declared as `private`. */
621-
predicate isPrivate() { hasAnnotation(this, "private") }
618+
predicate isPrivate() { hasAnnotation("private") }
622619
}
623620

624621
/** A declaration. E.g. a class, type, predicate, newtype... */
@@ -2090,6 +2087,112 @@ class ModuleExpr extends TModuleExpr, ModuleRef {
20902087
}
20912088
}
20922089

2090+
/** An argument to an annotation. */
2091+
private class AnnotationArg extends TAnnotationArg, AstNode {
2092+
Generated::AnnotArg arg;
2093+
2094+
AnnotationArg() { this = TAnnotationArg(arg) }
2095+
2096+
/** Gets the name of this argument. */
2097+
string getValue() {
2098+
result =
2099+
[
2100+
arg.getChild().(Generated::SimpleId).getValue(),
2101+
arg.getChild().(Generated::Result).getValue(), arg.getChild().(Generated::This).getValue()
2102+
]
2103+
}
2104+
2105+
override string toString() { result = this.getValue() }
2106+
}
2107+
2108+
private class NoInlineArg extends AnnotationArg {
2109+
NoInlineArg() { this.getValue() = "noinline" }
2110+
}
2111+
2112+
private class NoMagicArg extends AnnotationArg {
2113+
NoMagicArg() { this.getValue() = "nomagic" }
2114+
}
2115+
2116+
private class InlineArg extends AnnotationArg {
2117+
InlineArg() { this.getValue() = "inline" }
2118+
}
2119+
2120+
private class NoOptArg extends AnnotationArg {
2121+
NoOptArg() { this.getValue() = "noopt" }
2122+
}
2123+
2124+
private class MonotonicAggregatesArg extends AnnotationArg {
2125+
MonotonicAggregatesArg() { this.getValue() = "monotonicAggregates" }
2126+
}
2127+
2128+
/** An annotation on an element. */
2129+
class Annotation extends TAnnotation, AstNode {
2130+
Generated::Annotation annot;
2131+
2132+
Annotation() { this = TAnnotation(annot) }
2133+
2134+
override string toString() { result = "annotation" }
2135+
2136+
override string getAPrimaryQlClass() { result = "Annotation" }
2137+
2138+
override Location getLocation() { result = annot.getLocation() }
2139+
2140+
/** Gets the node corresponding to the field `args`. */
2141+
AnnotationArg getArgs(int i) { toGenerated(result) = annot.getArgs(i) }
2142+
2143+
/** Gets the node corresponding to the field `name`. */
2144+
string getName() { result = annot.getName().getValue() }
2145+
}
2146+
2147+
/** A `pragma[noinline]` annotation. */
2148+
class NoInline extends Annotation {
2149+
NoInline() { this.getArgs(0) instanceof NoInlineArg }
2150+
2151+
override string toString() { result = "noinline" }
2152+
}
2153+
2154+
/** A `pragma[inline]` annotation. */
2155+
class Inline extends Annotation {
2156+
Inline() { this.getArgs(0) instanceof InlineArg }
2157+
2158+
override string toString() { result = "inline" }
2159+
}
2160+
2161+
/** A `pragma[nomagic]` annotation. */
2162+
class NoMagic extends Annotation {
2163+
NoMagic() { this.getArgs(0) instanceof NoMagicArg }
2164+
2165+
override string toString() { result = "nomagic" }
2166+
}
2167+
2168+
/** A `pragma[noopt]` annotation. */
2169+
class NoOpt extends Annotation {
2170+
NoOpt() { this.getArgs(0) instanceof NoOptArg }
2171+
2172+
override string toString() { result = "noopt" }
2173+
}
2174+
2175+
/** A `language[monotonicAggregates]` annotation. */
2176+
class MonotonicAggregates extends Annotation {
2177+
MonotonicAggregates() { this.getArgs(0) instanceof MonotonicAggregatesArg }
2178+
2179+
override string toString() { result = "monotonicaggregates" }
2180+
}
2181+
2182+
/** A `bindingset` annotation. */
2183+
class BindingSet extends Annotation {
2184+
BindingSet() { this.getName() = "bindingset" }
2185+
2186+
/** Gets the `index`'th bound name in this bindingset. */
2187+
string getBoundName(int index) { result = this.getArgs(index).getValue() }
2188+
2189+
/** Gets a name bound by this bindingset, if any. */
2190+
string getABoundName() { result = getBoundName(_) }
2191+
2192+
/** Gets the number of names bound by this bindingset. */
2193+
int getNumberOfBoundNames() { result = count(getABoundName()) }
2194+
}
2195+
20932196
/**
20942197
* Classes modelling YAML AST nodes.
20952198
*/

ql/src/codeql_ql/ast/internal/AstNodes.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ newtype TAstNode =
5959
TDontCare(Generated::Underscore dontcare) or
6060
TModuleExpr(Generated::ModuleExpr me) or
6161
TPredicateExpr(Generated::PredicateExpr pe) or
62+
TAnnotation(Generated::Annotation annot) or
63+
TAnnotationArg(Generated::AnnotArg arg) or
6264
TYamlCommemt(Generated::YamlComment yc) or
6365
TYamlEntry(Generated::YamlEntry ye) or
6466
TYamlKey(Generated::YamlKey yk) or
@@ -184,6 +186,10 @@ Generated::AstNode toGenerated(AST::AstNode n) {
184186
n = TAnyCall(result)
185187
or
186188
n = TSuper(result)
189+
or
190+
n = TAnnotation(result)
191+
or
192+
n = TAnnotationArg(result)
187193
}
188194

189195
class TPredicate = TCharPred or TClasslessPredicate or TClassPredicate or TDBRelation;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @name Missing `noinline` or `nomagic` annotation
3+
* @description When a predicate is factored out to improve join-ordering, it should be marked as `noinline` or `nomagic`.
4+
* @kind problem
5+
* @problem.severity error
6+
* @id ql/missing-noinline
7+
* @tags performance
8+
* @precision high
9+
*/
10+
11+
import ql
12+
13+
from QLDoc doc, Predicate decl
14+
where
15+
doc.getContents().matches(["%join order%", "%join-order%"]) and
16+
decl.getQLDoc() = doc and
17+
not decl.getAnAnnotation() instanceof NoInline and
18+
not decl.getAnAnnotation() instanceof NoMagic and
19+
not decl.getAnAnnotation() instanceof NoOpt and
20+
// If it's marked as inline it's probably because the QLDoc says something like
21+
// "this predicate is inlined because it gives a better join-order".
22+
not decl.getAnAnnotation() instanceof Inline
23+
select decl, "This predicate might be inlined."

0 commit comments

Comments
 (0)