Skip to content
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
109 changes: 93 additions & 16 deletions ql/lib/codeql/bicep/ast/AstNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,58 @@ private import codeql.bicep.ast.internal.TreeSitter
private import codeql.bicep.controlflow.ControlFlowGraph

/**
* An AST node of a Bicep program
* An AST node of a Bicep program.
*
* This is the base class for all syntax nodes in the Bicep language. All
* Bicep entities that have a representation in the abstract syntax tree
* (such as expressions, statements, resources, parameters, etc.) extend this class.
*/
class AstNode extends TAstNode {
private BICEP::AstNode node;

AstNode() { toTreeSitter(this) = node }

/**
* Gets a string representation of this AST node.
*
* By default, returns the primary QL class name of this node.
*/
string toString() { result = this.getAPrimaryQlClass() }

/** Gets the location of the AST node. */
/**
* Gets the location of the AST node.
*
* The location includes information about the file, and the start and
* end positions of the node in the file (line and column).
*/
cached
Location getLocation() { result = this.getFullLocation() } // overridden in some subclasses

/** Gets the file containing this AST node. */
/**
* Gets the file containing this AST node.
*
* This is the source file where this AST node is defined.
*/
cached
File getFile() { result = this.getFullLocation().getFile() }

/** Gets the location that spans the entire AST node. */
/**
* Gets the location that spans the entire AST node.
*
* This includes the full text range covered by this node and all its children.
*/
cached
final Location getFullLocation() { result = toTreeSitter(this).getLocation() }

/**
* Holds if this AST node has the specified location information.
*
* @param filepath The file path
* @param startline The start line
* @param startcolumn The start column
* @param endline The end line
* @param endcolumn The end column
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
Expand All @@ -43,18 +74,29 @@ class AstNode extends TAstNode {

/**
* Gets the parent in the AST for this node.
*
* This is the immediately enclosing syntax node in the AST.
* For example, the parent of an expression might be a statement that contains it.
*/
cached
AstNode getParent() { result.getAChild() = this }

/**
* Gets a child of this node, which can also be retrieved using a predicate
* named `pred`.
* Gets a child of this node.
*
* This returns any direct child node in the AST hierarchy.
* The exact set of child nodes depends on the specific type of AST node.
*/
cached
AstNode getAChild() { toTreeSitter(result) = node.getAFieldOrChild() }

/** Gets the CFG scope that encloses this node, if any. */
/**
* Gets the CFG scope that encloses this node, if any.
*
* A CFG scope is a region of code that has its own control flow graph.
* This can include the entire file, functions, or other code blocks with
* independent control flow.
*/
cached
CfgScope getEnclosingCfgScope() {
exists(AstNode p | p = this.getParent*() |
Expand All @@ -66,19 +108,36 @@ class AstNode extends TAstNode {
}

/**
* Gets the primary QL class for the ast node.
* Gets the primary QL class for the AST node.
*
* This returns the name of the most specific QL class that describes this node.
* Used primarily for debugging and toString() representations.
*/
string getAPrimaryQlClass() { result = "???" }
}

/** A Bicep file */
/**
* A Bicep file.
*
* Represents a source file written in the Bicep language. This class provides
* access to file metadata and metrics such as the total number of lines and
* lines of code.
*/
class BicepFile extends File {
BicepFile() { exists(Location loc | bicep_ast_node_location(_, loc) and this = loc.getFile()) }

/** Gets a token in this file. */
/**
* Gets a token in this file.
*
* A token is a basic syntactic unit recognized by the lexer.
*/
private BICEP::Token getAToken() { result.getLocation().getFile() = this }

/** Holds if `line` contains a token. */
/**
* Holds if `line` contains a token.
*
* This is used to calculate metrics like the number of lines of code.
*/
private predicate line(int line) {
exists(BICEP::Token token, Location l |
token = this.getAToken() and
Expand All @@ -87,15 +146,28 @@ class BicepFile extends File {
)
}

/** Gets the number of lines in this file. */
/**
* Gets the number of lines in this file.
*
* This counts the total number of lines in the file, including empty lines
* and comment lines.
*/
int getNumberOfLines() { result = max([0, this.getAToken().getLocation().getEndLine()]) }

/** Gets the number of lines of code in this file. */
/**
* Gets the number of lines of code in this file.
*
* This counts only lines that contain at least one token, which excludes
* completely empty lines.
*/
int getNumberOfLinesOfCode() { result = count(int line | this.line(line)) }
}

/**
* A comment in a Bicep program
* A comment in a Bicep program.
*
* Represents a single- or multi-line comment in Bicep source code.
* Comments may be either single-line (// comment) or multi-line (/* comment *\/).
*/
class Comment extends AstNode, TComment {
private BICEP::Comment comment;
Expand All @@ -105,9 +177,14 @@ class Comment extends AstNode, TComment {
Comment() { this = TComment(comment) }

/**
* Gets the text of the comment.
* Gets the text content of the comment.
*
* For a single-line comment, this is the text after the // delimiter.
* For a multi-line comment, this is the text between the /* and *\/ delimiters.
*
* TODO: Implement this method
* Note: This method is currently a placeholder and needs implementation.
*
* @return The text content of the comment (currently an empty string)
*/
string getContents() { result = "" }
}
89 changes: 71 additions & 18 deletions ql/lib/codeql/bicep/ast/Calls.qll
Original file line number Diff line number Diff line change
Expand Up @@ -7,59 +7,112 @@ private import internal.CallExpression

/**
* Represents a callable expression in the AST, such as a function or method call.
* Provides access to the identifier and name of the call expression.
*
* This abstract class serves as the base for any expression that represents
* a call to a function or method. It provides common functionality for accessing
* the name and identifier of the called entity.
*/
abstract class Callable extends Expr {
/**
* Gets the identifier of the call expression.
* Gets the identifier of the callable expression.
*
* This is the name token that identifies what function or method is being called.
*
* @return The identifier node of the callable
*/
abstract Idents getIdentifier();

/**
* Gets the name of the call expression.
* Gets the name of the callable expression as a string.
*
* This is a convenience method that returns the name from the identifier.
*
* @return The name as a string.
* @return The name of the callable
*/
string getName() { result = this.getIdentifier().getName() }

/**
* Checks if the call expression has a specific name.
* Checks if the callable expression has a specific name.
*
* This is useful for identifying calls to known functions by name.
*
* @param name The name to check against.
* @return True if the call expression has the given name.
* @param name The name to check against
* @return True if the callable has the given name, false otherwise
*/
predicate hasName(string name) { this.getName() = name }
}

/**
* Represents a call expression node in the AST.
* Provides access to the identifier, arguments, and declared arguments.
* Represents a function or method call expression in the AST.
*
* This class models a function call in Bicep, consisting of an identifier
* (the function name) followed by arguments in parentheses. Function calls
* invoke functions defined in the language, user-defined functions, or
* module functions to compute values or perform operations.
*/
class CallExpression extends Expr instanceof CallExpressionImpl {
/** Gets the identifier of the call expression. */
/**
* Gets the identifier of the call expression.
*
* This is the name token that identifies what function is being called.
*
* @return The identifier node of the function being called
*/
Idents getIdentifier() { result = CallExpressionImpl.super.getIdentifier() }

/** Gets the name of the call expression. */
/**
* Gets the name of the call expression as a string.
*
* This is a convenience method that returns the name from the identifier.
*
* @return The name of the function being called
*/
string getName() { result = this.getIdentifier().getName() }

/** Gets the argument at the specified index. */
/**
* Gets the argument at the specified index.
*
* @param index The zero-based index of the argument to retrieve
* @return The expression node of the argument at the specified index
*/
Expr getArgument(int index) { result = this.getDeclaredArguments().getArgument(index) }

/** Gets all arguments of the call expression. */
/**
* Gets all arguments of the call expression.
*
* @return All argument expressions passed to the function
*/
Expr getArguments() { result = this.getDeclaredArguments().getArguments() }

/** Gets the declared arguments node. */
/**
* Gets the arguments collection node of the call expression.
*
* This provides access to the AST node that contains all the arguments.
*
* @return The arguments node of the call expression
*/
Arguments getDeclaredArguments() { result = CallExpressionImpl.super.getArguments() }
}

/**
* Represents the arguments node in the AST.
* Provides access to individual and all arguments.
* Represents a collection of arguments in a function call.
*
* This class models the set of arguments passed to a function, allowing
* access to individual arguments by index or to the complete set of arguments.
*/
class Arguments extends AstNode instanceof ArgumentsImpl {
/** Gets the argument at the specified index. */
/**
* Gets the argument at the specified index.
*
* @param index The zero-based index of the argument to retrieve
* @return The expression node of the argument at the specified index
*/
Expr getArgument(int index) { result = ArgumentsImpl.super.getArgument(index) }

/** Gets all arguments. */
/**
* Gets all arguments in the collection.
*
* @return All argument expressions in this arguments collection
*/
Expr getArguments() { result = ArgumentsImpl.super.getArguments() }
}
Loading