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 | 485x 485x 485x 485x 50x 50x 2x 2x 52x 50x 50x 50x 1x 1x 49x 8x 8x 7x 485x 485x 485x 485x | /**
* An array dimension that is not a constant expression: E0909, E0910
* (#1175; ADR-023 "no VLAs", ADR-044 "Values fixed at compile time").
*
* Every dimension, wherever the grammar has one -- a declaration at any
* scope, a struct field, a parameter, a `for` header -- because the check
* listens on the dimension nodes themselves rather than on the declarations
* that hold them. A rule enforced at some positions and silent at others is
* how this card's defects survived: a parameter-sized local reached C as an
* initialized variable-length array, at exit 0.
*
* A dimension C can evaluate and C-Next cannot (a header macro) is C's, and
* passes. One that has no value is rejected here, so render never sees it.
*/
import { ParseTreeWalker } from "antlr4ng";
import { CNextListener } from "../../PARSE/2-Parse/grammar/CNextListener";
import * as Parser from "../../PARSE/2-Parse/grammar/CNextParser";
import ConstExprLowering from "../../utils/ConstExprLowering";
import ConstantEvaluator from "../../utils/ConstantEvaluator";
import ConstantFold from "../../utils/ConstantFold";
import ParserUtils from "../../utils/ParserUtils";
import ConstantDiagnostics from "./helpers/ConstantDiagnostics";
import type IAnalysisContext from "./types/IAnalysisContext";
import type IConstantDimensionError from "./types/IConstantDimensionError";
class ConstantDimensionListener extends CNextListener {
private readonly found: IConstantDimensionError[] = [];
// eslint-disable-next-line @typescript-eslint/lines-between-class-members
constructor(private readonly context: IAnalysisContext) {
super();
}
public errors(): IConstantDimensionError[] {
return this.found;
}
override enterArrayTypeDimension = (
ctx: Parser.ArrayTypeDimensionContext,
): void => {
this.check(ctx.expression());
};
override enterArrayDimension = (ctx: Parser.ArrayDimensionContext): void => {
this.check(ctx.expression());
};
private check(expression: Parser.ExpressionContext | null): void {
if (!expression) return; // an unsized `[]` is E0892's
const result = ConstantEvaluator.evaluate(
ConstExprLowering.lower(expression),
ConstantFold.environment(this.context.program, this.context.sourceFile),
);
const at = ParserUtils.getPosition(expression);
if (result.kind === "overflow") {
this.found.push({
code: "E0910",
...at,
message: `Array dimension overflows ${result.typeName} at compile time: the arithmetic would clamp or wrap (ADR-044)`,
helpText: ConstantDiagnostics.OVERFLOW_HELP,
});
return;
}
if (result.kind !== "notConstant") return;
const why = ConstantDiagnostics.why(result);
if (why === null) return;
this.found.push({
code: "E0909",
...at,
message: `Array dimension must be known at compile time: ${why}`,
helpText:
"An array's size is built from literals, consts, sizeof and casts (ADR-023). C-Next has no variable-length arrays",
});
}
}
class ConstantDimensionAnalyzer {
constructor(private readonly context: IAnalysisContext) {}
public analyze(tree: Parser.ProgramContext): IConstantDimensionError[] {
const listener = new ConstantDimensionListener(this.context);
ParseTreeWalker.DEFAULT.walk(listener, tree);
return listener.errors();
}
}
export default ConstantDimensionAnalyzer;
|