Skip to content

Commit

Permalink
Merge pull request #15882 from hvitved/js/dataflow-node-get-location
Browse files Browse the repository at this point in the history
JS: Add `DataFlow::Node.getLocation`
  • Loading branch information
hvitved authored Mar 19, 2024
2 parents aa1d5c5 + 54fa818 commit 5ab1047
Show file tree
Hide file tree
Showing 23 changed files with 259 additions and 191 deletions.
3 changes: 1 addition & 2 deletions config/identical-files.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@
"cpp/ql/lib/semmle/code/cpp/XML.qll",
"csharp/ql/lib/semmle/code/csharp/XML.qll",
"java/ql/lib/semmle/code/xml/XML.qll",
"javascript/ql/lib/semmle/javascript/XML.qll",
"python/ql/lib/semmle/python/xml/XML.qll"
],
"DuplicationProblems.inc.qhelp": [
Expand Down Expand Up @@ -372,4 +371,4 @@
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
]
}
}
16 changes: 6 additions & 10 deletions javascript/ql/lib/semmle/javascript/AST.qll
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,27 @@ private import semmle.javascript.internal.CachedStages
* ```
*/
class AstNode extends @ast_node, NodeInStmtContainer {
override Location getLocation() { hasLocation(this, result) }

override File getFile() {
result = this.getLocation().getFile() // Specialized for performance reasons
}

/** Gets the first token belonging to this element. */
Token getFirstToken() {
exists(Location l1, Location l2 |
exists(DbLocation l1, DbLocation l2, string filepath, int startline, int startcolumn |
l1 = this.getLocation() and
l2 = result.getLocation() and
l1.getFile() = l2.getFile() and
l1.getStartLine() = l2.getStartLine() and
l1.getStartColumn() = l2.getStartColumn()
l1.hasLocationInfo(filepath, startline, startcolumn, _, _) and
l2.hasLocationInfo(filepath, startline, startcolumn, _, _)
)
}

/** Gets the last token belonging to this element. */
Token getLastToken() {
exists(Location l1, Location l2 |
exists(DbLocation l1, DbLocation l2, string filepath, int endline, int endcolumn |
l1 = this.getLocation() and
l2 = result.getLocation() and
l1.getFile() = l2.getFile() and
l1.getEndLine() = l2.getEndLine() and
l1.getEndColumn() = l2.getEndColumn()
l1.hasLocationInfo(filepath, _, _, endline, endcolumn) and
l2.hasLocationInfo(filepath, _, _, endline, endcolumn)
) and
// exclude empty EOF token
not result instanceof EOFToken
Expand Down
4 changes: 1 addition & 3 deletions javascript/ql/lib/semmle/javascript/CFG.qll
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,7 @@ class ControlFlowNode extends @cfg_node, Locatable, NodeInStmtContainer {
* A synthetic CFG node that does not correspond to a statement or expression;
* examples include guard nodes and entry/exit nodes.
*/
class SyntheticControlFlowNode extends @synthetic_cfg_node, ControlFlowNode {
override Location getLocation() { hasLocation(this, result) }
}
class SyntheticControlFlowNode extends @synthetic_cfg_node, ControlFlowNode { }

/** A synthetic CFG node marking the entry point of a function or toplevel script. */
class ControlFlowEntryNode extends SyntheticControlFlowNode, @entry_node {
Expand Down
2 changes: 0 additions & 2 deletions javascript/ql/lib/semmle/javascript/Comments.qll
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import javascript
* </pre>
*/
class Comment extends @comment, Locatable {
override Location getLocation() { hasLocation(this, result) }

/** Gets the toplevel element this comment belongs to. */
TopLevel getTopLevel() { comments(this, _, result, _, _) }

Expand Down
2 changes: 0 additions & 2 deletions javascript/ql/lib/semmle/javascript/Errors.qll
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import javascript

/** An error encountered during extraction. */
abstract class Error extends Locatable {
override Location getLocation() { hasLocation(this, result) }

/** Gets the message associated with this error. */
abstract string getMessage();

Expand Down
3 changes: 2 additions & 1 deletion javascript/ql/lib/semmle/javascript/Files.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import javascript
private import NodeModuleResolutionImpl
private import codeql.util.FileSystem
private import internal.Locations

private module FsInput implements InputSig {
abstract class ContainerBase extends @container {
Expand Down Expand Up @@ -83,7 +84,7 @@ class File extends Container, Impl::File {
*
* Note that files have special locations starting and ending at line zero, column zero.
*/
Location getLocation() { hasLocation(this, result) }
DbLocation getLocation() { result = getLocatableLocation(this) }

/** Gets the number of lines in this file. */
int getNumberOfLines() { result = sum(int loc | numlines(this, loc, _, _) | loc) }
Expand Down
8 changes: 0 additions & 8 deletions javascript/ql/lib/semmle/javascript/HTML.qll
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ module HTML {
class Element extends Locatable, @xmlelement {
Element() { exists(FileContainingHtml f | xmlElements(this, _, _, _, f)) }

override Location getLocation() { xmllocations(this, result) }

/**
* Gets the name of this HTML element.
*
Expand Down Expand Up @@ -122,8 +120,6 @@ module HTML {
class Attribute extends Locatable, @xmlattribute {
Attribute() { exists(FileContainingHtml f | xmlAttrs(this, _, _, _, _, f)) }

override Location getLocation() { xmllocations(this, result) }

/**
* Gets the inline script of this attribute, if any.
*/
Expand Down Expand Up @@ -326,8 +322,6 @@ module HTML {
* Holds if this text node is inside a `CDATA` tag.
*/
predicate isCData() { xmlChars(this, _, _, _, 1, _) }

override Location getLocation() { xmllocations(this, result) }
}

/**
Expand All @@ -349,7 +343,5 @@ module HTML {
string getText() { result = this.toString().regexpCapture("(?s)<!--(.*)-->", 1) }

override string toString() { xmlComments(this, result, _, _) }

override Location getLocation() { xmllocations(this, result) }
}
}
4 changes: 0 additions & 4 deletions javascript/ql/lib/semmle/javascript/JSDoc.qll
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ private import semmle.javascript.internal.CachedStages
* </pre>
*/
class JSDoc extends @jsdoc, Locatable {
override Location getLocation() { hasLocation(this, result) }

/** Gets the description text of this JSDoc comment. */
string getDescription() { jsdoc(this, result, _) }

Expand Down Expand Up @@ -75,8 +73,6 @@ abstract class Documentable extends AstNode {
* ```
*/
class JSDocTypeExprParent extends @jsdoc_type_expr_parent, Locatable {
override Location getLocation() { hasLocation(this, result) }

/** Gets the JSDoc comment to which this element belongs. */
JSDoc getJSDocComment() { none() }
}
Expand Down
12 changes: 2 additions & 10 deletions javascript/ql/lib/semmle/javascript/JSON.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import javascript
private import semmle.javascript.internal.Locations

/**
* A JSON-encoded value, which may be a primitive value, an array or an object.
Expand All @@ -20,8 +21,6 @@ import javascript
* ```
*/
class JsonValue extends @json_value, Locatable {
override Location getLocation() { json_locations(this, result) }

/** Gets the parent value to which this value belongs, if any. */
JsonValue getParent() { json(this, _, result, _, _) }

Expand All @@ -34,12 +33,7 @@ class JsonValue extends @json_value, Locatable {
override string toString() { json(this, _, _, _, result) }

/** Gets the JSON file containing this value. */
File getJsonFile() {
exists(Location loc |
json_locations(this, loc) and
result = loc.getFile()
)
}
File getJsonFile() { result = getLocatableLocation(this).getFile() }

/** If this is an object, gets the value of property `name`. */
JsonValue getPropValue(string name) { json_properties(this, name, result) }
Expand Down Expand Up @@ -172,7 +166,5 @@ class JsonObject extends @json_object, JsonValue {
* An error reported by the JSON parser.
*/
class JsonParseError extends @json_parse_error, Error {
override Location getLocation() { json_locations(this, result) }

override string getMessage() { json_errors(this, result) }
}
2 changes: 0 additions & 2 deletions javascript/ql/lib/semmle/javascript/Lines.qll
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import javascript
* extracted with the `--extract-program-text` flag.
*/
class Line extends @line, Locatable {
override Location getLocation() { hasLocation(this, result) }

/** Gets the toplevel element this line belongs to. */
TopLevel getTopLevel() { lines(this, result, _, _) }

Expand Down
53 changes: 21 additions & 32 deletions javascript/ql/lib/semmle/javascript/Locations.qll
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
/** 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,
* an end line, and an end column.
*
* This class is restricted to locations created by the extractor.
*
* For more information about locations see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
class Location extends @location {
class DbLocation extends TDbLocation {
/** Gets the file for this location. */
File getFile() { locations_default(this, result, _, _, _, _) }
File getFile() { dbLocationInfo(this, result, _, _, _, _) }

/** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine() { locations_default(this, _, result, _, _, _) }
int getStartLine() { dbLocationInfo(this, _, result, _, _, _) }

/** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { locations_default(this, _, _, result, _, _) }
int getStartColumn() { dbLocationInfo(this, _, _, result, _, _) }

/** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { locations_default(this, _, _, _, result, _) }
int getEndLine() { dbLocationInfo(this, _, _, _, result, _) }

/** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { locations_default(this, _, _, _, _, result) }
int getEndColumn() { dbLocationInfo(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) {
predicate startsBefore(DbLocation 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, _, _)
dbLocationInfo(this, f, sl1, sc1, _, _) and
dbLocationInfo(that, f, sl2, sc2, _, _)
|
sl1 < sl2
or
Expand All @@ -42,10 +45,10 @@ class Location extends @location {

/** Holds if this location ends after location `that`. */
pragma[inline]
predicate endsAfter(Location that) {
predicate endsAfter(DbLocation 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)
dbLocationInfo(this, f, _, _, el1, ec1) and
dbLocationInfo(that, f, _, _, el2, ec2)
|
el1 > el2
or
Expand All @@ -57,10 +60,10 @@ class Location extends @location {
* 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) }
predicate contains(DbLocation 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)) }
predicate isEmpty() { exists(int l, int c | dbLocationInfo(this, _, l, c, l, c - 1)) }

/** Gets a textual representation of this element. */
string toString() { result = this.getFile().getBaseName() + ":" + this.getStartLine().toString() }
Expand All @@ -76,22 +79,21 @@ class Location extends @location {
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(File f |
locations_default(this, f, startline, startcolumn, endline, endcolumn) and
dbLocationInfo(this, f, startline, startcolumn, endline, endcolumn) and
filepath = f.getAbsolutePath()
)
}
}

final class Location = LocationImpl;

/** 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()
}
final DbLocation getLocation() { result = getLocatableLocation(this) }

/**
* Gets the line on which this element starts.
Expand Down Expand Up @@ -142,16 +144,3 @@ class Locatable extends @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() }
}
2 changes: 0 additions & 2 deletions javascript/ql/lib/semmle/javascript/Regexp.qll
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ class RegExpParent extends Locatable, @regexpparent { }
* ```
*/
class RegExpTerm extends Locatable, @regexpterm {
override Location getLocation() { hasLocation(this, result) }

/** Gets the `i`th child term of this term. */
RegExpTerm getChild(int i) { regexpterm(result, _, this, i, _) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class FirstLineOf extends Locatable {
then endcolumn = xc
else
endcolumn =
max(int c | any(Location l).hasLocationInfo(filepath, startline, _, startline, c))
max(int c | any(DbLocation l).hasLocationInfo(filepath, startline, _, startline, c))
)
}
}
Expand Down
8 changes: 8 additions & 0 deletions javascript/ql/lib/semmle/javascript/SSA.qll
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,14 @@ class SsaDefinition extends TSsaDefinition {
string filepath, int startline, int startcolumn, int endline, int 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)
)
}

/** Gets the function or toplevel to which this definition belongs. */
StmtContainer getContainer() { result = this.getBasicBlock().getContainer() }
}
Expand Down
2 changes: 0 additions & 2 deletions javascript/ql/lib/semmle/javascript/Tokens.qll
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import javascript
* ```
*/
class Token extends Locatable, @token {
override Location getLocation() { hasLocation(this, result) }

/** Gets the toplevel syntactic structure to which this token belongs. */
TopLevel getTopLevel() { tokeninfo(this, _, result, _, _) }

Expand Down
4 changes: 2 additions & 2 deletions javascript/ql/lib/semmle/javascript/Variables.qll
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,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.
*/
Location getLocation() {
DbLocation getLocation() {
result =
min(Location loc |
min(DbLocation loc |
loc = this.getADeclaration().getLocation()
|
loc order by loc.getStartLine(), loc.getStartColumn()
Expand Down
Loading

0 comments on commit 5ab1047

Please sign in to comment.