Skip to content

Create a shared implementation for Locations and Files #10592

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

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 2 additions & 0 deletions javascript/ql/lib/qlpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ dbscheme: semmlecode.javascript.dbscheme
extractor: javascript
library: true
upgrades: upgrades
dependencies:
codeql/utils: 0.0.1
37 changes: 37 additions & 0 deletions javascript/ql/lib/semmle/javascript/internal/LocationsImpl.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Provides an implementation of `LocationsSig` from the `codeql/utils` package.
*/

private import codeql.utils.Locations as Locs

/** An implementation of `LocationsSig`. */
module LocationsImpl implements Locs::LocationsSig {
abstract class Container extends @container {
abstract string getAbsolutePath();

string toString() { result = this.getAbsolutePath() }

Container getParentContainer() { containerparent(result, this) }
}

class File extends @file, Container {
override string getAbsolutePath() { files(this, result) }
}

class Folder extends @folder, Container {
override string getAbsolutePath() { folders(this, result) }
}

string getSourceLocationPrefix() { sourceLocationPrefix(result) }

class Location = @location;

predicate locations(
Location loc, File file, int startLine, int startColum, int endLine, int endColumn
) {
locations_default(loc, file, startLine, startColum, endLine, endColumn)
}
}

/** An instantiation of the shared Locations module. */
module Inst = Locs::Make<LocationsImpl>;
63 changes: 2 additions & 61 deletions ruby/ql/lib/codeql/Locations.qll
Original file line number Diff line number Diff line change
@@ -1,64 +1,5 @@
/** Provides classes for working with locations. */

import files.FileSystem

/**
* 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 [LGTM 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 }

/** 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
* [LGTM 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()
)
}

/** Holds if this location starts strictly before the specified location. */
pragma[inline]
predicate strictlyBefore(Location other) {
this.getStartLine() < other.getStartLine()
or
this.getStartLine() = other.getStartLine() and this.getStartColumn() < other.getStartColumn()
}
}
private import codeql.utils.Locations as Locs
import codeql.ruby.internal.LocationsImpl::Inst

/** An entity representing an empty location. */
class EmptyLocation extends Location {
Expand Down
178 changes: 4 additions & 174 deletions ruby/ql/lib/codeql/files/FileSystem.qll
Original file line number Diff line number Diff line change
@@ -1,177 +1,7 @@
/** Provides classes for working with files and folders. */
private import codeql.ruby.internal.LocationsImpl

private import codeql.Locations
class Container = Inst::Container;

/** A file or folder. */
abstract class Container extends @container {
/** Gets a file or sub-folder in this container. */
Container getAChildContainer() { this = result.getParentContainer() }
class File = Inst::File;

/** Gets a file in this container. */
File getAFile() { result = this.getAChildContainer() }

/** Gets a sub-folder in this container. */
Folder getAFolder() { result = this.getAChildContainer() }

/**
* Gets the absolute, canonical path of this container, using forward slashes
* as path separator.
*
* The path starts with a _root prefix_ followed by zero or more _path
* segments_ separated by forward slashes.
*
* The root prefix is of one of the following forms:
*
* 1. A single forward slash `/` (Unix-style)
* 2. An upper-case drive letter followed by a colon and a forward slash,
* such as `C:/` (Windows-style)
* 3. Two forward slashes, a computer name, and then another forward slash,
* such as `//FileServer/` (UNC-style)
*
* Path segments are never empty (that is, absolute paths never contain two
* contiguous slashes, except as part of a UNC-style root prefix). Also, path
* segments never contain forward slashes, and no path segment is of the
* form `.` (one dot) or `..` (two dots).
*
* Note that an absolute path never ends with a forward slash, except if it is
* a bare root prefix, that is, the path has no path segments. A container
* whose absolute path has no segments is always a `Folder`, not a `File`.
*/
abstract string getAbsolutePath();

/**
* Gets the base name of this container including extension, that is, the last
* segment of its absolute path, or the empty string if it has no segments.
*
* Here are some examples of absolute paths and the corresponding base names
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Base name</th></tr>
* <tr><td>"/tmp/tst.go"</td><td>"tst.go"</td></tr>
* <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr>
* <tr><td>"/"</td><td>""</td></tr>
* <tr><td>"C:/"</td><td>""</td></tr>
* <tr><td>"D:/"</td><td>""</td></tr>
* <tr><td>"//FileServer/"</td><td>""</td></tr>
* </table>
*/
string getBaseName() {
result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
}

/**
* Gets the extension of this container, that is, the suffix of its base name
* after the last dot character, if any.
*
* In particular,
*
* - if the name does not include a dot, there is no extension, so this
* predicate has no result;
* - if the name ends in a dot, the extension is the empty string;
* - if the name contains multiple dots, the extension follows the last dot.
*
* Here are some examples of absolute paths and the corresponding extensions
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Extension</th></tr>
* <tr><td>"/tmp/tst.go"</td><td>"go"</td></tr>
* <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr>
* <tr><td>"/bin/bash"</td><td>not defined</td></tr>
* <tr><td>"/tmp/tst2."</td><td>""</td></tr>
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
* </table>
*/
string getExtension() {
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
}

/** Gets the file in this container that has the given `baseName`, if any. */
File getFile(string baseName) {
result = this.getAFile() and
result.getBaseName() = baseName
}

/** Gets the sub-folder in this container that has the given `baseName`, if any. */
Folder getFolder(string baseName) {
result = this.getAFolder() and
result.getBaseName() = baseName
}

/** Gets the parent container of this file or folder, if any. */
Container getParentContainer() { containerparent(result, this) }

/**
* Gets the relative path of this file or folder from the root folder of the
* analyzed source location. The relative path of the root folder itself is
* the empty string.
*
* This has no result if the container is outside the source root, that is,
* if the root folder is not a reflexive, transitive parent of this container.
*/
string getRelativePath() {
exists(string absPath, string pref |
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
|
absPath = pref and result = ""
or
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
not result.matches("/%")
)
}

/**
* Gets the stem of this container, that is, the prefix of its base name up to
* (but not including) the last dot character if there is one, or the entire
* base name if there is not.
*
* Here are some examples of absolute paths and the corresponding stems
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Stem</th></tr>
* <tr><td>"/tmp/tst.go"</td><td>"tst"</td></tr>
* <tr><td>"/tmp/.classpath"</td><td>""</td></tr>
* <tr><td>"/bin/bash"</td><td>"bash"</td></tr>
* <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr>
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
* </table>
*/
string getStem() {
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
}

/**
* Gets a URL representing the location of this container.
*
* For more information see https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls.
*/
abstract string getURL();

/**
* Gets a textual representation of the path of this container.
*
* This is the absolute path of the container.
*/
string toString() { result = this.getAbsolutePath() }
}

/** A folder. */
class Folder extends Container, @folder {
override string getAbsolutePath() { folders(this, result) }

/** Gets the URL of this folder. */
override string getURL() { result = "folder://" + this.getAbsolutePath() }
}

/** A file. */
class File extends Container, @file {
override string getAbsolutePath() { files(this, result) }

/** Gets the URL of this file. */
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }

/** Holds if this file was extracted from ordinary source code. */
predicate fromSource() { any() }
}
class Folder = Inst::Folder;
37 changes: 37 additions & 0 deletions ruby/ql/lib/codeql/ruby/internal/LocationsImpl.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Provides an implementation of `LocationsSig` from the `codeql/utils` package.
*/

private import codeql.utils.Locations as Locs

/** An implementation of `LocationsSig`. */
module LocationsImpl implements Locs::LocationsSig {
abstract class Container extends @container {
abstract string getAbsolutePath();

Container getParentContainer() { containerparent(result, this) }

string toString() { result = this.getAbsolutePath() }
}

class File extends @file, Container {
override string getAbsolutePath() { files(this, result) }
}

class Folder extends Container, @folder {
override string getAbsolutePath() { folders(this, result) }
}

string getSourceLocationPrefix() { sourceLocationPrefix(result) }

class Location = @location_default;

predicate locations(
Location loc, File file, int startLine, int startColum, int endLine, int endColumn
) {
locations_default(loc, file, startLine, startColum, endLine, endColumn)
}
}

/** An instantiation of the shared Locations module. */
module Inst = Locs::Make<LocationsImpl>;
1 change: 1 addition & 0 deletions ruby/ql/lib/qlpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ upgrades: upgrades
library: true
dependencies:
codeql/ssa: 0.0.1
codeql/utils: 0.0.1
4 changes: 4 additions & 0 deletions shared/utils/change-notes/2022-09-26-initial-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Initial release. Extracted common file system and locations code into a library pack to share code between languages.
Loading