mirror of
https://github.com/github/codeql.git
synced 2025-12-18 18:10:39 +01:00
Evaluated non-recursive predicate Element::Element.getFile/0#dispred#536cb5f3#bb@f6f5329i in 182326ms (size: 50437).
Evaluated relational algebra for predicate Element::Element.getFile/0#dispred#536cb5f3#bb@f6f5329i with tuple counts:
2029351 ~0% {2} r1 = SCAN `Expr::Expr.getLocation/0#dispred#0a3d90c6` OUTPUT In.1, In.0
2029351 ~0% {2} | JOIN WITH `Location::Location.getStartLine/0#d54f9e6c` ON FIRST 1 OUTPUT Rhs.1, Lhs.1
1168789 ~0% {2} | JOIN WITH `InconsistentCheckReturnNull::assertInvocation/2#b2a4c9e3_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1
5533128288 ~0% {3} | JOIN WITH `Location::Location.getContainer/0#9edabfb6_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0
50413 ~0% {2} | JOIN WITH `Element::Element.getLocation/0#dispred#6c3f5b09#bf` ON FIRST 2 OUTPUT Lhs.0, Lhs.2
3043 ~0% {2} r2 = JOIN `project#InconsistentCheckReturnNull::relevantFunctionCall/2#d18cd566` WITH `Expr::Expr.getLocation/0#dispred#0a3d90c6` ON FIRST 1 OUTPUT Rhs.1, Lhs.0
3043 ~0% {2} r3 = JOIN r2 WITH locations_default ON FIRST 1 OUTPUT Rhs.4, Lhs.1
1945 ~3% {2} | JOIN WITH `InconsistentCheckReturnNull::assertInvocation/2#b2a4c9e3_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1
9106248 ~2% {3} | JOIN WITH `Location::Location.getContainer/0#9edabfb6_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0
0 ~0% {2} | JOIN WITH `Element::Element.getLocation/0#dispred#6c3f5b09#bf` ON FIRST 2 OUTPUT Lhs.0, Lhs.2
3043 ~0% {3} r4 = JOIN r2 WITH locations_default ON FIRST 1 OUTPUT _, Lhs.1, Rhs.4
3043 ~0% {2} | REWRITE WITH Tmp.0 := 1, Out.0 := (In.2 + Tmp.0) KEEPING 2
2013 ~0% {2} | JOIN WITH `InconsistentCheckReturnNull::assertInvocation/2#b2a4c9e3_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1
9621327 ~0% {3} | JOIN WITH `Location::Location.getContainer/0#9edabfb6_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.0
24 ~3% {2} | JOIN WITH `Element::Element.getLocation/0#dispred#6c3f5b09#bf` ON FIRST 2 OUTPUT Lhs.0, Lhs.2
50437 ~0% {2} r5 = r1 UNION r3 UNION r4
return r5
311 lines
11 KiB
Plaintext
311 lines
11 KiB
Plaintext
/**
|
|
* Provides the `Element` class, which is the base class for all classes representing C or C++
|
|
* program elements.
|
|
*/
|
|
|
|
import semmle.code.cpp.Location
|
|
private import semmle.code.cpp.Enclosing
|
|
private import semmle.code.cpp.internal.ResolveClass
|
|
private import semmle.code.cpp.internal.ResolveGlobalVariable
|
|
private import semmle.code.cpp.internal.ResolveFunction
|
|
|
|
/**
|
|
* Get the `Element` that represents this `@element`.
|
|
* Normally this will simply be a cast of `e`, but sometimes it is not.
|
|
* For example, for an incomplete struct `e` the result may be a
|
|
* complete struct with the same name.
|
|
*/
|
|
pragma[inline]
|
|
Element mkElement(@element e) { unresolveElement(result) = e }
|
|
|
|
/**
|
|
* INTERNAL: Do not use.
|
|
*
|
|
* Gets an `@element` that resolves to the `Element`. This should
|
|
* normally only be called from member predicates, where `e` is not
|
|
* `this` and you need the result for an argument to a database
|
|
* extensional.
|
|
* See `underlyingElement` for when `e` is `this`.
|
|
*/
|
|
pragma[inline]
|
|
@element unresolveElement(Element e) {
|
|
not result instanceof @usertype and
|
|
not result instanceof @variable and
|
|
not result instanceof @function and
|
|
result = e
|
|
or
|
|
e = resolveClass(result)
|
|
or
|
|
e = resolveGlobalVariable(result)
|
|
or
|
|
e = resolveFunction(result)
|
|
}
|
|
|
|
/**
|
|
* INTERNAL: Do not use.
|
|
*
|
|
* Gets the `@element` that this `Element` extends. This should normally
|
|
* only be called from member predicates, where `e` is `this` and you
|
|
* need the result for an argument to a database extensional.
|
|
* See `unresolveElement` for when `e` is not `this`.
|
|
*/
|
|
@element underlyingElement(Element e) { result = e }
|
|
|
|
/**
|
|
* A C/C++ element with no member predicates other than `toString`. Not for
|
|
* general use. This class does not define a location, so classes wanting to
|
|
* change their location without affecting other classes can extend
|
|
* `ElementBase` instead of `Element` to create a new rootdef for `getURL`,
|
|
* `getLocation`, or `hasLocationInfo`.
|
|
*/
|
|
class ElementBase extends @element {
|
|
/** Gets a textual representation of this element. */
|
|
cached
|
|
string toString() { none() }
|
|
|
|
/**
|
|
* Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
|
|
*/
|
|
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
|
|
|
|
/**
|
|
* Gets the name of a primary CodeQL class to which this element belongs.
|
|
*
|
|
* For most elements, this is simply the most precise syntactic category to
|
|
* which they belong; for example, `AddExpr` is a primary class, but
|
|
* `BinaryOperation` is not.
|
|
*
|
|
* This predicate can have multiple results if multiple primary classes match.
|
|
* For some elements, this predicate may not have a result.
|
|
*/
|
|
string getAPrimaryQlClass() { none() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ element. This class is the base class for all C/C++
|
|
* elements, such as functions, classes, expressions, and so on.
|
|
*/
|
|
class Element extends ElementBase {
|
|
/** Gets the primary file where this element occurs. */
|
|
pragma[nomagic]
|
|
File getFile() { result = this.getLocation().getFile() }
|
|
|
|
/**
|
|
* Holds if this element may be from source. This predicate holds for all
|
|
* elements, except for those in the dummy file, whose name is the empty string.
|
|
* The dummy file contains declarations that are built directly into the compiler.
|
|
*/
|
|
predicate fromSource() { this.getFile().fromSource() }
|
|
|
|
/** Gets the primary location of this element. */
|
|
Location getLocation() { none() }
|
|
|
|
/**
|
|
* Gets the source of this element: either itself or a macro that expanded
|
|
* to this element.
|
|
*
|
|
* If the element is not in a macro expansion, then the "root" is just
|
|
* the element itself. Otherwise, it is the definition of the innermost
|
|
* macro whose expansion the element is in.
|
|
*
|
|
* This method is useful for filtering macro results in checks: simply
|
|
* blame `e.findRootCause` rather than `e`. This will report only bugs
|
|
* that are not in macros, and in addition report macros that (somewhere)
|
|
* expand to a bug.
|
|
*/
|
|
Element findRootCause() {
|
|
if exists(MacroInvocation mi | this = mi.getAGeneratedElement())
|
|
then
|
|
exists(MacroInvocation mi |
|
|
this = mi.getAGeneratedElement() and
|
|
not hasCloserMacroInvocation(this, mi) and
|
|
result = mi.getMacro()
|
|
)
|
|
else result = this
|
|
}
|
|
|
|
/**
|
|
* Gets the parent scope of this `Element`, if any.
|
|
* A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`,
|
|
* or certain kinds of `Statement`.
|
|
*/
|
|
Element getParentScope() {
|
|
// result instanceof Class
|
|
exists(Declaration m |
|
|
m = this and
|
|
result = m.getDeclaringType() and
|
|
not this instanceof EnumConstant
|
|
)
|
|
or
|
|
exists(TemplateClass tc | this = tc.getATemplateArgument() and result = tc)
|
|
or
|
|
// result instanceof Namespace
|
|
exists(Namespace n | result = n and n.getADeclaration() = this)
|
|
or
|
|
exists(FriendDecl d, Namespace n | this = d and n.getADeclaration() = d and result = n)
|
|
or
|
|
exists(Namespace n | this = n and result = n.getParentNamespace())
|
|
or
|
|
// result instanceof Stmt
|
|
exists(LocalVariable v |
|
|
this = v and
|
|
exists(DeclStmt ds | ds.getADeclaration() = v and result = ds.getParent())
|
|
)
|
|
or
|
|
exists(Parameter p |
|
|
this = p and
|
|
(
|
|
result = p.getFunction() or
|
|
result = p.getCatchBlock().getParent().(Handler).getParent().(TryStmt).getParent() or
|
|
result = p.getRequiresExpr().getEnclosingStmt().getParent()
|
|
)
|
|
)
|
|
or
|
|
exists(GlobalVariable g, Namespace n | this = g and n.getADeclaration() = g and result = n)
|
|
or
|
|
exists(TemplateVariable tv | this = tv.getATemplateArgument() and result = tv)
|
|
or
|
|
exists(EnumConstant e | this = e and result = e.getDeclaringEnum())
|
|
or
|
|
// result instanceof Block|Function
|
|
exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
|
|
or
|
|
exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf)
|
|
or
|
|
// result instanceof Stmt
|
|
exists(ControlStructure s | this = s and result = s.getParent())
|
|
or
|
|
using_container(unresolveElement(result), underlyingElement(this))
|
|
}
|
|
|
|
/**
|
|
* Holds if this element comes from a macro expansion. Only elements that
|
|
* are entirely generated by a macro are included - for elements that
|
|
* partially come from a macro, see `isAffectedByMacro`.
|
|
*/
|
|
predicate isInMacroExpansion() { inMacroExpansion(this) }
|
|
|
|
/**
|
|
* Holds if this element is affected in any way by a macro. All elements
|
|
* that are totally or partially generated by a macro are included, so
|
|
* this is a super-set of `isInMacroExpansion`.
|
|
*/
|
|
predicate isAffectedByMacro() { affectedByMacro(this) }
|
|
|
|
private Element getEnclosingElementPref() {
|
|
enclosingfunction(underlyingElement(this), unresolveElement(result)) or
|
|
result.(Function) = stmtEnclosingElement(this) or
|
|
this.(LocalScopeVariable).getFunction() = result or
|
|
enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) or
|
|
derivations(underlyingElement(this), unresolveElement(result), _, _, _) or
|
|
stmtparents(underlyingElement(this), _, unresolveElement(result)) or
|
|
exprparents(underlyingElement(this), _, unresolveElement(result)) or
|
|
namequalifiers(underlyingElement(this), unresolveElement(result), _, _) or
|
|
initialisers(underlyingElement(this), unresolveElement(result), _, _) or
|
|
exprconv(unresolveElement(result), underlyingElement(this)) or
|
|
param_decl_bind(underlyingElement(this), _, unresolveElement(result)) or
|
|
using_container(unresolveElement(result), underlyingElement(this)) or
|
|
static_asserts(unresolveElement(this), _, _, _, underlyingElement(result))
|
|
}
|
|
|
|
/** Gets the closest `Element` enclosing this one. */
|
|
cached
|
|
Element getEnclosingElement() {
|
|
result = this.getEnclosingElementPref()
|
|
or
|
|
not exists(this.getEnclosingElementPref()) and
|
|
(
|
|
this = result.(Class).getAMember()
|
|
or
|
|
result = exprEnclosingElement(this)
|
|
or
|
|
var_decls(underlyingElement(this), unresolveElement(result), _, _, _)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this `Element` is a part of a template instantiation (but not
|
|
* the template itself).
|
|
*/
|
|
predicate isFromTemplateInstantiation(Element instantiation) {
|
|
exists(Element e | isFromTemplateInstantiationRec(e, instantiation) |
|
|
this = e or
|
|
this.(DeclarationEntry).getDeclaration() = e
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this `Element` is part of a template `template` (not if it is
|
|
* part of an instantiation of `template`). This means it is represented in
|
|
* the database purely as syntax and without guarantees on the presence or
|
|
* correctness of type-based operations such as implicit conversions.
|
|
*
|
|
* If an element is nested within several templates, this predicate holds with
|
|
* a value of `template` for each containing template.
|
|
*/
|
|
predicate isFromUninstantiatedTemplate(Element template) {
|
|
exists(Element e | isFromUninstantiatedTemplateRec(e, template) |
|
|
this = e or
|
|
this.(DeclarationEntry).getDeclaration() = e
|
|
)
|
|
}
|
|
}
|
|
|
|
pragma[noinline]
|
|
private predicate hasCloserMacroInvocation(Element elem, MacroInvocation mi) {
|
|
exists(MacroInvocation closer |
|
|
elem = closer.getAGeneratedElement() and
|
|
mi = closer.getParentInvocation()
|
|
)
|
|
}
|
|
|
|
private predicate isFromTemplateInstantiationRec(Element e, Element instantiation) {
|
|
instantiation.(Function).isConstructedFrom(_) and
|
|
e = instantiation
|
|
or
|
|
instantiation.(Class).isConstructedFrom(_) and
|
|
e = instantiation
|
|
or
|
|
instantiation.(Variable).isConstructedFrom(_) and
|
|
e = instantiation
|
|
or
|
|
isFromTemplateInstantiationRec(e.getEnclosingElement(), instantiation)
|
|
}
|
|
|
|
private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
|
|
is_class_template(unresolveElement(template)) and
|
|
e = template
|
|
or
|
|
is_function_template(unresolveElement(template)) and
|
|
e = template
|
|
or
|
|
is_variable_template(unresolveElement(template)) and
|
|
e = template
|
|
or
|
|
isFromUninstantiatedTemplateRec(e.getEnclosingElement(), template)
|
|
}
|
|
|
|
/**
|
|
* A C++11 `static_assert` or C11 `_Static_assert` construct. For example each
|
|
* line in the following example contains a static assert:
|
|
* ```
|
|
* static_assert(sizeof(MyStruct) <= 4096);
|
|
* static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!");
|
|
* ```
|
|
*/
|
|
class StaticAssert extends Locatable, @static_assert {
|
|
override string toString() { result = "static_assert(..., \"" + this.getMessage() + "\")" }
|
|
|
|
/**
|
|
* Gets the expression which this static assertion ensures is true.
|
|
*/
|
|
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _, _) }
|
|
|
|
/**
|
|
* Gets the message which will be reported by the compiler if this static assertion fails.
|
|
*/
|
|
string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) }
|
|
|
|
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) }
|
|
}
|