JS: Remove synthetic locations

This commit is contained in:
Asger F
2025-08-27 11:20:24 +02:00
parent f1ca0ecc3c
commit dcf63fc434
13 changed files with 63 additions and 264 deletions

View File

@@ -31,7 +31,7 @@ class AstNode extends @ast_node, NodeInStmtContainer {
/** Gets the first token belonging to this element. */
Token getFirstToken() {
exists(DbLocation l1, DbLocation l2, string filepath, int startline, int startcolumn |
exists(Location l1, Location l2, string filepath, int startline, int startcolumn |
l1 = this.getLocation() and
l2 = result.getLocation() and
l1.hasLocationInfo(filepath, startline, startcolumn, _, _) and
@@ -41,7 +41,7 @@ class AstNode extends @ast_node, NodeInStmtContainer {
/** Gets the last token belonging to this element. */
Token getLastToken() {
exists(DbLocation l1, DbLocation l2, string filepath, int endline, int endcolumn |
exists(Location l1, Location l2, string filepath, int endline, int endcolumn |
l1 = this.getLocation() and
l2 = result.getLocation() and
l1.hasLocationInfo(filepath, _, _, endline, endcolumn) and

View File

@@ -3,7 +3,6 @@
import javascript
private import NodeModuleResolutionImpl
private import codeql.util.FileSystem
private import internal.Locations
private module FsInput implements InputSig {
abstract class ContainerBase extends @container {
@@ -99,7 +98,7 @@ class File extends Container, Impl::File {
*
* Note that files have special locations starting and ending at line zero, column zero.
*/
DbLocation getLocation() { result = getLocatableLocation(this) }
Location getLocation() { hasLocation(this, result) }
/** Gets the number of lines in this file. */
int getNumberOfLines() { result = sum(int loc | numlines(this, loc, _, _) | loc) }

View File

@@ -3,7 +3,6 @@
*/
import javascript
private import semmle.javascript.internal.Locations
/**
* A JSON-encoded value, which may be a primitive value, an array or an object.
@@ -33,7 +32,7 @@ class JsonValue extends @json_value, Locatable {
override string toString() { json(this, _, _, _, result) }
/** Gets the JSON file containing this value. */
File getJsonFile() { result = getLocatableLocation(this).getFile() }
File getJsonFile() { exists(Location loc | json_locations(this, loc) and result = loc.getFile()) }
/** If this is an object, gets the value of property `name`. */
JsonValue getPropValue(string name) { json_properties(this, name, result) }

View File

@@ -1,7 +1,6 @@
/** Provides classes for working with locations and program elements that have locations. */
import javascript
private import internal.Locations
/**
* A location as given by a file, a start line, a start column,
@@ -11,31 +10,31 @@ private import internal.Locations
*
* For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
class DbLocation extends TDbLocation {
final class Location extends @location_default {
/** Gets the file for this location. */
File getFile() { dbLocationInfo(this, result, _, _, _, _) }
File getFile() { locations_default(this, result, _, _, _, _) }
/** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine() { dbLocationInfo(this, _, result, _, _, _) }
int getStartLine() { locations_default(this, _, result, _, _, _) }
/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { dbLocationInfo(this, _, _, result, _, _) }
int getStartColumn() { locations_default(this, _, _, result, _, _) }
/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { dbLocationInfo(this, _, _, _, result, _) }
int getEndLine() { locations_default(this, _, _, _, result, _) }
/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { dbLocationInfo(this, _, _, _, _, result) }
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(DbLocation that) {
exists(File f, int sl1, int sc1, int sl2, int sc2 |
dbLocationInfo(this, f, sl1, sc1, _, _) and
dbLocationInfo(that, f, sl2, sc2, _, _)
predicate startsBefore(Location that) {
exists(string f, int sl1, int sc1, int sl2, int sc2 |
this.hasLocationInfo(f, sl1, sc1, _, _) and
that.hasLocationInfo(f, sl2, sc2, _, _)
|
sl1 < sl2
or
@@ -45,10 +44,10 @@ class DbLocation extends TDbLocation {
/** Holds if this location ends after location `that`. */
pragma[inline]
predicate endsAfter(DbLocation that) {
exists(File f, int el1, int ec1, int el2, int ec2 |
dbLocationInfo(this, f, _, _, el1, ec1) and
dbLocationInfo(that, f, _, _, el2, ec2)
predicate endsAfter(Location that) {
exists(string f, int el1, int ec1, int el2, int ec2 |
this.hasLocationInfo(f, _, _, el1, ec1) and
that.hasLocationInfo(f, _, _, el2, ec2)
|
el1 > el2
or
@@ -60,10 +59,10 @@ class DbLocation extends TDbLocation {
* Holds if this location contains location `that`, meaning that it starts
* before and ends after it.
*/
predicate contains(DbLocation that) { this.startsBefore(that) and this.endsAfter(that) }
predicate contains(Location that) { this.startsBefore(that) and this.endsAfter(that) }
/** Holds if this location is empty. */
predicate isEmpty() { exists(int l, int c | dbLocationInfo(this, _, l, c, l, c - 1)) }
predicate isEmpty() { exists(int l, int c | this.hasLocationInfo(_, l, c, l, c - 1)) }
/** Gets a textual representation of this element. */
string toString() { result = this.getFile().getBaseName() + ":" + this.getStartLine().toString() }
@@ -79,13 +78,19 @@ class DbLocation extends TDbLocation {
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(File f |
dbLocationInfo(this, f, startline, startcolumn, endline, endcolumn) and
locations_default(this, f, startline, startcolumn, endline, endcolumn) and
filepath = f.getAbsolutePath()
)
}
}
final class Location = LocationImpl;
cached
private Location getLocatableLocation(@locatable l) {
hasLocation(l, result) or
xmllocations(l, result) or
json_locations(l, result) or
yaml_locations(l, result)
}
/** A program element with a location. */
class Locatable extends @locatable {
@@ -93,7 +98,7 @@ class Locatable extends @locatable {
File getFile() { result = this.getLocation().getFile() }
/** Gets this element's location. */
final DbLocation getLocation() { result = getLocatableLocation(this) }
final Location getLocation() { result = getLocatableLocation(this) }
/**
* Gets the line on which this element starts.

View File

@@ -26,7 +26,7 @@ class FirstLineOf extends Locatable {
then endcolumn = xc
else
endcolumn =
max(int c | any(DbLocation l).hasLocationInfo(filepath, startline, _, startline, c))
max(int c | any(Location l).hasLocationInfo(filepath, startline, _, startline, c))
)
}
}

View File

@@ -412,17 +412,22 @@ class SsaVariable extends TSsaDefinition {
/** Gets a textual representation of this element. */
string toString() { result = this.getDefinition().prettyPrintRef() }
/** Gets the location of this SSA variable. */
Location getLocation() { result = this.getDefinition().getLocation() }
/**
* DEPRECATED. Use `getLocation().hasLocationInfo()` instead.
*
* 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(
deprecated predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getDefinition().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -478,23 +483,22 @@ class SsaDefinition extends TSsaDefinition {
string toString() { result = this.prettyPrintDef() }
/**
* DEPRECATED. Use `getLocation().hasLocationInfo()` instead.
*
* 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/).
*/
abstract predicate hasLocationInfo(
deprecated predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
);
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the location of this element. */
final Location getLocation() {
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
result.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
}
Location getLocation() { result = this.getBasicBlock().getLocation() }
/** Gets the function or toplevel to which this definition belongs. */
StmtContainer getContainer() { result = this.getBasicBlock().getContainer() }
@@ -522,20 +526,13 @@ class SsaExplicitDefinition extends SsaDefinition, TExplicitDef {
override VarDef getAContributingVarDef() { result = this.getDef() }
override string prettyPrintRef() {
exists(int l, int c | this.hasLocationInfo(_, l, c, _, _) | result = "def@" + l + ":" + c)
exists(int l, int c | this.getLocation().hasLocationInfo(_, l, c, _, _) |
result = "def@" + l + ":" + c
)
}
override string prettyPrintDef() { result = this.getDef().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(Location loc |
pragma[only_bind_into](loc) = pragma[only_bind_into](this.getDef()).getLocation() and
loc.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
}
/**
* Gets the data flow node representing the incoming value assigned at this definition,
* if any.
@@ -557,21 +554,10 @@ abstract class SsaImplicitDefinition extends SsaDefinition {
abstract string getKind();
override string prettyPrintRef() {
exists(int l, int c | this.hasLocationInfo(_, l, c, _, _) |
exists(int l, int c | this.getLocation().hasLocationInfo(_, l, c, _, _) |
result = this.getKind() + "@" + l + ":" + c
)
}
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
endline = startline and
endcolumn = startcolumn and
exists(Location loc |
pragma[only_bind_into](loc) = pragma[only_bind_into](this.getBasicBlock()).getLocation() and
loc.hasLocationInfo(filepath, startline, startcolumn, _, _)
)
}
}
/**
@@ -617,16 +603,6 @@ class SsaVariableCapture extends SsaImplicitDefinition, TCapture {
override string getKind() { result = "capture" }
override string prettyPrintDef() { result = "capture variable " + this.getSourceVariable() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(ReachableBasicBlock bb, int i | this.definesAt(bb, i, _) |
bb.getNode(i)
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
}
}
/**
@@ -747,13 +723,7 @@ class SsaRefinementNode extends SsaPseudoDefinition, TRefinement {
this.getSourceVariable() + " = refine[" + this.getGuard() + "](" + this.ppInputs() + ")"
}
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getGuard()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override Location getLocation() { result = this.getGuard().getLocation() }
}
module Ssa {

View File

@@ -353,9 +353,9 @@ class LocalVariable extends Variable {
* If the variable has one or more declarations, the location of the first declaration is used.
* If the variable has no declaration, the entry point of its declaring container is used.
*/
DbLocation getLocation() {
Location getLocation() {
result =
min(DbLocation loc |
min(Location loc |
loc = this.getADeclaration().getLocation()
|
loc order by loc.getStartLine(), loc.getStartColumn()

View File

@@ -3,13 +3,12 @@
*/
import semmle.files.FileSystem
private import semmle.javascript.internal.Locations
private import codeql.xml.Xml
private module Input implements InputSig<File, DbLocation> {
private module Input implements InputSig<File, Location> {
class XmlLocatableBase = @xmllocatable or @xmlnamespaceable;
predicate xmllocations_(XmlLocatableBase e, DbLocation loc) { loc = getLocatableLocation(e) }
predicate xmllocations_(XmlLocatableBase e, Location loc) { xmllocations(e, loc) }
class XmlParentBase = @xmlparent;
@@ -67,4 +66,4 @@ private module Input implements InputSig<File, DbLocation> {
}
}
import Make<File, DbLocation, Input>
import Make<File, Location, Input>

View File

@@ -9,8 +9,6 @@ import javascript
private import codeql.yaml.Yaml as LibYaml
private module YamlSig implements LibYaml::InputSig {
class Location = DbLocation;
class LocatableBase extends @yaml_locatable, Locatable { }
import javascript

View File

@@ -4,7 +4,7 @@ private import semmle.javascript.dataflow.internal.VariableOrThis
private import codeql.dataflow.VariableCapture
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
module VariableCaptureConfig implements InputSig<js::DbLocation> {
module VariableCaptureConfig implements InputSig<js::Location> {
private js::Function getLambdaFromVariable(js::LocalVariable variable) {
result.getVariable() = variable
or
@@ -168,7 +168,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
string toString() { none() } // Overridden in subclass
js::DbLocation getLocation() { none() } // Overridden in subclass
js::Location getLocation() { none() } // Overridden in subclass
predicate hasCfgNode(BasicBlock bb, int i) { none() } // Overridden in subclass
@@ -186,7 +186,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
override string toString() { result = pattern.toString() }
/** Gets the location of this write. */
override js::DbLocation getLocation() { result = pattern.getLocation() }
override js::Location getLocation() { result = pattern.getLocation() }
override js::DataFlow::Node getSource() {
// Note: there is not always an expression corresponding to the RHS of the assignment.
@@ -222,7 +222,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
override string toString() { result = "[implicit init] " + variable }
override js::DbLocation getLocation() { result = variable.getLocation() }
override js::Location getLocation() { result = variable.getLocation() }
override CapturedVariable getVariable() { result = variable }
@@ -242,7 +242,7 @@ module VariableCaptureConfig implements InputSig<js::DbLocation> {
predicate entryBlock(BasicBlock bb) { bb instanceof js::EntryBasicBlock }
}
module VariableCaptureOutput = Flow<js::DbLocation, VariableCaptureConfig>;
module VariableCaptureOutput = Flow<js::Location, VariableCaptureConfig>;
js::DataFlow::Node getNodeFromClosureNode(VariableCaptureOutput::ClosureNode node) {
result = TValueNode(node.(VariableCaptureOutput::ExprNode).getExpr())

View File

@@ -25,7 +25,7 @@ class LocalVariableOrThis extends TLocalVariableOrThis {
}
/** Gets the location of a declaration of this variable, or the declaring container if this is `this`. */
DbLocation getLocation() {
Location getLocation() {
result = this.asLocalVariable().getLocation()
or
result = this.asThisContainer().getLocation()
@@ -95,7 +95,7 @@ abstract class ThisUse instanceof ControlFlowNode {
string toString() { result = super.toString() }
/** Gets the location of this use of `this`. */
DbLocation getLocation() { result = super.getLocation() }
Location getLocation() { result = super.getLocation() }
}
private predicate implicitThisUse(ControlFlowNode node, StmtContainer thisBinder) {

View File

@@ -9,7 +9,7 @@ private import codeql.ssa.Ssa
private import semmle.javascript.internal.BasicBlockInternal as BasicBlockInternal
private import semmle.javascript.dataflow.internal.VariableOrThis
module SsaConfig implements InputSig<js::DbLocation> {
module SsaConfig implements InputSig<js::Location> {
class ControlFlowNode = js::ControlFlowNode;
class BasicBlock = js::BasicBlock;
@@ -47,7 +47,7 @@ module SsaConfig implements InputSig<js::DbLocation> {
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
}
import Make<js::DbLocation, SsaConfig>
import Make<js::Location, SsaConfig>
module SsaDataflowInput implements DataFlowIntegrationInputSig {
private import codeql.util.Boolean

View File

@@ -1,171 +0,0 @@
/** Provides classes for working with locations and program elements that have locations. */
import javascript
// Should _not_ be cached, as that would require the data flow stage to be evaluated
// in order to evaluate the AST stage. Ideally, we would cache each injector separately,
// but that's not possible. Instead, we cache all predicates that need the injectors
// to be tuple numbered.
newtype TLocation =
TDbLocation(@location loc) or
TSynthLocation(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
any(SsaDefinition def).hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
// avoid overlap with existing DB locations
not exists(File f |
locations_default(_, f, startline, startcolumn, endline, endcolumn) and
f.getAbsolutePath() = filepath
)
}
/**
* 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/).
*/
abstract class LocationImpl extends TLocation {
/** Gets the file for this location. */
abstract File getFile();
/** Gets the 1-based line number (inclusive) where this location starts. */
abstract int getStartLine();
/** Gets the 1-based column number (inclusive) where this location starts. */
abstract int getStartColumn();
/** Gets the 1-based line number (inclusive) where this location ends. */
abstract int getEndLine();
/** Gets the 1-based column number (inclusive) where this location ends. */
abstract int getEndColumn();
/** 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(string f, int sl1, int sc1, int sl2, int sc2 |
this.hasLocationInfo(f, sl1, sc1, _, _) and
that.hasLocationInfo(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(string f, int el1, int ec1, int el2, int ec2 |
this.hasLocationInfo(f, _, _, el1, ec1) and
that.hasLocationInfo(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 | this.hasLocationInfo(_, 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/).
*/
abstract predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
);
}
class DbLocationImpl extends LocationImpl instanceof DbLocation {
override File getFile() { result = DbLocation.super.getFile() }
override int getStartLine() { result = DbLocation.super.getStartLine() }
override int getStartColumn() { result = DbLocation.super.getStartColumn() }
override int getEndLine() { result = DbLocation.super.getEndLine() }
override int getEndColumn() { result = DbLocation.super.getEndColumn() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
DbLocation.super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
class SynthLocationImpl extends LocationImpl, TSynthLocation {
override File getFile() { synthLocationInfo(this, result.getAbsolutePath(), _, _, _, _) }
override int getStartLine() { synthLocationInfo(this, _, result, _, _, _) }
override int getStartColumn() { synthLocationInfo(this, _, _, result, _, _) }
override int getEndLine() { synthLocationInfo(this, _, _, _, result, _) }
override int getEndColumn() { synthLocationInfo(this, _, _, _, _, result) }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
synthLocationInfo(this, filepath, startline, startcolumn, endline, endcolumn)
}
}
cached
private module Cached {
cached
DbLocation getLocatableLocation(@locatable l) {
exists(@location loc |
hasLocation(l, loc) or
xmllocations(l, loc) or
json_locations(l, loc) or
yaml_locations(l, loc)
|
result = TDbLocation(loc)
)
}
cached
predicate dbLocationInfo(
DbLocation l, File f, int startline, int startcolumn, int endline, int endcolumn
) {
exists(@location loc |
l = TDbLocation(loc) and
locations_default(loc, f, startline, startcolumn, endline, endcolumn)
)
}
}
import Cached
cached
private module CachedInDataFlowStage {
private import semmle.javascript.internal.CachedStages
cached
predicate synthLocationInfo(
SynthLocationImpl l, string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
Stages::DataFlowStage::ref() and
l = TSynthLocation(filepath, startline, startcolumn, endline, endcolumn)
}
}
private import CachedInDataFlowStage