Skip to content

JS: Add DataFlow::Node.getLocation #15882

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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