Files
codeql/cpp/ql/lib/semmle/code/cpp/Location.qll
2023-01-13 11:42:15 +00:00

173 lines
6.0 KiB
Plaintext

/**
* Provides classes and predicates for locations in the source code.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.File
/**
* A location of a C/C++ artifact.
*/
class Location extends @location {
/** Gets the container corresponding to this location. */
pragma[nomagic]
Container getContainer() { this.fullLocationInfo(result, _, _, _, _) }
/** Gets the file corresponding to this location, if any. */
File getFile() { result = this.getContainer() }
/** Gets the 1-based line number (inclusive) where this location starts. */
pragma[nomagic]
int getStartLine() { this.fullLocationInfo(_, result, _, _, _) }
/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) }
/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { this.fullLocationInfo(_, _, _, result, _) }
/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) }
/**
* Gets a textual representation of this element.
*
* The format is "file://filePath:startLine:startColumn:endLine:endColumn".
*/
string toString() {
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
toUrl(filepath, startline, startcolumn, endline, endcolumn, result)
)
}
/**
* Holds if this element is in the specified container.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline`.
*
* This predicate is similar to `hasLocationInfo`, but exposes the `Container`
* entity, rather than merely its path.
*/
predicate fullLocationInfo(
Container container, int startline, int startcolumn, int endline, int endcolumn
) {
locations_default(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_expr(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or
locations_stmt(this, unresolveElement(container), startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(Container f | this.fullLocationInfo(f, startline, startcolumn, endline, endcolumn) |
filepath = f.getAbsolutePath()
)
}
/** Holds if `this` comes on a line strictly before `l`. */
pragma[inline]
predicate isBefore(Location l) { this.isBefore(l, false) }
/**
* Holds if `this` comes strictly before `l`. The boolean `sameLine` is
* true if `l` is on the same line as `this`, but starts at a later column.
* Otherwise, `sameLine` is false.
*/
pragma[inline]
predicate isBefore(Location l, boolean sameLine) {
this.getFile() = l.getFile() and
(
sameLine = false and
this.getEndLine() < l.getStartLine()
or
sameLine = true and
this.getEndLine() = l.getStartLine() and
this.getEndColumn() < l.getStartColumn()
)
}
/** Holds if location `l` is completely contained within this one. */
predicate subsumes(Location l) {
exists(File f | f = this.getFile() |
exists(int thisStart, int thisEnd | this.charLoc(f, thisStart, thisEnd) |
exists(int lStart, int lEnd | l.charLoc(f, lStart, lEnd) |
thisStart <= lStart and lEnd <= thisEnd
)
)
)
}
/**
* Holds if this location corresponds to file `f` and character "offsets"
* `start..end`. Note that these are not real character offsets, because
* we use `maxCols` to find the length of the longest line and then pretend
* that all the lines are the same length. However, these offsets are
* convenient for comparing or sorting locations in a file. For an example,
* see `subsumes`.
*/
predicate charLoc(File f, int start, int end) {
f = this.getFile() and
exists(int maxCols | maxCols = maxCols(f) |
start = this.getStartLine() * maxCols + this.getStartColumn() and
end = this.getEndLine() * maxCols + this.getEndColumn()
)
}
}
/**
* Gets the length of the longest line in file `f`.
*/
pragma[nomagic]
private int maxCols(File f) {
result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn()))
}
/**
* A C/C++ element that has a location in a file
*/
class Locatable extends Element { }
/**
* A dummy location which is used when something doesn't have a location in
* the source code but needs to have a `Location` associated with it. There
* may be several distinct kinds of unknown locations. For example: one for
* expressions, one for statements and one for other program elements.
*/
class UnknownLocation extends Location {
UnknownLocation() { this.getFile().getAbsolutePath() = "" }
}
/**
* A dummy location which is used when something doesn't have a location in
* the source code but needs to have a `Location` associated with it.
*/
class UnknownDefaultLocation extends UnknownLocation {
UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) }
}
/**
* A dummy location which is used when an expression doesn't have a
* location in the source code but needs to have a `Location` associated
* with it.
*/
class UnknownExprLocation extends UnknownLocation {
UnknownExprLocation() { locations_expr(this, _, 0, 0, 0, 0) }
}
/**
* A dummy location which is used when a statement doesn't have a location
* in the source code but needs to have a `Location` associated with it.
*/
class UnknownStmtLocation extends UnknownLocation {
UnknownStmtLocation() { locations_stmt(this, _, 0, 0, 0, 0) }
}