Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | 24x 24x 15x 237x 237x 193x 193x 191x 191x 46x 145x 241x 241x 241x 241x 239x 239x 238x 238x 237x 237x 234x 234x 230x 230x 229x 229x 229x 229x 229x 229x 228x 228x 206x 206x 195x 207x 207x 128x 128x 84x 84x 83x 83x 66x 92x 93x 72x 93x 96x 73x 96x 120x 76x 120x 162x 100x 162x 163x 142x 163x 164x 143x 164x 165x 144x 165x 165x 145x 165x 167x 145x 167x 169x 147x 171x 171x 2x 169x 169x 169x 40x 20x 149x | /**
* Utility class for traversing expression trees in the parse tree.
*
* C-Next's expression grammar is deeply nested:
* expression -> ternaryExpression -> orExpression -> andExpression ->
* equalityExpression -> relationalExpression -> bitwiseOrExpression ->
* bitwiseXorExpression -> bitwiseAndExpression -> shiftExpression ->
* additiveExpression -> multiplicativeExpression -> unaryExpression ->
* postfixExpression -> primaryExpression -> literal/identifier
*
* This utility extracts common traversal patterns used by multiple analyzers.
*/
import * as Parser from "../transpiler/logic/parser/grammar/CNextParser";
/**
* Static utility methods for expression tree traversal
*/
class ExpressionUtils {
/**
* Extract the literal from a simple expression (if it's just a literal).
*
* Returns null if the expression is complex (has operators, function calls, etc.)
* Only returns a value if the expression resolves to a single literal.
*
* @param ctx - The expression context from the parse tree
* @returns The literal context, or null if not a simple literal expression
*/
static extractLiteral(
ctx: Parser.ExpressionContext,
): Parser.LiteralContext | null {
const primary = ExpressionUtils.extractPrimaryExpression(ctx);
if (!primary) return null;
return primary.literal() ?? null;
}
/**
* Extract the primary expression from an expression (if it's simple).
*
* Returns null if the expression is complex (has operators, function calls, etc.)
*
* @param ctx - The expression context from the parse tree
* @returns The primary expression context, or null if complex
*/
static extractPrimaryExpression(
ctx: Parser.ExpressionContext,
): Parser.PrimaryExpressionContext | null {
const unary = ExpressionUtils.extractUnaryExpression(ctx);
if (!unary) return null;
const postfix = unary.postfixExpression();
if (!postfix) return null;
// If there are postfix operations (array access, member access, function call),
// this is not a simple primary expression
const postfixOps = postfix.postfixOp();
if (postfixOps && postfixOps.length > 0) {
return null;
}
return postfix.primaryExpression() ?? null;
}
/**
* Extract the unary expression from an expression (if it's simple).
*
* Returns null if the expression has binary operators at any level.
*
* @param ctx - The expression context from the parse tree
* @returns The unary expression context, or null if complex
*/
static extractUnaryExpression(
ctx: Parser.ExpressionContext,
): Parser.UnaryExpressionContext | null {
// expression -> ternaryExpression
const ternary = ctx.ternaryExpression();
Iif (!ternary) return null;
// ternaryExpression has multiple orExpressions if it's a ternary (condition ? a : b)
const orExprs = ternary.orExpression();
if (orExprs?.length !== 1) return null;
// orExpression -> andExpression (multiple = has || operator)
const andExprs = orExprs[0].andExpression();
if (andExprs?.length !== 1) return null;
// andExpression -> equalityExpression (multiple = has && operator)
const eqExprs = andExprs[0].equalityExpression();
if (eqExprs?.length !== 1) return null;
// equalityExpression -> relationalExpression (multiple = has = or != operator)
const relExprs = eqExprs[0].relationalExpression();
if (relExprs?.length !== 1) return null;
// relationalExpression -> bitwiseOrExpression (multiple = has <, >, <=, >= operator)
const bitorExprs = relExprs[0].bitwiseOrExpression();
if (bitorExprs?.length !== 1) return null;
// bitwiseOrExpression -> bitwiseXorExpression (multiple = has | operator)
const bitxorExprs = bitorExprs[0].bitwiseXorExpression();
if (bitxorExprs?.length !== 1) return null;
// bitwiseXorExpression -> bitwiseAndExpression (multiple = has ^ operator)
const bitandExprs = bitxorExprs[0].bitwiseAndExpression();
Iif (bitandExprs?.length !== 1) return null;
// bitwiseAndExpression -> shiftExpression (multiple = has & operator)
const shiftExprs = bitandExprs[0].shiftExpression();
Iif (shiftExprs?.length !== 1) return null;
// shiftExpression -> additiveExpression (multiple = has << or >> operator)
const addExprs = shiftExprs[0].additiveExpression();
if (addExprs?.length !== 1) return null;
// additiveExpression -> multiplicativeExpression (multiple = has + or - operator)
const multExprs = addExprs[0].multiplicativeExpression();
if (multExprs?.length !== 1) return null;
// multiplicativeExpression -> unaryExpression (multiple = has *, /, % operator)
const unaryExprs = multExprs[0].unaryExpression();
if (unaryExprs?.length !== 1) return null;
return unaryExprs[0];
}
/**
* Extract the identifier from a simple expression (if it's just an identifier).
*
* Returns null if the expression is complex or not a simple identifier.
*
* @param ctx - The expression context from the parse tree
* @returns The identifier text, or null if not a simple identifier expression
*/
static extractIdentifier(ctx: Parser.ExpressionContext): string | null {
const primary = ExpressionUtils.extractPrimaryExpression(ctx);
if (!primary) return null;
const identifier = primary.IDENTIFIER();
return identifier?.getText() ?? null;
}
/**
* ADR-023: Check if an expression contains a function call anywhere in its tree.
*
* This traverses the full expression hierarchy to find any postfix expression
* with an argument list (function call syntax).
*
* @param ctx - The expression context to check
* @returns true if a function call is found anywhere in the expression
*/
static hasFunctionCall(ctx: Parser.ExpressionContext): boolean {
const ternary = ctx.ternaryExpression();
if (!ternary) return false;
for (const or of ternary.orExpression()) {
if (ExpressionUtils.hasFunctionCallInOr(or)) return true;
}
return false;
}
/**
* Issue #254: Check if an orExpression contains a function call.
*
* Used for ternary conditions where the condition is an OrExpressionContext.
*
* @param ctx - The orExpression context to check
* @returns true if a function call is found anywhere in the expression
*/
static hasFunctionCallInOr(ctx: Parser.OrExpressionContext): boolean {
for (const and of ctx.andExpression()) {
if (ExpressionUtils.hasFunctionCallInAnd(and)) return true;
}
return false;
}
private static hasFunctionCallInAnd(
ctx: Parser.AndExpressionContext,
): boolean {
for (const eq of ctx.equalityExpression()) {
if (ExpressionUtils.hasFunctionCallInEquality(eq)) return true;
}
return false;
}
private static hasFunctionCallInEquality(
ctx: Parser.EqualityExpressionContext,
): boolean {
for (const rel of ctx.relationalExpression()) {
if (ExpressionUtils.hasFunctionCallInRelational(rel)) return true;
}
return false;
}
private static hasFunctionCallInRelational(
ctx: Parser.RelationalExpressionContext,
): boolean {
for (const bor of ctx.bitwiseOrExpression()) {
if (ExpressionUtils.hasFunctionCallInBitwiseOr(bor)) return true;
}
return false;
}
private static hasFunctionCallInBitwiseOr(
ctx: Parser.BitwiseOrExpressionContext,
): boolean {
for (const bxor of ctx.bitwiseXorExpression()) {
if (ExpressionUtils.hasFunctionCallInBitwiseXor(bxor)) return true;
}
return false;
}
private static hasFunctionCallInBitwiseXor(
ctx: Parser.BitwiseXorExpressionContext,
): boolean {
for (const band of ctx.bitwiseAndExpression()) {
if (ExpressionUtils.hasFunctionCallInBitwiseAnd(band)) return true;
}
return false;
}
private static hasFunctionCallInBitwiseAnd(
ctx: Parser.BitwiseAndExpressionContext,
): boolean {
for (const shift of ctx.shiftExpression()) {
if (ExpressionUtils.hasFunctionCallInShift(shift)) return true;
}
return false;
}
private static hasFunctionCallInShift(
ctx: Parser.ShiftExpressionContext,
): boolean {
for (const add of ctx.additiveExpression()) {
if (ExpressionUtils.hasFunctionCallInAdditive(add)) return true;
}
return false;
}
private static hasFunctionCallInAdditive(
ctx: Parser.AdditiveExpressionContext,
): boolean {
for (const mult of ctx.multiplicativeExpression()) {
if (ExpressionUtils.hasFunctionCallInMultiplicative(mult)) return true;
}
return false;
}
private static hasFunctionCallInMultiplicative(
ctx: Parser.MultiplicativeExpressionContext,
): boolean {
for (const unary of ctx.unaryExpression()) {
if (ExpressionUtils.hasFunctionCallInUnary(unary)) return true;
}
return false;
}
private static hasFunctionCallInUnary(
ctx: Parser.UnaryExpressionContext,
): boolean {
// Issue #366: Handle nested unary operators (!, -, ~, &) that wrap function calls
// e.g., !!isReady() or -getValue()
const nestedUnary = ctx.unaryExpression();
if (nestedUnary) {
return ExpressionUtils.hasFunctionCallInUnary(nestedUnary);
}
// Base case: check postfixExpression
const postfix = ctx.postfixExpression();
Iif (!postfix) return false;
for (const op of postfix.postfixOp()) {
if (op.argumentList() || op.getText().startsWith("(")) {
return true;
}
}
return false;
}
}
export default ExpressionUtils;
|