diff --git a/ql/lib/codeql/bicep/ast/AstNodes.qll b/ql/lib/codeql/bicep/ast/AstNodes.qll index 89d832d..94a99f6 100644 --- a/ql/lib/codeql/bicep/ast/AstNodes.qll +++ b/ql/lib/codeql/bicep/ast/AstNodes.qll @@ -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 ) { @@ -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*() | @@ -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 @@ -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; @@ -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 = "" } } diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll index 3fb6551..805c254 100644 --- a/ql/lib/codeql/bicep/ast/Calls.qll +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -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() } } diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll index b8063b6..9b31db1 100644 --- a/ql/lib/codeql/bicep/ast/Expr.qll +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -23,113 +23,189 @@ private import Resources /** * An expression in the AST. + * + * This is the base class for all expression nodes in Bicep, including literals, + * variables, function calls, operators, and other constructs that compute values. + * Expressions can be nested and can appear in various contexts such as assignments, + * parameter values, and return statements. */ final class Expr extends AstNode instanceof ExprImpl { } /** - * A AssignmentExpression expression in the AST. + * An assignment expression in the AST. + * + * Represents an assignment of a value to a variable or property, such as + * `x = 42` or `obj.prop = value`. The assignment expression consists of + * a left-hand side (the target), an assignment operator, and a right-hand + * side (the value). */ class AssignmentExpression extends Expr instanceof AssignmentExpressionImpl { /** * Gets the left operand of the assignment expression. + * + * This is the target of the assignment, which could be a variable, + * property access, or other assignable expression. */ Expr getLeft() { result = AssignmentExpressionImpl.super.getLeft() } /** * Gets the right operand of the assignment expression. + * + * This is the value being assigned to the target. */ Expr getRight() { result = AssignmentExpressionImpl.super.getRight() } } /** - * A BinaryExpression expression in the AST. + * A binary expression in the AST. + * + * Represents an expression with a binary operator and two operands. + * This includes arithmetic operations (like addition, subtraction), + * logical operations (like AND, OR), comparison operations (like equality, + * less than), and other binary operators in the Bicep language. */ class BinaryExpression extends Expr instanceof BinaryExpressionImpl { /** * Gets the left operand of the binary expression. + * + * This is the expression that appears to the left of the operator. */ Expr getLeft() { result = BinaryExpressionImpl.super.getLeft() } /** * Gets the right operand of the binary expression. + * + * This is the expression that appears to the right of the operator. */ Expr getRight() { result = BinaryExpressionImpl.super.getRight() } /** - * Gets the operator of the binary expression. + * Gets the operator of the binary expression as a string. + * + * Examples include "+", "-", "*", "/", "==", "!=", "&&", "||", etc. */ string getOperator() { result = BinaryExpressionImpl.super.getOperator() } } /** - * A Expression expression in the AST. + * A generic expression in the AST. + * + * This class represents any expression that doesn't fit into other more + * specific expression categories. It serves as a base implementation for + * expressions in the Bicep language. */ final class Expression extends Expr instanceof ExpressionImpl { } /** - * A Interpolation literal in the AST. + * An interpolation expression in the AST. + * + * Represents string interpolation expressions of the form ${expr} that appear within + * string literals. Interpolations allow embedding dynamic values or expressions + * within string literals. */ final class Interpolation extends Expr instanceof InterpolationImpl { /** - * Gets the expression contained within the interpolation. + * Gets the expression contained within the interpolation. + * + * This is the expression between the ${...} delimiters whose value + * will be converted to a string and inserted into the containing string. */ Expr getExpression() { result = InterpolationImpl.super.getExpression() } + /** + * Gets the string representation of this interpolation. + * + * Returns the interpolation in the form "${expression}". + */ string getValue() { result = "${" + this.getExpression().toString() + "}" } } /** - * A LambdaExpression expression in the AST. + * A lambda expression in the AST. + * + * Represents an anonymous function in Bicep, typically used for callbacks, + * filters, or other functional programming patterns. A lambda expression + * consists of parameters and a body that defines the computation to be performed. */ final class LambdaExpression extends Expr instanceof LambdaExpressionImpl { } /** - * A MemberExpression expression in the AST. + * A member expression in the AST. + * + * Represents a property or method access on an object, such as `object.property` + * or `resource.name`. Member expressions consist of an object (the namespace) + * and a property (the member being accessed). */ class MemberExpression extends Expr instanceof MemberExpressionImpl { /** - * The namespace of the member expression. + * Gets the namespace (object) of the member expression. + * + * This is the part of the expression before the dot operator. + * For example, in `resource.name`, this would be `resource`. */ Idents getNamespace() { result = MemberExpressionImpl.super.getObject() } /** - * The member of the member expression. + * Gets the member (property) of the member expression. + * + * This is the part of the expression after the dot operator. + * For example, in `resource.name`, this would be `name`. */ Idents getName() { result = MemberExpressionImpl.super.getProperty() } /** - * Gets the full name of the member expression, which includes the namespace and the member name. + * Gets the full name of the member expression. + * + * This combines the namespace and member names with a dot separator. + * For example, for the expression `resource.name`, this would return `"resource.name"`. */ string getFullName() { result = this.getNamespace().getName() + "." + this.getName().getName() } } /** * An alias for MemberExpression. + * + * This alias provides a shorter name for convenience. */ final class MemberExpr = MemberExpression; /** - * A NullableType literal in the AST. + * A nullable type expression in the AST. + * + * Represents a type that can be null, typically denoted with a question mark + * after the type name (e.g., `string?`). Nullable types explicitly allow + * the value to be null in addition to values of the underlying type. */ final class NullableType extends Expr instanceof NullableTypeImpl { } /** - * A ParenthesizedExpression expression in the AST. + * A parenthesized expression in the AST. + * + * Represents an expression enclosed within parentheses, such as `(1 + 2)`. + * Parentheses can be used to explicitly specify operator precedence or + * to improve code readability. */ class ParenthesizedExpression extends Expr instanceof ParenthesizedExpressionImpl { /** - * Gets the expression contained within the parentheses. + * Gets the expression at the specified index contained within the parentheses. + * + * @param index The index of the expression to retrieve + * @return The expression at the specified index */ Expr getExpression(int index) { result = ParenthesizedExpressionImpl.super.getExpression(index) } /** - * Get the expressions contained within the parentheses. + * Gets all expressions contained within the parentheses. + * + * This method returns all expressions enclosed by the parentheses, + * which could be a single expression or multiple expressions if the + * parentheses contain a comma-separated list. */ Expr getExpressions() { result = ParenthesizedExpressionImpl.super.getExpressions() @@ -137,21 +213,37 @@ class ParenthesizedExpression extends Expr instanceof ParenthesizedExpressionImp } /** - * A PrimaryExpression expression in the AST. + * A primary expression in the AST. + * + * Represents a basic, self-contained expression such as a literal value, + * identifier, or other fundamental expression type. Primary expressions + * serve as the building blocks for more complex expressions. */ final class PrimaryExpression extends Expr instanceof PrimaryExpressionImpl { } /** - * A ResourceExpression expression in the AST. + * A resource expression in the AST. + * + * Represents an expression that refers to or creates an Azure resource. + * Resource expressions are fundamental to Bicep as they define the + * infrastructure resources to be provisioned. */ final class ResourceExpression extends Expr instanceof ResourceExpressionImpl { } /** - * A TernaryExpression expression in the AST. + * A ternary expression in the AST. + * + * Represents a conditional (ternary) expression of the form `condition ? trueExpr : falseExpr`. + * The expression evaluates the condition and returns one of two values based on whether + * the condition is true or false. */ final class TernaryExpression extends Expr instanceof TernaryExpressionImpl { } /** - * A UnaryExpression expression in the AST. + * A unary expression in the AST. + * + * Represents an expression with a unary operator and a single operand. + * Examples include negation (`!expr`), numeric negation (`-expr`), + * and other operations that apply to a single value. */ final class UnaryExpression extends Expr instanceof UnaryExpressionImpl { } diff --git a/ql/lib/codeql/bicep/ast/Idents.qll b/ql/lib/codeql/bicep/ast/Idents.qll index 1611189..028da75 100644 --- a/ql/lib/codeql/bicep/ast/Idents.qll +++ b/ql/lib/codeql/bicep/ast/Idents.qll @@ -5,22 +5,49 @@ private import internal.Identifier private import internal.PropertyIdentifier /** - * A Idents AST node. + * The base class for all identifiers in the AST. + * + * This abstract class represents all types of identifiers in Bicep, including + * variable names, property names, parameter names, and resource identifiers. + * Identifiers are names that refer to declared entities in the program. */ abstract class Idents extends Expr instanceof IdentsImpl { + /** + * Gets the name of this identifier as a string. + * + * @return The name of this identifier + */ abstract string getName(); } /** - * A Identifier unknown AST node. + * A regular identifier in the AST. + * + * Represents a standard identifier that refers to a variable, parameter, resource, + * or other named entity in a Bicep program. For example, in an expression like + * `myVariable`, `myVariable` is represented by an Identifier. */ class Identifier extends Idents instanceof IdentifierImpl { + /** + * Gets the name of this identifier as a string. + * + * @return The name of this identifier + */ override string getName() { result = IdentifierImpl.super.getName() } } /** - * A PropertyIdentifier unknown AST node. + * A property identifier in the AST. + * + * Represents the name part of a property in an object literal. For example, + * in the property `name: 'example'`, `name` is represented by a PropertyIdentifier. + * Property identifiers are used as keys in object literals. */ class PropertyIdentifier extends Idents instanceof PropertyIdentifierImpl { + /** + * Gets the name of this property identifier as a string. + * + * @return The name of this property identifier + */ override string getName() { result = PropertyIdentifierImpl.super.getName() } } diff --git a/ql/lib/codeql/bicep/ast/Literals.qll b/ql/lib/codeql/bicep/ast/Literals.qll index 729c7fc..8c57fe7 100644 --- a/ql/lib/codeql/bicep/ast/Literals.qll +++ b/ql/lib/codeql/bicep/ast/Literals.qll @@ -19,37 +19,69 @@ private import internal.StringContent private import internal.SubscriptExpression /** - * A literal in the AST. + * A literal value in the AST. + * + * This is the base class for all literal values in Bicep, such as strings, + * numbers, booleans, null, and arrays. Literals represent fixed values that + * are directly expressed in the source code rather than computed. */ final class Literals extends AstNode instanceof LiteralsImpl { } /** - * A Array unknown AST node. + * An array literal in the AST. + * + * Represents an array literal in the Bicep language, which is a comma-separated + * list of expressions enclosed in square brackets, such as `[1, 2, 3]` or + * `['a', 'b', 'c']`. Arrays can contain elements of different types. */ class Array extends Literals instanceof ArrayImpl { + /** + * Gets all elements of the array. + * + * @return All expressions contained in the array literal + */ Expr getElements() { result = ArrayImpl.super.getElements() } + /** + * Gets the element at the specified index in the array. + * + * @param index The zero-based index of the element to retrieve + * @return The expression at the specified index in the array + */ Expr getElement(int index) { result = ArrayImpl.super.getElement(index) } } /** - * A SubscriptExpression expression in the AST. + * A subscript expression in the AST. + * + * Represents an array element access using the subscript notation, such as + * `array[0]` or `collection[indexExpr]`. A subscript expression consists of + * an array/collection and an index expression within square brackets. */ class SubscriptExpression extends Expr instanceof SubscriptExpressionImpl { /** - * Gets the index of the subscript expression. + * Gets the index expression of the subscript. + * + * This is the expression inside the square brackets that determines which + * element to access from the array or collection. */ Expr getIndex() { result = SubscriptExpressionImpl.super.getIndex() } /** - * Gets the object of the subscript expression. + * Gets the identifier of the object being subscripted. + * + * This is the name of the array or collection being accessed. */ Idents getIdentifier() { result = SubscriptExpressionImpl.super.getObject() } /** * Gets the array that this subscript expression is indexing into. - * This is equivalent to the variable declaration that contains the - * subscript expression. + * + * This method attempts to resolve the array by finding a variable declaration + * with a matching name in the same scope and retrieving its initializer if + * that initializer is an array. + * + * @return The array being indexed, if it can be determined */ Array getArray() { exists(VariableDeclaration variable | @@ -62,9 +94,18 @@ class SubscriptExpression extends Expr instanceof SubscriptExpressionImpl { } /** - * A Boolean unknown AST node. + * A boolean literal in the AST. + * + * Represents a boolean literal in Bicep, which can have the value `true` or `false`. + * Boolean literals are used in logical expressions, conditions, and other contexts + * where a truth value is needed. */ class Boolean extends Literals instanceof BooleanImpl { + /** + * Gets the boolean value of this literal as a QL boolean. + * + * @return `true` if the literal is the boolean value "true", `false` otherwise + */ boolean getBool() { exists(string bl | bl = BooleanImpl.super.getValue().toLowerCase() | bl = "true" and @@ -75,6 +116,13 @@ class Boolean extends Literals instanceof BooleanImpl { ) } + /** + * Gets the boolean value of this literal. + * + * This is an alias for getBool(). + * + * @return The boolean value of this literal + */ boolean getValue() { result = this.getBool() } @@ -82,34 +130,71 @@ class Boolean extends Literals instanceof BooleanImpl { /** * An alias for the Boolean literal in the AST. + * + * This provides a more descriptive name to match other literal classes. */ class BooleanLiteral = Boolean; /** - * A Null literal in the AST. + * A null literal in the AST. + * + * Represents the `null` literal in Bicep, which denotes the absence of a value + * or a reference that doesn't point to any object. Null literals are often used + * to indicate optional values or to represent uninitialized state. */ final class NullLiteral extends Literals instanceof NullImpl { } /** - * A NullableReturnType literal in the AST. + * A nullable return type literal in the AST. + * + * Represents a function return type annotation that indicates the function may + * return null in addition to values of another type. This typically appears in + * function declarations and is denoted with a question mark after the type name. */ final class NullableReturnTypeLiteral extends Literals instanceof NullableReturnTypeImpl { } /** - * A Number unknown AST node. + * A number literal in the AST. + * + * Represents a numeric literal in Bicep, such as `42`, `3.14`, or `-10`. + * Number literals can be used in arithmetic expressions, array indices, + * parameter values, and any other context where numeric values are expected. */ class Number extends Literals instanceof NumberImpl { + /** + * Gets the integer value of the number literal. + * + * Note: This method currently only handles integer values. If the number + * is a floating-point value, it will be converted to an integer. + * + * @return The integer value of the number literal + */ int getValue() { result = NumberImpl.super.getValue().toInt() } } +/** + * An alias for StringLiteral. + * + * This provides a shorter name for convenience. + */ class String = StringLiteral; /** - * A String literal in the AST. + * A string literal in the AST. + * + * Represents a string literal in Bicep, which can be a single-quoted string, + * double-quoted string, or a string with interpolated expressions. String literals + * may contain plain text content and/or interpolated expressions (${...}). */ class StringLiteral extends Literals instanceof StringImpl { /** * Gets the value of the string literal. + * + * This concatenates all the string contents and interpolated expressions + * to form the complete string value. For interpolated expressions, their + * string representation is included in the result. + * + * @return The complete string value */ string getValue() { result = @@ -128,12 +213,30 @@ class StringLiteral extends Literals instanceof StringImpl { ) } + /** + * Gets the interpolation expression at the specified index within the string. + * + * String literals can contain embedded expressions using the ${...} syntax. + * This method returns the interpolation at the given position in the string. + * + * @param index The index of the interpolation to retrieve + * @return The interpolation expression at that index + */ Interpolation getInterpolation(int index) { result = StringImpl.super.getChild(index) } } /** - * A StringContent literal in the AST. + * A string content literal in the AST. + * + * Represents a segment of plain text within a string literal, without any + * interpolation. String literals are composed of one or more StringContentLiteral + * instances, possibly interspersed with Interpolation expressions. */ class StringContentLiteral extends Literals instanceof StringContentImpl { + /** + * Gets the text value of this string content. + * + * @return The literal text content + */ string getValue() { result = StringContentImpl.super.getValue() } } diff --git a/ql/lib/codeql/bicep/ast/Misc.qll b/ql/lib/codeql/bicep/ast/Misc.qll index 6c31e2b..fe02bd5 100644 --- a/ql/lib/codeql/bicep/ast/Misc.qll +++ b/ql/lib/codeql/bicep/ast/Misc.qll @@ -124,11 +124,21 @@ class TargetScopeAssignment extends AstNode instanceof TargetScopeAssignmentImpl class TestBlock extends AstNode instanceof TestBlockImpl { } /** - * A Type unknown AST node. + * A type node in the AST. + * + * This class represents all type annotations in Bicep, including primitive types + * (like string, int, bool), complex types (like arrays, objects), and user-defined + * types. Types are used in parameter declarations, variable declarations, function + * return types, and other contexts to specify the kind of values that are expected. */ class Type extends AstNode instanceof TypeImpl { /** - * Returns the type of this AST node. + * Gets the name of this type as a string. + * + * For primitive types, this will be the name of the type (e.g., "string", "int"). + * For complex types, this will be a representation of the type structure. + * + * @return The type name or representation as a string */ string getType() { result = TypeImpl.super.getType() } } diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index a50a928..25503e9 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -8,15 +8,36 @@ private import internal.ObjectProperty private import internal.Object /** - * A Object unknown AST node. + * An object literal in the AST. + * + * Represents an object in Bicep, which is a collection of key-value pairs enclosed + * in curly braces, such as `{ name: 'example', value: 42 }`. Objects are commonly + * used to define resource properties, parameter defaults, and return values. */ class Object extends Expr instanceof ObjectImpl { + /** + * Gets all properties defined in this object. + * + * @return All property definitions in this object + */ ObjectProperty getProperties() { result = ObjectImpl.super.getProperties() } + /** + * Gets the property at the specified index. + * + * @param i The index of the property to retrieve + * @return The property at the specified index + */ ObjectProperty getProp(int i) { result = ObjectImpl.super.getProperty(i) } /** - * Get the value of a property by its name. + * Gets the value of a property by its name. + * + * This method searches for a property with the given name and returns its value. + * The property name can be specified either as an identifier or a string literal. + * + * @param name The name of the property to look for + * @return The value of the property, if found */ Expr getProperty(string name) { exists(ObjectProperty property | @@ -33,35 +54,76 @@ class Object extends Expr instanceof ObjectImpl { } /** - * A ObjectProperty unknown AST node. + * An object property in the AST. + * + * Represents a key-value pair in an object literal, consisting of a property name + * and a property value. The property name can be an identifier or a string literal, + * and the value can be any expression. */ class ObjectProperty extends Expr instanceof ObjectPropertyImpl { + /** + * Gets the name (key) of this property. + * + * @return The expression representing the property name + */ Expr getName() { result = ObjectPropertyImpl.super.getName() } + /** + * Gets the value of this property. + * + * @return The expression representing the property value + */ Expr getValue() { result = ObjectPropertyImpl.super.getValue() } } /** - * A ResourceDeclaration unknown AST node. + * A resource declaration in the AST. + * + * Represents a resource declaration in Bicep, which defines an Azure resource + * to be deployed. A resource declaration includes the resource type, an identifier + * for referencing the resource, and an object containing the resource properties. */ class ResourceDeclaration extends AstNode instanceof ResourceDeclarationImpl { /** - * The name of the resource instance + * Gets the identifier of the resource instance. + * + * This is the symbolic name used to reference this resource elsewhere + * in the Bicep file. For example, in `resource storageAccount '...' = { ... }`, + * the identifier would be `storageAccount`. */ Idents getIdentifier() { result = ResourceDeclarationImpl.super.getIdentifier() } /** - * The name of the resource instance. + * Gets the name literal of the resource type. + * + * This is the string literal that specifies the Azure resource type. + * For example, in `resource storageAccount 'Microsoft.Storage/storageAccounts@2021-02-01' = { ... }`, + * the name would be `'Microsoft.Storage/storageAccounts@2021-02-01'`. */ Literals getName() { result = ResourceDeclarationImpl.super.getName() } /** - * The object that represents the resource. + * Gets the object that contains the resource properties. + * + * This is the object literal that defines all the properties of the resource. + * It includes attributes like name, location, properties, and other resource-specific + * configuration settings. */ Object getBody() { result = ResourceDeclarationImpl.super.getObject() } + /** + * Gets all properties defined in the resource body. + * + * @return All property definitions in the resource body + */ ObjectProperty getProperties() { result = this.getBody().getProperties() } + /** + * Gets the value of a specific property in the resource body by name. + * + * @param name The name of the property to look for + * @return The value of the property, if found + */ Expr getProperty(string name) { result = this.getBody().getProperty(name) } } @@ -85,19 +147,57 @@ Resource resolveResource(Expr expr) { ) } +/** + * A high-level representation of an Azure resource. + * + * This class provides a more abstract view of Azure resources defined in Bicep, + * with convenient methods to access common resource properties like type, name, + * and parent-child relationships. + */ class Resource extends TResource { private ResourceDeclaration resource; Resource() { this = TResourceDeclaration(resource) } + /** + * Gets the resource type of this Azure resource. + * + * This is extracted from the resource type string literal in the resource declaration. + * For example, from a declaration like `resource vm 'Microsoft.Compute/virtualMachines@2021-03-01'`, + * this would return `"Microsoft.Compute/virtualMachines@2021-03-01"`. + * + * @return The resource type as a string + */ string getResourceType() { exists(StringLiteral sl | sl = resource.getName() | result = sl.getValue()) } + /** + * Gets the identifier of this resource. + * + * This is the symbolic name used to reference this resource within the Bicep file. + * + * @return The identifier of this resource + */ Identifier getIdentifier() { result = resource.getIdentifier() } + /** + * Gets the underlying resource declaration for this resource. + * + * This provides access to the full AST node representing the resource declaration. + * + * @return The ResourceDeclaration AST node + */ ResourceDeclaration getResourceDeclaration() { result = resource } + /** + * Gets the name of this resource as defined in its properties. + * + * This is different from the identifier. It's the actual deployment name of the + * resource that will be used in Azure, defined in the "name" property. + * + * @return The resource name as a string + */ string getName() { exists(StringLiteral name | name = resource.getProperty("name") and @@ -105,14 +205,43 @@ class Resource extends TResource { ) } + /** + * Gets the value of a specific property in this resource. + * + * @param name The name of the property to look for + * @return The value of the property, if found + */ Expr getProperty(string name) { result = resource.getProperty(name) } + /** + * Gets the parent resource of this resource, if any. + * + * This attempts to resolve the "parent" property to find the parent resource. + * Used for resources that have hierarchical relationships in Azure. + * + * @return The parent resource, if one exists + */ Resource getParent() { result = resolveResource(this.getProperty("parent")) } + /** + * Gets a string representation of this resource. + * + * @return A string representation of this resource + */ string toString() { result = resource.toString() } + /** + * Gets the primary QL class for this AST node. + * + * @return The name of the QL class ("Resource") + */ string getAPrimaryQlClass() { result = "Resource" } + /** + * Gets the location of this resource in the source code. + * + * @return The source location of this resource + */ Location getLocation() { result = resource.getLocation() } } diff --git a/ql/lib/codeql/bicep/ast/Stmts.qll b/ql/lib/codeql/bicep/ast/Stmts.qll index 685c833..1e4ef91 100644 --- a/ql/lib/codeql/bicep/ast/Stmts.qll +++ b/ql/lib/codeql/bicep/ast/Stmts.qll @@ -27,149 +27,353 @@ private import codeql.bicep.CFG private import codeql.bicep.controlflow.internal.ControlFlowGraphImpl as CfgImpl /** - * A Stmt block + * The base class for all statements in the AST. + * + * This class represents all types of statements in Bicep, including + * declarations, control flow statements, and other top-level constructs. + * Statements are the primary building blocks of Bicep programs. */ class Stmts extends AstNode instanceof StmtsImpl { - /** Gets a control-flow node for this statement, if any. */ + /** + * Gets a control-flow node for this statement, if any. + * + * Control-flow nodes represent this statement in the control-flow graph, + * which models the flow of execution through the program. + * + * @return A control-flow node for this statement + */ CfgImpl::AstCfgNode getAControlFlowNode() { result.getAstNode() = this } - /** Gets a control-flow entry node for this statement, if any */ + /** + * Gets a control-flow entry node for this statement, if any. + * + * Entry nodes represent the point where execution enters this statement + * in the control-flow graph. + * + * @return A control-flow entry node for this statement + */ AstNode getAControlFlowEntryNode() { result = CfgImpl::getAControlFlowEntryNode(this) } } /** - * A AssertStatement statement + * An assert statement in the AST. + * + * Represents an assertion in Bicep, which checks a condition at runtime + * and raises an error if the condition is false. Assert statements are + * useful for validating assumptions and ensuring inputs meet expected criteria. */ final class AssertStatementStmt extends Stmts instanceof AssertStatementImpl { } /** - * A ForStatement statement + * A for statement in the AST. + * + * Represents a for loop in Bicep, which iterates over a collection of items. + * For loops are commonly used for creating multiple instances of resources + * or for processing arrays of configuration data. */ final class ForStatementStmt extends Stmts instanceof ForStatementImpl { } /** - * A IfStatement statement + * An if statement in the AST. + * + * Represents a conditional statement in Bicep that executes certain code + * only when a specific condition is true. If statements enable conditional + * resource creation or property setting based on input parameters or other factors. */ class IfStatement extends Stmts instanceof IfStatementImpl { - /** Gets the condition of the if statement. */ + /** + * Gets the condition of the if statement. + * + * This is the expression that is evaluated to determine whether + * the body of the if statement should be executed. + * + * @return The condition expression + */ Expr getCondition() { result = IfStatementImpl.super.getCondition() } - /** Gets the body of the if statement. */ + /** + * Gets the body of the if statement. + * + * This is the expression or block that will be executed if the + * condition evaluates to true. + * + * @return The body expression + */ Expr getBody() { result = IfStatementImpl.super.getBody() } } /** - * A ImportStatement statement + * An import statement in the AST. + * + * Represents an import statement in Bicep, which brings external modules, + * namespaces, or resources into the current scope. Import statements enable + * code reuse and modularization of Bicep templates. */ final class ImportStatementStmt extends Stmts instanceof ImportStatementImpl { } /** - * A Infrastructure unknown AST node. + * An infrastructure node in the AST. + * + * Represents the root node of a Bicep file, which contains all the top-level + * statements and declarations that define the infrastructure to be deployed. + * This is essentially the entry point of a Bicep program. */ class Infrastructure extends AstNode instanceof InfrastructureImpl { - /** Gets the first statement in the infrastructure. */ + /** + * Gets the statement at the specified index in the infrastructure. + * + * @param index The index of the statement to retrieve + * @return The statement at the specified index + */ Stmts getStatement(int index) { result = InfrastructureImpl.super.getStatement(index) } } /** * Represents a parameter declaration node in the AST. - * Provides access to the identifier, name, type, and default value of the parameter. + * + * Parameters in Bicep are used to accept input values when deploying the template. + * They enable customization of deployments without modifying the template code. + * A parameter declaration includes a name, a type, and optionally a default value + * and/or allowed values. */ class ParameterDeclaration extends Stmts instanceof ParameterDeclarationImpl { - /** Gets the identifier of the parameter declaration. */ + /** + * Gets the identifier of the parameter declaration. + * + * This is the name token of the parameter as it appears in the source code. + * + * @return The identifier node of the parameter + */ Identifier getIdentifier() { result = ParameterDeclarationImpl.super.getName() } - /** Gets the name of the parameter declaration. */ + /** + * Gets the name of the parameter declaration as a string. + * + * This is a convenience method that returns the name from the identifier. + * + * @return The name of the parameter + */ string getName() { result = this.getIdentifier().getName() } - /** Gets the type of the parameter declaration. */ + /** + * Gets the type of the parameter declaration. + * + * This specifies what kind of values are allowed for this parameter, + * such as 'string', 'int', 'bool', or more complex types. + * + * @return The type node of the parameter + */ Type getType() { result = ParameterDeclarationImpl.super.getType() } - /** Gets the default value of the parameter declaration, if any. */ + /** + * Gets the default value of the parameter declaration, if any. + * + * If provided, this value will be used when no explicit value is + * provided during deployment. + * + * @return The default value expression, if one exists + */ Expr getDefaultValue() { result = ParameterDeclarationImpl.super.getDefaultValue() } } /** * Represents an output declaration node in the AST. - * Provides access to the identifier, name, type, and value of the output. + * + * Outputs in Bicep allow exposing values from the deployment, such as resource + * properties, calculated values, or other results. They are typically used to + * return information about deployed resources or to pass values to parent templates + * in nested deployments. */ class OutputDeclaration extends Stmts instanceof OutputDeclarationImpl { - /** Gets the identifier of the output declaration. */ + /** + * Gets the identifier of the output declaration. + * + * This is the name token of the output as it appears in the source code. + * + * @return The identifier node of the output + */ Identifier getIdentifier() { result = OutputDeclarationImpl.super.getIdentifier() } - /** Gets the name of the output declaration. */ + /** + * Gets the name of the output declaration as a string. + * + * This is a convenience method that returns the name from the identifier. + * + * @return The name of the output + */ string getName() { result = this.getIdentifier().getName() } - /** Gets the type of the output declaration. */ + /** + * Gets the type of the output declaration. + * + * This specifies what kind of value is being exposed by this output, + * such as 'string', 'int', 'bool', or more complex types. + * + * @return The type node of the output + */ Type getType() { result = OutputDeclarationImpl.super.getType() } - /** Gets the value of the output declaration. */ + /** + * Gets the value expression of the output declaration. + * + * This is the expression that will be evaluated to determine the + * value of the output when the template is deployed. + * + * @return The value expression of the output + */ Expr getValue() { result = OutputDeclarationImpl.super.getValue() } } /** - * Represents a user-defined function node in the AST. - * Provides access to the identifier, name, return type, parameters, and body of the function. + * Represents a user-defined function in the AST. + * + * User-defined functions allow creating reusable pieces of logic in Bicep templates. + * They encapsulate calculations or transformations that can be called from multiple + * places in the template, promoting code reuse and maintainability. */ class UserDefinedFunction extends Stmts instanceof UserDefinedFunctionImpl { - /** Gets the identifier of the user-defined function. */ + /** + * Gets the identifier of the user-defined function. + * + * This is the name token of the function as it appears in the source code. + * + * @return The identifier node of the function + */ Identifier getIdentifier() { result = UserDefinedFunctionImpl.super.getName() } - /** Gets the name of the user-defined function. */ + /** + * Gets the name of the user-defined function as a string. + * + * This is a convenience method that returns the name from the identifier. + * + * @return The name of the function + */ string getName() { result = this.getIdentifier().getName() } - /** Gets the return type of the user-defined function. */ + /** + * Gets the return type of the user-defined function. + * + * This specifies what kind of value the function returns, + * such as 'string', 'int', 'bool', or more complex types. + * + * @return The return type node of the function + */ Type getReturnType() { result = UserDefinedFunctionImpl.super.getReturnType() } - /** Gets the declared parameters of the user-defined function. */ + /** + * Gets the declared parameters node of the user-defined function. + * + * This contains all the parameter declarations for the function. + * + * @return The parameters node of the function + */ Parameters getDeclaredParameters() { result = UserDefinedFunctionImpl.super.getParameters() } - /** Gets all parameters of the user-defined function. */ + /** + * Gets all individual parameters of the user-defined function. + * + * @return All parameter nodes of the function + */ Parameter getParameters() { result = this.getDeclaredParameters().getParameter(_) } - /** Gets the parameter at the specified index. */ + /** + * Gets the parameter at the specified index. + * + * @param index The index of the parameter to retrieve + * @return The parameter at the specified index + */ Parameter getParameter(int index) { result = this.getDeclaredParameters().getParameter(index) } - /** Gets the body of the user-defined function. */ + /** + * Gets the body of the user-defined function. + * + * This is the expression that defines the computation performed by the function. + * When the function is called, this expression is evaluated with the provided + * argument values bound to the function parameters. + * + * @return The body expression of the function + */ Expr getBody() { result = UserDefinedFunctionImpl.super.getBody() } } /** - * Represents a parameter node in the AST. - * Provides access to the parameter's name and type. + * Represents a function parameter node in the AST. + * + * Parameters define the inputs that a function accepts. Each parameter has a name + * and a type, which constrains the kind of values that can be passed to the function + * for that parameter. */ class Parameter extends AstNode instanceof ParameterImpl { - /** Gets the name of the parameter. */ + /** + * Gets the identifier of the parameter. + * + * This is the name token of the parameter as it appears in the function declaration. + * + * @return The identifier node of the parameter + */ Idents getIdentifier() { result = ParameterImpl.super.getName() } + /** + * Gets the name of the parameter as a string. + * + * This is a convenience method that returns the name from the identifier. + * + * @return The name of the parameter + */ string getName() { result = this.getIdentifier().getName() } - /** Gets the type of the parameter. */ + /** + * Gets the type of the parameter. + * + * This specifies what kind of values are allowed for this parameter, + * such as 'string', 'int', 'bool', or more complex types. + * + * @return The type node of the parameter + */ Type getType() { result = ParameterImpl.super.getType() } } /** - * Represents a parameters node in the AST. - * Provides access to individual parameters by index. + * Represents a parameters collection node in the AST. + * + * This class represents the collection of parameters in a function declaration, + * allowing access to the individual parameter nodes. */ class Parameters extends AstNode instanceof ParametersImpl { - /** Gets the parameter at the specified index. */ + /** + * Gets the parameter at the specified index. + * + * @param index The zero-based index of the parameter to retrieve + * @return The parameter node at the specified index + */ Parameter getParameter(int index) { result = ParametersImpl.super.getParameter(index) } } /** - * A ImportWithStatement statement + * An import-with statement in the AST. + * + * Represents an import statement with additional configuration options in Bicep. + * Import-with statements allow importing external modules with specific settings + * or transformations applied to the imported items. */ final class ImportWithStatementStmt extends Stmts instanceof ImportWithStatementImpl { } /** - * A Statement statement + * A generic statement in the AST. + * + * This class represents any statement that doesn't fit into other more specific + * statement categories. It serves as a base implementation for statements + * in the Bicep language. */ final class StatementStmt extends Stmts instanceof StatementImpl { } /** - * A UsingStatement statement + * A using statement in the AST. + * + * Represents a using statement in Bicep, which introduces a local name for + * an imported module or namespace. Using statements help improve readability + * by allowing shorter references to external resources. */ final class UsingStatementStmt extends Stmts instanceof UsingStatementImpl { } diff --git a/ql/lib/codeql/bicep/ast/Variables.qll b/ql/lib/codeql/bicep/ast/Variables.qll index 40eb306..2469520 100644 --- a/ql/lib/codeql/bicep/ast/Variables.qll +++ b/ql/lib/codeql/bicep/ast/Variables.qll @@ -13,16 +13,31 @@ private import codeql.bicep.controlflow.ControlFlowGraph private import internal.VariableDeclaration /** - * A VariableDeclaration unknown AST node. + * Represents a variable declaration in the AST. + * + * This class models a variable declaration in Bicep, which associates a name + * with a value. Variable declarations allow storing and reusing values throughout + * a template, making the template more readable and maintainable. They can refer + * to literals, expressions, or other variables and resources. */ class VariableDeclaration extends AstNode instanceof VariableDeclarationImpl { /** * Gets the identifier of the variable declaration. + * + * This is the name token of the variable as it appears in the source code. + * + * @return The identifier node of the variable */ Idents getIdentifier() { result = VariableDeclarationImpl.super.getIdentifier() } /** * Gets the initializer expression of the variable declaration. + * + * This is the expression that provides the value for the variable. + * It could be a literal, a reference to another variable, a complex + * expression, or any other expression that produces a value. + * + * @return The initializer expression of the variable */ Expr getInitializer() { result = VariableDeclarationImpl.super.getInitializer() } } @@ -50,7 +65,12 @@ private predicate variableDecl(AstNode node, string name) { } /** - * Variable represents a variable defination. + * Represents a variable definition in the Bicep program. + * + * This class models any named entity that can be referenced elsewhere in the code, + * including parameters, variables, outputs, and resources. It provides a unified + * interface for accessing information about variables regardless of their specific + * declaration type. */ class Variable extends MkVariable { private AstNode node; @@ -58,24 +78,54 @@ class Variable extends MkVariable { Variable() { this = MkVariable(node, name) } + /** + * Gets the name of this variable. + * + * @return The name of the variable as a string + */ string getName() { result = name } + /** + * Gets a string representation of this variable. + * + * @return A string in the format "Variable[name]" + */ string toString() { result = "Variable[" + name + "]" } + /** + * Gets the AST node that defines this variable. + * + * This could be a parameter declaration, variable declaration, resource declaration, etc. + * + * @return The AST node that defines this variable + */ AstNode getAstNode() { result = node } /** - * Get the location of this variable. + * Gets the source location of this variable. + * + * This is the location of the AST node that defines the variable. + * + * @return The source location of the variable definition */ Location getLocation() { result = node.getLocation() } /** - * Geta the inner variable of this variable. + * Gets an access to this variable. + * + * This returns any usage of the variable in the code. + * + * @return A variable access that refers to this variable */ VariableAccess getAnAccess() { result.getVariable() = this } /** - * Gets the type of this variable, if any. + * Gets the type of this variable, if it can be determined. + * + * This method attempts to find the type by looking at the variable's declaration, + * either as a parameter or an output. + * + * @return The type of the variable, if available */ Type getType() { result = this.getParameter().getType() @@ -84,7 +134,9 @@ class Variable extends MkVariable { } /** - * Gets the parameter of this variable, if any. + * Gets the parameter declaration for this variable, if it is a parameter. + * + * @return The parameter declaration for this variable, if any */ ParameterDeclaration getParameter() { exists(ParameterDeclaration param | @@ -95,7 +147,9 @@ class Variable extends MkVariable { } /** - * Gets the variable declaration of this variable, if any. + * Gets the output declaration for this variable, if it is an output. + * + * @return The output declaration for this variable, if any */ OutputDeclaration getOutput() { exists(OutputDeclaration output | @@ -129,7 +183,11 @@ private predicate access(AstNode node, Variable v, string name) { } /** - * VariableAccess is a class that represents the access to a variable. + * Represents an access to a variable in the code. + * + * This class models any usage of a variable (parameter, declared variable, + * resource, output) within the Bicep code. It provides information about + * where the variable is accessed and which variable is being accessed. */ class VariableAccess extends MkVariableAccess, TVariableAccess { private string name; @@ -138,32 +196,76 @@ class VariableAccess extends MkVariableAccess, TVariableAccess { VariableAccess() { this = MkVariableAccess(node, v, name) } + /** + * Gets the name of the variable being accessed. + * + * @return The name of the variable as a string + */ string getName() { result = name } + /** + * Gets the AST node that represents this variable access. + * + * This is typically an identifier node in the syntax tree. + * + * @return The AST node for this variable access + */ AstNode getAstNode() { result = node } + /** + * Gets the variable being accessed. + * + * @return The variable that this access refers to + */ Variable getVariable() { result = v } + /** + * Gets a string representation of this variable access. + * + * @return A string in the format "VariableAccess[name]" + */ string toString() { result = "VariableAccess[" + name + "]" } /** - * Get the location of this variable. + * Gets the source location of this variable access. + * + * This is the location in the source code where the variable is referenced. + * + * @return The source location of the variable access */ Location getLocation() { result = node.getLocation() } /** - * Gets the type of this variable, if any. + * Gets the type of the variable being accessed, if it can be determined. + * + * @return The type of the variable, if available */ Type getType() { result = this.getVariable().getType() } /** - * Gets the enclosing scope of this variable, if any. + * Gets the enclosing CFG scope of this variable access. + * + * This is the control flow graph scope that contains this access. + * + * @return The enclosing CFG scope */ CfgScope getEnclosingCfgScope() { result = v.getEnclosingCfgScope() } + /** + * Gets the primary QL class for this AST node. + * + * @return The name of the QL class ("VariableAccess") + */ string getAPrimaryQlClass() { result = "VariableAccess" } } +/** + * Represents a write access to a variable. + * + * This class models places in the code where a variable's value is being + * defined or assigned. This includes declarations of parameters, variables, + * resources, and outputs. + */ class VariableWriteAccess extends VariableAccess { VariableWriteAccess() { // Parameter @@ -178,12 +280,29 @@ class VariableWriteAccess extends VariableAccess { this.getAstNode().getParent() instanceof OutputDeclaration } + /** + * Gets the primary QL class for this AST node. + * + * @return The name of the QL class ("VariableWrite") + */ override string getAPrimaryQlClass() { result = "VariableWrite" } } +/** + * Represents a read access to a variable. + * + * This class models places in the code where a variable's value is being + * used but not modified. This includes using the variable in expressions, + * passing it as an argument to functions, or referencing it in other contexts. + */ class VariableReadAccess extends VariableAccess { VariableReadAccess() { not this instanceof VariableWriteAccess } + /** + * Gets the primary QL class for this AST node. + * + * @return The name of the QL class ("VariableRead") + */ override string getAPrimaryQlClass() { result = "VariableRead" } }