Files
codeql/javascript/ql/lib/semmle/javascript/Locations.qll
2021-11-01 09:51:15 +01:00

158 lines
5.2 KiB
Plaintext

/** Provides classes for working with locations and program elements that have locations. */
import javascript
/**
* A location as given by a file, a start line, a start column,
* an end line, and an end column.
*
* For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
class Location extends @location {
/** Gets the file for this location. */
File getFile() { locations_default(this, result, _, _, _, _) }
/** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine() { locations_default(this, _, result, _, _, _) }
/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { locations_default(this, _, _, result, _, _) }
/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { locations_default(this, _, _, _, result, _) }
/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { locations_default(this, _, _, _, _, result) }
/** Gets the number of lines covered by this location. */
int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 }
/** Holds if this location starts before location `that`. */
pragma[inline]
predicate startsBefore(Location that) {
exists(File f, int sl1, int sc1, int sl2, int sc2 |
locations_default(this, f, sl1, sc1, _, _) and
locations_default(that, f, sl2, sc2, _, _)
|
sl1 < sl2
or
sl1 = sl2 and sc1 < sc2
)
}
/** Holds if this location ends after location `that`. */
pragma[inline]
predicate endsAfter(Location that) {
exists(File f, int el1, int ec1, int el2, int ec2 |
locations_default(this, f, _, _, el1, ec1) and
locations_default(that, f, _, _, el2, ec2)
|
el1 > el2
or
el1 = el2 and ec1 > ec2
)
}
/**
* Holds if this location contains location `that`, meaning that it starts
* before and ends after it.
*/
predicate contains(Location that) { this.startsBefore(that) and this.endsAfter(that) }
/** Holds if this location is empty. */
predicate isEmpty() { exists(int l, int c | locations_default(this, _, l, c, l, c - 1)) }
/** Gets a textual representation of this element. */
string toString() { result = this.getFile().getBaseName() + ":" + this.getStartLine().toString() }
/**
* 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(File f |
locations_default(this, f, startline, startcolumn, endline, endcolumn) and
filepath = f.getAbsolutePath()
)
}
}
/** A program element with a location. */
class Locatable extends @locatable {
/** Gets the file this program element comes from. */
File getFile() { result = this.getLocation().getFile() }
/** Gets this element's location. */
Location getLocation() {
// overridden by subclasses
none()
}
/**
* Gets the line on which this element starts.
*
* This predicate is only defined for program elements from snapshots that
* have been extracted with the `--extract-program-text` flag.
*/
Line getStartLine() {
exists(Location l1, Location l2 |
l1 = this.getLocation() and
l2 = result.getLocation() and
l1.getFile() = l2.getFile() and
l1.getStartLine() = l2.getStartLine()
)
}
/**
* Gets the line on which this element ends.
*
* This predicate is only defined for program elements from snapshots that
* have been extracted with the `--extract-program-text` flag.
*/
Line getEndLine() {
exists(Location l1, Location l2 |
l1 = this.getLocation() and
l2 = result.getLocation() and
l1.getFile() = l2.getFile() and
l1.getEndLine() = l2.getStartLine()
)
}
/** Gets the number of lines covered by this element. */
int getNumLines() { result = this.getLocation().getNumLines() }
/** Gets a textual representation of this element. */
string toString() {
// to be overridden by subclasses
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 primary QL class for the Locatable.
*/
string getAPrimaryQlClass() { result = "???" }
}
/**
* A `File`, considered as a `Locatable`.
*
* For reasons of backwards compatibility, @file is a subtype of @locatable. This class exists to
* provide an override of `Locatable.getLocation()` for @files, since it would otherwise default
* to `none()`, which is unhelpful.
*/
private class FileLocatable extends File, Locatable {
override Location getLocation() { result = File.super.getLocation() }
override string toString() { result = File.super.toString() }
}