diff --git a/go/ql/lib/semmle/go/Locations.qll b/go/ql/lib/semmle/go/Locations.qll index d5ab0858f213..b187a5d9b41f 100644 --- a/go/ql/lib/semmle/go/Locations.qll +++ b/go/ql/lib/semmle/go/Locations.qll @@ -3,6 +3,21 @@ import go private import internal.Locations +private module DbLocationInput implements LocationClassInputSig { + class Base = TDbLocation; + + predicate locationInfo( + Base b, string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(File f | + dbLocationInfo(b, f, startline, startcolumn, endline, endcolumn) and + filepath = f.getAbsolutePath() + ) + } + + File getFile(Base b) { dbLocationInfo(b, result, _, _, _, _) } +} + /** * A location as given by a file, a start line, a start column, * an end line, and an end column. @@ -11,51 +26,27 @@ 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 { - /** Gets the file for this location. */ - File getFile() { dbLocationInfo(this, result, _, _, _, _) } - - /** Gets the 1-based line number (inclusive) where this location starts. */ - int getStartLine() { dbLocationInfo(this, _, result, _, _, _) } - - /** Gets the 1-based column number (inclusive) where this location starts. */ - int getStartColumn() { dbLocationInfo(this, _, _, result, _, _) } - - /** Gets the 1-based line number (inclusive) where this location ends. */ - int getEndLine() { dbLocationInfo(this, _, _, _, result, _) } +class DbLocation = LocationClass::Location; - /** Gets the 1-based column number (inclusive) where this location ends. */ - int getEndColumn() { dbLocationInfo(this, _, _, _, _, result) } +private module LocationInput implements LocationClassInputSig { + class Base = TLocation; - /** Gets the number of lines covered by this location. */ - int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 } - - /** Gets a textual representation of this element. */ - string toString() { - exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | - this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn - ) + predicate locationInfo( + Base b, string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + DbLocationInput::locationInfo(b, filepath, startline, startcolumn, endline, endcolumn) + or + dataFlowNodeLocationInfo(b, filepath, 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(File f | - dbLocationInfo(this, f, startline, startcolumn, endline, endcolumn) and - filepath = f.getAbsolutePath() - ) + File getFile(Base b) { + result = DbLocationInput::getFile(b) + or + dataFlowNodeLocationInfo(b, result.getAbsolutePath(), _, _, _, _) } } -final class Location = LocationImpl; +class Location = LocationClass::Location; /** A program element with a location. */ class Locatable extends @locatable { diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index cc353ab64df5..3b0c08957932 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -4,6 +4,7 @@ private import semmle.go.dataflow.FlowSummary private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl private import semmle.go.dataflow.ExternalFlow +private import semmle.go.internal.Locations cached private newtype TNode = @@ -158,12 +159,7 @@ module Public { } /** Gets the location of this node. */ - 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 = getDataFlowNodeLocation(this) } /** Gets the file in which this node appears. */ File getFile() { this.hasLocationInfo(result.getAbsolutePath(), _, _, _, _) } diff --git a/go/ql/lib/semmle/go/internal/Locations.qll b/go/ql/lib/semmle/go/internal/Locations.qll index 8b0a206b8838..6810db733493 100644 --- a/go/ql/lib/semmle/go/internal/Locations.qll +++ b/go/ql/lib/semmle/go/internal/Locations.qll @@ -2,13 +2,76 @@ import go +/** Provides the input to `LocationClass`. */ +signature module LocationClassInputSig { + class Base; + + predicate locationInfo( + Base b, string filepath, int startline, int startcolumn, int endline, int endcolumn + ); + + File getFile(Base b); +} + +/** Provides a class layer for locations. */ +module LocationClass { + private import Input + + /** + * 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/). + */ + final class Location instanceof Base { + /** Gets the file for this location. */ + File getFile() { result = getFile(this) } + + /** Gets the 1-based line number (inclusive) where this location starts. */ + int getStartLine() { locationInfo(this, _, result, _, _, _) } + + /** Gets the 1-based column number (inclusive) where this location starts. */ + int getStartColumn() { locationInfo(this, _, _, result, _, _) } + + /** Gets the 1-based line number (inclusive) where this location ends. */ + int getEndLine() { locationInfo(this, _, _, _, result, _) } + + /** Gets the 1-based column number (inclusive) where this location ends. */ + int getEndColumn() { locationInfo(this, _, _, _, _, result) } + + /** Gets the number of lines covered by this location. */ + int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 } + + /** Gets a textual representation of this element. */ + string toString() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and + result = filepath + "@" + 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 + ) { + locationInfo(this, filepath, startline, startcolumn, endline, endcolumn) + } + } +} + // 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) { + TDataFlowNodeLocation(string filepath, int startline, int startcolumn, int endline, int endcolumn) { any(DataFlow::Node n).hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and // avoid overlap with existing DB locations not existingDBLocation(filepath, startline, startcolumn, endline, endcolumn) @@ -24,91 +87,6 @@ private predicate existingDBLocation( ) } -/** - * 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 } - - /** Gets a textual representation of this element. */ - string toString() { - exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | - this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - result = filepath + "@" + 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/). - */ - abstract predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ); -} - -class DbLocationImpl extends LocationImpl instanceof DbLocation { - private @location loc; - - DbLocationImpl() { this = TDbLocation(loc) } - - 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 @@ -143,8 +121,21 @@ private module Cached { import Cached cached -private predicate synthLocationInfo( - SynthLocationImpl l, string filepath, int startline, int startcolumn, int endline, int endcolumn -) { - l = TSynthLocation(filepath, startline, startcolumn, endline, endcolumn) +private module DataFlowNodes { + cached + predicate dataFlowNodeLocationInfo( + Location l, string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + l = TDataFlowNodeLocation(filepath, startline, startcolumn, endline, endcolumn) + } + + cached + Location getDataFlowNodeLocation(DataFlow::Node n) { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and + result.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + } } + +import DataFlowNodes