mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
331 lines
9.6 KiB
Plaintext
331 lines
9.6 KiB
Plaintext
import semmle.code.cpp.Location
|
|
import semmle.code.cpp.Element
|
|
|
|
/**
|
|
* A C/C++ preprocessor directive. For example each of the following lines of
|
|
* code contains a `PreprocessorDirective`:
|
|
* ```
|
|
* #pragma once
|
|
* #ifdef MYDEFINE
|
|
* #include "myfile.h"
|
|
* #line 1 "source.c"
|
|
* ```
|
|
*/
|
|
class PreprocessorDirective extends Locatable, @preprocdirect {
|
|
override string toString() { result = "Preprocessor directive" }
|
|
|
|
override Location getLocation() { preprocdirects(underlyingElement(this), _, result) }
|
|
|
|
string getHead() { preproctext(underlyingElement(this), result, _) }
|
|
|
|
/**
|
|
* Gets a preprocessor branching directive whose condition affects
|
|
* whether this directive is performed.
|
|
*
|
|
* From a lexical point of view, this returns all `#if`, `#ifdef`,
|
|
* `#ifndef`, or `#elif` directives which occur before this directive and
|
|
* have a matching `#endif` which occurs after this directive.
|
|
*/
|
|
PreprocessorBranch getAGuard() {
|
|
exists(PreprocessorEndif e, int line |
|
|
result.getEndIf() = e and
|
|
e.getFile() = this.getFile() and
|
|
result.getFile() = this.getFile() and
|
|
line = this.getLocation().getStartLine() and
|
|
result.getLocation().getStartLine() < line and
|
|
line < e.getLocation().getEndLine()
|
|
)
|
|
}
|
|
}
|
|
|
|
private class TPreprocessorBranchDirective = @ppd_branch or @ppd_else or @ppd_endif;
|
|
|
|
/**
|
|
* A C/C++ preprocessor branch related directive: `#if`, `#ifdef`,
|
|
* `#ifndef`, `#elif`, `#elifdef`, `#elifndef`, `#else` or `#endif`.
|
|
*/
|
|
class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBranchDirective {
|
|
/**
|
|
* Gets the `#if`, `#ifdef` or `#ifndef` directive which matches this
|
|
* branching directive.
|
|
*
|
|
* If this branch directive was unbalanced, then there will be no
|
|
* result. Conversely, if the branch matches different `#if` directives
|
|
* in different translation units, then there can be more than one
|
|
* result.
|
|
*/
|
|
PreprocessorBranch getIf() {
|
|
result = this.(PreprocessorIf) or
|
|
result = this.(PreprocessorIfdef) or
|
|
result = this.(PreprocessorIfndef) or
|
|
preprocpair(unresolveElement(result), underlyingElement(this))
|
|
}
|
|
|
|
/**
|
|
* Gets the `#endif` directive which matches this branching directive.
|
|
*
|
|
* If this branch directive was unbalanced, then there will be no
|
|
* result. Conversely, if the branch matched different `#endif`
|
|
* directives in different translation units, then there can be more than
|
|
* one result.
|
|
*/
|
|
PreprocessorEndif getEndIf() {
|
|
preprocpair(unresolveElement(this.getIf()), unresolveElement(result))
|
|
}
|
|
|
|
/**
|
|
* Gets the next `#elif`, `#elifdef`, `#elifndef`, `#else` or `#endif` matching
|
|
* this branching directive.
|
|
*
|
|
* For example `somePreprocessorBranchDirective.getIf().getNext()` gets
|
|
* the second directive in the same construct as
|
|
* `somePreprocessorBranchDirective`.
|
|
*/
|
|
PreprocessorBranchDirective getNext() {
|
|
exists(PreprocessorBranch branch |
|
|
this.getIndexInBranch(branch) + 1 = result.getIndexInBranch(branch)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the index of this branching directive within the matching `#if`,
|
|
* `#ifdef` or `#ifndef`.
|
|
*/
|
|
private int getIndexInBranch(PreprocessorBranch branch) {
|
|
this =
|
|
rank[result](PreprocessorBranchDirective other |
|
|
other.getIf() = branch
|
|
|
|
|
other order by other.getLocation().getStartLine()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`,
|
|
* `#elif`, `#elifdef`, or `#elifndef`.
|
|
*
|
|
* A branching directive has a condition and that condition may be evaluated
|
|
* at compile-time. As a result, the preprocessor will either take the
|
|
* branch, or not take the branch.
|
|
*
|
|
* However, there are also situations in which a branch's condition isn't
|
|
* evaluated. The obvious case of this is when the directive is contained
|
|
* within a branch which wasn't taken. There is also a much more subtle
|
|
* case involving header guard branches: suitably clever compilers can
|
|
* notice that a branch is a header guard, and can then subsequently ignore
|
|
* a `#include` for the file being guarded. It is for this reason that
|
|
* `wasTaken()` always holds on header guard branches, but `wasNotToken()`
|
|
* rarely holds on header guard branches.
|
|
*/
|
|
class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch {
|
|
/**
|
|
* Holds if at least one translation unit evaluated this directive's
|
|
* condition and subsequently took the branch.
|
|
*/
|
|
predicate wasTaken() { preproctrue(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Holds if at least one translation unit evaluated this directive's
|
|
* condition but then didn't take the branch.
|
|
*
|
|
* If `#else` is the next matching directive, then this means that the
|
|
* `#else` was taken instead.
|
|
*/
|
|
predicate wasNotTaken() { preprocfalse(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Holds if this directive was either taken by all translation units
|
|
* which evaluated it, or was not taken by any translation unit which
|
|
* evaluated it.
|
|
*/
|
|
predicate wasPredictable() { not (this.wasTaken() and this.wasNotTaken()) }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#if` directive. For example there is a
|
|
* `PreprocessorIf` on the first line of the following code:
|
|
* ```
|
|
* #if defined(MYDEFINE)
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
* For the related notion of a directive which causes branching (which
|
|
* includes `#if`, plus also `#ifdef`, `#ifndef`, `#elif`, `#elifdef`,
|
|
* and `#elifndef`), see `PreprocessorBranch`.
|
|
*/
|
|
class PreprocessorIf extends PreprocessorBranch, @ppd_if {
|
|
override string toString() { result = "#if " + this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#ifdef` directive. For example there is a
|
|
* `PreprocessorIfdef` on the first line of the following code:
|
|
* ```
|
|
* #ifdef MYDEFINE
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
* The syntax `#ifdef X` is shorthand for `#if defined(X)`.
|
|
*/
|
|
class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
|
|
override string toString() { result = "#ifdef " + this.getHead() }
|
|
|
|
override string getAPrimaryQlClass() { result = "PreprocessorIfdef" }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#ifndef` directive. For example there is a
|
|
* `PreprocessorIfndef` on the first line of the following code:
|
|
* ```
|
|
* #ifndef MYDEFINE
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
* The syntax `#ifndef X` is shorthand for `#if !defined(X)`.
|
|
*/
|
|
class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
|
|
override string toString() { result = "#ifndef " + this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#else` directive. For example there is a
|
|
* `PreprocessorElse` on the fifth line of the following code:
|
|
* ```
|
|
* #ifdef MYDEFINE1
|
|
* // ...
|
|
* #elif MYDEFINE2
|
|
* // ...
|
|
* #else
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
*/
|
|
class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else {
|
|
override string toString() { result = "#else" }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#elif` directive. For example there is a
|
|
* `PreprocessorElif` on the third line of the following code:
|
|
* ```
|
|
* #ifdef MYDEFINE1
|
|
* // ...
|
|
* #elif MYDEFINE2
|
|
* // ...
|
|
* #else
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
*/
|
|
class PreprocessorElif extends PreprocessorBranch, @ppd_elif {
|
|
override string toString() { result = "#elif " + this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#elifdef` directive. For example there is a
|
|
* `PreprocessorElifdef` on the third line of the following code:
|
|
* ```
|
|
* #ifdef MYDEFINE1
|
|
* // ...
|
|
* #elifdef MYDEFINE2
|
|
* // ...
|
|
* #else
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
*/
|
|
class PreprocessorElifdef extends PreprocessorBranch, @ppd_elifdef {
|
|
override string toString() { result = "#elifdef " + this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#elifndef` directive. For example there is a
|
|
* `PreprocessorElifndef` on the third line of the following code:
|
|
* ```
|
|
* #ifdef MYDEFINE1
|
|
* // ...
|
|
* #elifndef MYDEFINE2
|
|
* // ...
|
|
* #else
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
*/
|
|
class PreprocessorElifndef extends PreprocessorBranch, @ppd_elifndef {
|
|
override string toString() { result = "#elifndef " + this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#endif` directive. For example there is a
|
|
* `PreprocessorEndif` on the third line of the following code:
|
|
* ```
|
|
* #ifdef MYDEFINE
|
|
* // ...
|
|
* #endif
|
|
* ```
|
|
*/
|
|
class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif {
|
|
override string toString() { result = "#endif" }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#warning` directive. For example:
|
|
* ```
|
|
* #warning "This configuration is not supported."
|
|
* ```
|
|
*/
|
|
class PreprocessorWarning extends PreprocessorDirective, @ppd_warning {
|
|
override string toString() { result = "#warning " + this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#error` directive. For example:
|
|
* ```
|
|
* #error "This configuration is not implemented."
|
|
* ```
|
|
*/
|
|
class PreprocessorError extends PreprocessorDirective, @ppd_error {
|
|
override string toString() { result = "#error " + this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#undef` directive. For example there is a
|
|
* `PreprocessorUndef` on the second line of the following code:
|
|
* ```
|
|
* #ifdef MYMACRO
|
|
* #undef MYMACRO
|
|
* #endif
|
|
* ```
|
|
*/
|
|
class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
|
|
override string toString() { result = "#undef " + this.getHead() }
|
|
|
|
/**
|
|
* Gets the name of the macro that is undefined.
|
|
*/
|
|
string getName() { result = this.getHead() }
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#pragma` directive. For example:
|
|
* ```
|
|
* #pragma once
|
|
* ```
|
|
*/
|
|
class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
|
|
override string toString() {
|
|
if exists(this.getHead()) then result = "#pragma " + this.getHead() else result = "#pragma"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A C/C++ preprocessor `#line` directive. For example:
|
|
* ```
|
|
* #line 1 "source.c"
|
|
* ```
|
|
*/
|
|
class PreprocessorLine extends PreprocessorDirective, @ppd_line {
|
|
override string toString() { result = "#line " + this.getHead() }
|
|
}
|