@typecad/cuttlefish 1.0.0-alpha.7 → 1.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/config.d.ts +32 -0
- package/dist/api/index.d.ts +1 -1
- package/dist/api/schema/types.d.ts +15 -0
- package/dist/api/shared/async-runtime-static.d.ts +12 -1
- package/dist/api/shared/async-runtime-static.js +69 -24
- package/dist/api/shared/async-symbol-detector.d.ts +11 -0
- package/dist/api/shared/async-symbol-detector.js +140 -0
- package/dist/api/shared/async-types.d.ts +24 -0
- package/dist/api/shared/coop-scheduler.d.ts +60 -0
- package/dist/api/shared/coop-scheduler.js +149 -0
- package/dist/api/shared/display-adapter.d.ts +2 -2
- package/dist/api/shared/display-adapter.js +9 -119
- package/dist/api/shared/display-adapters/sdl.js +29 -29
- package/dist/api/shared/display-profile.d.ts +38 -33
- package/dist/api/shared/display-profile.js +22 -170
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +8 -6
- package/dist/api/shared/framework-manifest.d.ts +147 -28
- package/dist/api/shared/framework-manifest.js +35 -2
- package/dist/api/shared/graphics-strategy.d.ts +15 -6
- package/dist/api/shared/hal-op-ir.d.ts +340 -3
- package/dist/api/shared/hal-op-ir.js +34 -0
- package/dist/api/shared/index.d.ts +9 -1
- package/dist/api/shared/index.js +11 -1
- package/dist/api/shared/ir-core.d.ts +4 -0
- package/dist/api/shared/ir-declarations.d.ts +6 -0
- package/dist/api/shared/platform-strategy.d.ts +104 -2
- package/dist/api/shared/polyfill-helper-registry.js +18 -1
- package/dist/api/shared/promise-runtime.d.ts +1 -1
- package/dist/api/shared/promise-runtime.js +17 -13
- package/dist/api/shared/toolchain-types.d.ts +17 -0
- package/dist/api/shared/validate-framework-manifest.js +77 -0
- package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
- package/dist/api/shared/worker-runtime-polyfill.js +34 -0
- package/dist/api/shared/worker-runtime.d.ts +69 -0
- package/dist/api/shared/worker-runtime.js +163 -0
- package/dist/cli.js +125 -121
- package/dist/config-loader.d.ts +7 -2
- package/dist/config-loader.js +20 -19
- package/dist/config-schema.d.ts +107 -65
- package/dist/config-schema.js +14 -0
- package/dist/contract/board-generator.d.ts +17 -0
- package/dist/contract/board-generator.js +57 -0
- package/dist/contract/contract-parser.d.ts +217 -0
- package/dist/contract/contract-parser.js +224 -0
- package/dist/contract/index.d.ts +22 -0
- package/dist/contract/index.js +68 -0
- package/dist/create/board-codegen.js +4 -4
- package/dist/create/board-generators.js +4 -5
- package/dist/create/board-spec.d.ts +74 -77
- package/dist/create/board-spec.js +0 -1
- package/dist/create/init-scaffold.d.ts +7 -3
- package/dist/create/init-scaffold.js +0 -62
- package/dist/create/init-templates.js +62 -16
- package/dist/create/init-wizard.js +32 -27
- package/dist/debug/preprocessor.js +178 -39
- package/dist/debug/types.d.ts +23 -0
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +34 -24
- package/dist/emit/compliance/arxml-writer.d.ts +11 -0
- package/dist/emit/compliance/arxml-writer.js +34 -0
- package/dist/emit/compliance/compliance-context.d.ts +57 -0
- package/dist/emit/compliance/compliance-context.js +113 -0
- package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
- package/dist/emit/compliance/deviation-ledger.js +47 -0
- package/dist/emit/compliance/deviation-writer.d.ts +30 -0
- package/dist/emit/compliance/deviation-writer.js +37 -0
- package/dist/emit/compliance/index.d.ts +7 -0
- package/dist/emit/compliance/index.js +6 -0
- package/dist/emit/compliance/rule-engine.d.ts +13 -0
- package/dist/emit/compliance/rule-engine.js +101 -0
- package/dist/emit/compliance/rules.d.ts +16 -0
- package/dist/emit/compliance/rules.js +191 -0
- package/dist/emit/compliance/types.d.ts +59 -0
- package/dist/emit/compliance/types.js +8 -0
- package/dist/emit/emitters/class-emitter.js +6 -1
- package/dist/emit/emitters/emitter-context.d.ts +23 -1
- package/dist/emit/emitters/function-emitter-impl.js +96 -6
- package/dist/emit/emitters/line-appender.js +19 -0
- package/dist/emit/emitters/line-marker.d.ts +38 -0
- package/dist/emit/emitters/line-marker.js +39 -0
- package/dist/emit/emitters/output-finalizer.js +80 -1
- package/dist/emit/emitters/setup.d.ts +17 -0
- package/dist/emit/emitters/setup.js +217 -36
- package/dist/emit/emitters/top-level-prep.js +6 -0
- package/dist/emit/emitters/type-decl-emitter.js +28 -3
- package/dist/emit/emitters/ui-emitter.js +17 -8
- package/dist/emit/expression-renderer.d.ts +1 -1
- package/dist/emit/expression-renderer.js +81 -5
- package/dist/emit/route-hal-op.js +18 -5
- package/dist/emit/snprintf-helpers.js +7 -4
- package/dist/emit/statement-renderer.d.ts +10 -0
- package/dist/emit/statement-renderer.js +21 -5
- package/dist/emit/utils/async-state-machine.js +39 -12
- package/dist/framework-package.js +2 -0
- package/dist/framework-registry.d.ts +17 -0
- package/dist/install/framework-catalog.d.ts +53 -0
- package/dist/install/framework-catalog.js +107 -0
- package/dist/install/handle-install.d.ts +35 -0
- package/dist/install/handle-install.js +177 -0
- package/dist/install/index.d.ts +4 -0
- package/dist/install/index.js +3 -0
- package/dist/ir/adc-range-validation.js +14 -1
- package/dist/ir/build-ir-state.d.ts +1 -0
- package/dist/ir/build-ir-state.js +14 -0
- package/dist/ir/build-ir.js +4 -4
- package/dist/ir/call-graph.js +16 -0
- package/dist/ir/expression-to-ir.js +150 -0
- package/dist/ir/function-builder.js +22 -0
- package/dist/ir/hal/hal-emitter.js +10 -6
- package/dist/ir/hal/hal-parser.js +16 -9
- package/dist/ir/hal/hal-plugins.js +164 -3
- package/dist/ir/identifier-collector.js +21 -4
- package/dist/ir/interrupt-analysis.d.ts +5 -1
- package/dist/ir/interrupt-analysis.js +8 -16
- package/dist/ir/ownership-analysis.js +6 -0
- package/dist/ir/pin-capability-validation.js +37 -0
- package/dist/ir/pin-mode-validation.d.ts +2 -2
- package/dist/ir/pin-mode-validation.js +49 -18
- package/dist/ir/program-analysis.d.ts +42 -0
- package/dist/ir/program-analysis.js +243 -6
- package/dist/ir/render-expr.js +11 -0
- package/dist/ir/timing-validation.js +11 -2
- package/dist/ir/transformers/call-statement.js +105 -0
- package/dist/ir/transformers/expressions.js +4 -0
- package/dist/ir/transformers/hal-call-resolver.js +21 -0
- package/dist/ir/transformers/namespace-methods.js +17 -12
- package/dist/ir/transformers/ui-reactive.js +2 -2
- package/dist/ir/transformers/variables.js +51 -0
- package/dist/ir/type-resolution.js +18 -0
- package/dist/ir/ui-element-auto-wire.js +7 -5
- package/dist/ir/utils/map-statements.d.ts +4 -0
- package/dist/ir/utils/map-statements.js +79 -0
- package/dist/ir/validation-orchestrator.js +6 -1
- package/dist/ir/worker-analysis.d.ts +10 -0
- package/dist/ir/worker-analysis.js +261 -0
- package/dist/orchestrator/graph-builder.js +8 -7
- package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
- package/dist/platform/coop-scheduler-runtime.js +41 -0
- package/dist/platform/generic-strategy.d.ts +15 -3
- package/dist/platform/generic-strategy.js +49 -4
- package/dist/safety/safety-bridge.d.ts +11 -0
- package/dist/safety/safety-bridge.js +48 -0
- package/dist/safety/sidecar-bridge.d.ts +5 -0
- package/dist/safety/sidecar-bridge.js +22 -0
- package/dist/safety-hook.d.ts +79 -0
- package/dist/safety-hook.js +35 -0
- package/dist/stores/display-profile-store.d.ts +0 -1
- package/dist/stores/display-profile-store.js +0 -1
- package/dist/testing.d.ts +4 -0
- package/dist/testing.js +5 -0
- package/dist/transpile.js +124 -35
- package/dist/types.d.ts +22 -2
- package/dist/ui-hook.d.ts +0 -2
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +68 -3
- package/package.json +20 -5
- package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
- package/dist/api/shared/display-adapters/eink-mono.js +0 -53
- package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
- package/dist/api/shared/display-adapters/ssd1309.js +0 -136
- package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
- package/dist/api/shared/display-adapters/st7796.js +0 -132
- package/dist/libdef/idf-discovery.d.ts +0 -7
- package/dist/libdef/idf-discovery.js +0 -59
- package/dist/licenses.d.ts +0 -185
- package/dist/licenses.js +0 -963
|
@@ -40,6 +40,12 @@ export function preprocess(options) {
|
|
|
40
40
|
// Collect variables in scope at each breakpoint
|
|
41
41
|
const scopeAnalyzer = new ScopeAnalyzer(sf);
|
|
42
42
|
const debugStrategy = getDebugStrategy();
|
|
43
|
+
// Per-file breakpoint ID counter. Each HALTING breakpoint gets a stable
|
|
44
|
+
// integer ID so the codegen can emit a per-breakpoint disable flag (skipped
|
|
45
|
+
// via the 's' key at runtime). IDs are scoped to this file and reset per
|
|
46
|
+
// preprocess() call, so they stay small and deterministic across builds.
|
|
47
|
+
// Logpoints never halt and don't need an ID.
|
|
48
|
+
let nextBreakpointId = 0;
|
|
43
49
|
// Find the first non-import, non-comment line to insert debug init
|
|
44
50
|
let insertIndex = 0;
|
|
45
51
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -67,8 +73,12 @@ export function preprocess(options) {
|
|
|
67
73
|
const displayLine = lineText.trim();
|
|
68
74
|
// Collect variables in scope at this line
|
|
69
75
|
const vars = scopeAnalyzer.getVariablesInScope(lineNum);
|
|
76
|
+
// Halting breakpoints get a per-file ID (used by the codegen to emit a
|
|
77
|
+
// disable flag the user can toggle at runtime with the 's' key). Logpoints
|
|
78
|
+
// never halt, so they pass no ID.
|
|
79
|
+
const breakpointId = bp.logMessage ? undefined : nextBreakpointId++;
|
|
70
80
|
// Inject debug code BEFORE the breakpoint line
|
|
71
|
-
outputLines.push(...generateBreakpointCode(relativeFileName, lineNum + 1, displayLine, vars, bp, debugStrategy));
|
|
81
|
+
outputLines.push(...generateBreakpointCode(relativeFileName, lineNum + 1, displayLine, vars, bp, debugStrategy, breakpointId));
|
|
72
82
|
}
|
|
73
83
|
// Always include the original line
|
|
74
84
|
outputLines.push(lineText);
|
|
@@ -87,14 +97,14 @@ export function preprocess(options) {
|
|
|
87
97
|
* Generate debug code for a breakpoint.
|
|
88
98
|
* Delegates to the loaded strategy when available.
|
|
89
99
|
*/
|
|
90
|
-
function generateBreakpointCode(fileName, lineNum, originalLine, variables, bp, debugStrategy) {
|
|
100
|
+
function generateBreakpointCode(fileName, lineNum, originalLine, variables, bp, debugStrategy, breakpointId) {
|
|
91
101
|
// If this is a logpoint (has logMessage), generate log-only code
|
|
92
102
|
if (bp.logMessage) {
|
|
93
103
|
const parts = parseLogMessage(bp.logMessage);
|
|
94
104
|
if (debugStrategy.generateDebugLogpointCode) {
|
|
95
105
|
return debugStrategy.generateDebugLogpointCode({
|
|
96
106
|
fileName, lineNum, parts,
|
|
97
|
-
variables: variables.map(v => ({ name: v.name, isFunction: v.isFunction })),
|
|
107
|
+
variables: variables.map(v => ({ name: v.name, isFunction: v.isFunction, cppType: v.cppType })),
|
|
98
108
|
});
|
|
99
109
|
}
|
|
100
110
|
return [];
|
|
@@ -104,8 +114,9 @@ function generateBreakpointCode(fileName, lineNum, originalLine, variables, bp,
|
|
|
104
114
|
if (debugStrategy.generateDebugBreakpointCode) {
|
|
105
115
|
return debugStrategy.generateDebugBreakpointCode({
|
|
106
116
|
fileName, lineNum, originalLine,
|
|
107
|
-
variables: variables.map(v => ({ name: v.name, isFunction: v.isFunction })),
|
|
117
|
+
variables: variables.map(v => ({ name: v.name, isFunction: v.isFunction, cppType: v.cppType })),
|
|
108
118
|
normalizedCondition,
|
|
119
|
+
breakpointId,
|
|
109
120
|
});
|
|
110
121
|
}
|
|
111
122
|
return [];
|
|
@@ -162,52 +173,180 @@ class ScopeAnalyzer {
|
|
|
162
173
|
if (this.functionVars.has(line)) {
|
|
163
174
|
return this.functionVars.get(line);
|
|
164
175
|
}
|
|
165
|
-
|
|
176
|
+
// Module-scope (top-level): filter by declaration order so breakpoints
|
|
177
|
+
// before a variable's declaration don't reference it (use-before-declare).
|
|
178
|
+
const topVars = this.functionVars.get(-1) || [];
|
|
179
|
+
return topVars.filter(v => v.declLine === undefined || v.declLine < line);
|
|
166
180
|
}
|
|
167
181
|
analyze() {
|
|
182
|
+
// Top-level (module-scope) variables. Iterating sf.statements is reliable
|
|
183
|
+
// across TS statement-classification variants (a bare `let x = 0;` can be
|
|
184
|
+
// parsed as either VariableStatement or FirstStatement; both expose
|
|
185
|
+
// .declarationList, so read it uniformly).
|
|
168
186
|
const topLevelVars = [];
|
|
169
|
-
|
|
187
|
+
for (const stmt of this.sf.statements) {
|
|
188
|
+
this.collectVariablesFromStatement(stmt, topLevelVars);
|
|
189
|
+
}
|
|
170
190
|
this.functionVars.set(-1, topLevelVars);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
// Record a scope entry for every line inside each function body, capturing
|
|
192
|
+
// enclosing-scope vars + parameters + body locals. Nested functions get
|
|
193
|
+
// their own entries that shadow the parent on their line range.
|
|
194
|
+
//
|
|
195
|
+
// Per-line scope filtering: body locals are only in scope on lines STRICTLY
|
|
196
|
+
// AFTER their declaration line. The breakpoint dump is injected at the
|
|
197
|
+
// START of the breakpoint line (before that line's own declaration runs),
|
|
198
|
+
// so a local declared on line N is not yet in scope at line N. Params and
|
|
199
|
+
// enclosing-scope vars are in scope from the function's first line.
|
|
200
|
+
const recordFunction = (node, currentVars) => {
|
|
201
|
+
const startLine = this.sf.getLineAndCharacterOfPosition(node.getStart()).line;
|
|
202
|
+
const endLine = node.body
|
|
203
|
+
? this.sf.getLineAndCharacterOfPosition(node.body.getEnd()).line
|
|
204
|
+
: startLine;
|
|
205
|
+
// Always-in-scope from the function's first line: enclosing vars + params.
|
|
206
|
+
const alwaysInScope = [...currentVars];
|
|
207
|
+
for (const param of node.parameters) {
|
|
208
|
+
if (ts.isIdentifier(param.name)) {
|
|
209
|
+
alwaysInScope.push({
|
|
210
|
+
name: param.name.text,
|
|
211
|
+
// Params have no initializer; infer from the annotation only.
|
|
212
|
+
cppType: this.inferCppTypeFromAnnotation(param.type),
|
|
213
|
+
});
|
|
188
214
|
}
|
|
189
|
-
return localVars;
|
|
190
215
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
216
|
+
// Body locals carry their own declaration line for per-line filtering.
|
|
217
|
+
const bodyLocals = [];
|
|
218
|
+
if (node.body) {
|
|
219
|
+
this.collectVariablesInBody(node.body, bodyLocals, recordFunction);
|
|
220
|
+
}
|
|
221
|
+
for (let line = startLine; line <= endLine; line++) {
|
|
222
|
+
// A body local is in scope at `line` only if declared on a strictly
|
|
223
|
+
// earlier line (declLine < line). This prevents both compile errors
|
|
224
|
+
// (use-before-declaration on the declaring line) and runtime reads of
|
|
225
|
+
// uninitialized locals at breakpoints above the declaration.
|
|
226
|
+
const localsInScope = bodyLocals.filter(v => v.declLine === undefined || v.declLine < line);
|
|
227
|
+
this.functionVars.set(line, [...alwaysInScope, ...localsInScope]);
|
|
203
228
|
}
|
|
229
|
+
};
|
|
230
|
+
// Find top-level functions, then recurse into their bodies for nested ones.
|
|
231
|
+
for (const stmt of this.sf.statements) {
|
|
232
|
+
this.findAndRecordFunctions(stmt, topLevelVars, recordFunction);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/** Collect variables declared directly by a single statement (top-level use). */
|
|
236
|
+
collectVariablesFromStatement(stmt, vars) {
|
|
237
|
+
if (ts.isVariableStatement(stmt) && stmt.declarationList) {
|
|
238
|
+
this.collectFromDeclarationList(stmt.declarationList, vars);
|
|
204
239
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
240
|
+
else if (this.isFirstStatementVariableDeclaration(stmt)) {
|
|
241
|
+
this.collectFromDeclarationList(stmt.declarationList, vars);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Walk a function body collecting locals, recursing into nested functions so
|
|
246
|
+
* they get their own scope entry (without leaking their locals to the parent).
|
|
247
|
+
*/
|
|
248
|
+
collectVariablesInBody(body, vars, recordFunction) {
|
|
249
|
+
ts.forEachChild(body, (child) => {
|
|
250
|
+
if (ts.isFunctionDeclaration(child) || ts.isArrowFunction(child) || ts.isMethodDeclaration(child)) {
|
|
251
|
+
// Nested function: record its scope, but do not add its name to this
|
|
252
|
+
// scope's printable vars (it's a function value).
|
|
253
|
+
recordFunction(child, vars);
|
|
254
|
+
return;
|
|
208
255
|
}
|
|
256
|
+
this.collectVariablesFromStatement(child, vars);
|
|
257
|
+
this.collectVariablesInBody(child, vars, recordFunction);
|
|
209
258
|
});
|
|
210
259
|
}
|
|
260
|
+
/** Recurse looking for function declarations to record. */
|
|
261
|
+
findAndRecordFunctions(node, currentVars, recordFunction) {
|
|
262
|
+
if (ts.isFunctionDeclaration(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node)) {
|
|
263
|
+
recordFunction(node, currentVars);
|
|
264
|
+
return; // recordFunction already walks the body for nested functions.
|
|
265
|
+
}
|
|
266
|
+
ts.forEachChild(node, (child) => this.findAndRecordFunctions(child, currentVars, recordFunction));
|
|
267
|
+
}
|
|
268
|
+
collectFromDeclarationList(list, vars) {
|
|
269
|
+
for (const decl of list.declarations) {
|
|
270
|
+
if (ts.isIdentifier(decl.name)) {
|
|
271
|
+
const isFunction = decl.initializer !== undefined && this.isFunctionExpression(decl.initializer);
|
|
272
|
+
// declLine is the 0-indexed source line of the declaration. Used by
|
|
273
|
+
// recordFunction to filter body locals per line (declLine < line).
|
|
274
|
+
const declLine = this.sf.getLineAndCharacterOfPosition(decl.getStart()).line;
|
|
275
|
+
vars.push({
|
|
276
|
+
name: decl.name.text,
|
|
277
|
+
isFunction,
|
|
278
|
+
declLine,
|
|
279
|
+
cppType: isFunction ? undefined : this.inferCppType(decl),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Infer a coarse C++ type category from an explicit `: T` type annotation
|
|
286
|
+
* node. Returns `unknown` when there is no annotation or it isn't a primitive
|
|
287
|
+
* keyword we recognize.
|
|
288
|
+
*/
|
|
289
|
+
inferCppTypeFromAnnotation(typeNode) {
|
|
290
|
+
if (!typeNode)
|
|
291
|
+
return 'unknown';
|
|
292
|
+
if (typeNode.kind === ts.SyntaxKind.BooleanKeyword)
|
|
293
|
+
return 'bool';
|
|
294
|
+
if (typeNode.kind === ts.SyntaxKind.StringKeyword)
|
|
295
|
+
return 'string';
|
|
296
|
+
if (typeNode.kind === ts.SyntaxKind.NumberKeyword)
|
|
297
|
+
return 'float'; // TS number → %g is the safe choice
|
|
298
|
+
if (typeNode.kind === ts.SyntaxKind.BigIntKeyword)
|
|
299
|
+
return 'long';
|
|
300
|
+
return 'unknown';
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Infer a coarse C++ type category from a variable declaration, WITHOUT a
|
|
304
|
+
* TypeChecker (the debug path is parse-only). Priority:
|
|
305
|
+
* 1. Explicit `: T` annotation (authoritative).
|
|
306
|
+
* 2. Initializer shape (literal kind, or a small allowlist of known HAL
|
|
307
|
+
* calls like millis()/micros() → long).
|
|
308
|
+
* 3. `unknown` — printf codegens cast to double + %g so it always compiles.
|
|
309
|
+
*
|
|
310
|
+
* Function-valued vars are handled by the caller (isFunction); this returns
|
|
311
|
+
* unknown for them but the caller skips emission of function-typed vars.
|
|
312
|
+
*/
|
|
313
|
+
inferCppType(decl) {
|
|
314
|
+
// 1. Explicit type annotation wins.
|
|
315
|
+
const fromAnnotation = this.inferCppTypeFromAnnotation(decl.type);
|
|
316
|
+
if (fromAnnotation !== 'unknown')
|
|
317
|
+
return fromAnnotation;
|
|
318
|
+
// 2. Initializer shape.
|
|
319
|
+
const init = decl.initializer;
|
|
320
|
+
if (init) {
|
|
321
|
+
if (init.kind === ts.SyntaxKind.TrueKeyword || init.kind === ts.SyntaxKind.FalseKeyword)
|
|
322
|
+
return 'bool';
|
|
323
|
+
if (ts.isStringLiteral(init))
|
|
324
|
+
return 'string';
|
|
325
|
+
if (ts.isNoSubstitutionTemplateLiteral(init) || ts.isTemplateExpression(init))
|
|
326
|
+
return 'string';
|
|
327
|
+
if (ts.isNumericLiteral(init)) {
|
|
328
|
+
// Integer literal unless it contains '.' or an exponent marker.
|
|
329
|
+
return /[.eE]/.test(init.text) ? 'float' : 'int';
|
|
330
|
+
}
|
|
331
|
+
if (ts.isCallExpression(init)) {
|
|
332
|
+
// A small allowlist of HAL calls with known C++ return types.
|
|
333
|
+
const callee = ts.isIdentifier(init.expression) ? init.expression.text : '';
|
|
334
|
+
if (callee === 'millis' || callee === 'micros')
|
|
335
|
+
return 'long';
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// 3. Anything else (references, binary expressions, unknown calls, …).
|
|
339
|
+
return 'unknown';
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* TS sometimes parses a bare `let x = 0;` as a FirstStatement node rather
|
|
343
|
+
* than a VariableStatement. Detect that case by shape (has declarationList).
|
|
344
|
+
*/
|
|
345
|
+
isFirstStatementVariableDeclaration(node) {
|
|
346
|
+
if (node.kind !== ts.SyntaxKind.FirstStatement)
|
|
347
|
+
return false;
|
|
348
|
+
return Object.prototype.hasOwnProperty.call(node, 'declarationList');
|
|
349
|
+
}
|
|
211
350
|
isFunctionExpression(expr) {
|
|
212
351
|
return ts.isArrowFunction(expr) || ts.isFunctionExpression(expr);
|
|
213
352
|
}
|
package/dist/debug/types.d.ts
CHANGED
|
@@ -6,7 +6,30 @@ export interface CapturedVariable {
|
|
|
6
6
|
name: string;
|
|
7
7
|
/** Whether this is a function (cannot print value) */
|
|
8
8
|
isFunction?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Coarse C++ type category, inferred from the declaration's annotation or
|
|
11
|
+
* initializer shape (no TypeChecker is available in the debug path). Used by
|
|
12
|
+
* printf-based codegens (ESP-IDF) to pick the right format specifier; ignored
|
|
13
|
+
* by type-agnostic codegens (Arduino's Serial.println).
|
|
14
|
+
*/
|
|
15
|
+
cppType?: DebugCppType;
|
|
16
|
+
/**
|
|
17
|
+
* 0-indexed source line on which this variable is declared. Used by the
|
|
18
|
+
* scope analyzer to exclude not-yet-declared locals from a breakpoint's
|
|
19
|
+
* variable dump (the dump is injected at the START of the breakpoint line,
|
|
20
|
+
* before that line's own declaration executes).
|
|
21
|
+
*/
|
|
22
|
+
declLine?: number;
|
|
9
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Coarse C++ type category for debug variable formatting.
|
|
26
|
+
*
|
|
27
|
+
* The debug preprocessor has no TypeChecker (it uses parse-only
|
|
28
|
+
* createSourceFile), so this is inferred from explicit `: T` annotations and
|
|
29
|
+
* initializer shape. `unknown` is the fallback; printf-based codegens cast to
|
|
30
|
+
* `double` and use `%g` so the code always compiles.
|
|
31
|
+
*/
|
|
32
|
+
export type DebugCppType = 'bool' | 'int' | 'long' | 'float' | 'string' | 'unknown';
|
|
10
33
|
/**
|
|
11
34
|
* A rich breakpoint with optional condition and log message.
|
|
12
35
|
*/
|
|
@@ -2,7 +2,7 @@ import type { CallGraph } from "../ir/call-graph.js";
|
|
|
2
2
|
import type { GpioPinEntry, PeripheralAllocation, AsyncTaskEntry, ExecutionFlow, HeapEstimate, PeripheralConflictEntry, ModuleGraph, TreeShakingReport, BuildTiming } from "./json-schema.js";
|
|
3
3
|
/**
|
|
4
4
|
* Build a categorized architecture diagram showing how the sketch
|
|
5
|
-
* is organized: entry points, HAL objects,
|
|
5
|
+
* is organized: entry points, HAL objects, HAL APIs (grouped by
|
|
6
6
|
* subsystem), state variables, and custom functions.
|
|
7
7
|
*
|
|
8
8
|
* Noise symbols (true/false/HIGH/board chain) are filtered out.
|
|
@@ -41,30 +41,40 @@ const BOARD_CHAIN = new Set([
|
|
|
41
41
|
"TypeCAD", "Arduino", "Uno", "Nano", "Mega", "Mega2560",
|
|
42
42
|
"Demo", "Board", "ATmega328P", "Features", "Esp32", "DevKit",
|
|
43
43
|
]);
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Known HAL runtime API call names used for diagram coloring/grouping.
|
|
46
|
+
*
|
|
47
|
+
* This is the cross-framework Wiring-derived HAL surface (the call names that
|
|
48
|
+
* appear in emitted C++ across Arduino-core, ESP32 Arduino, RP2040 Arduino,
|
|
49
|
+
* etc.). It is a diagnostic heuristic for grouping nodes in the mermaid
|
|
50
|
+
* execution-flow and interrupt-map diagrams — not emitted code. The set is
|
|
51
|
+
* generic: it identifies "this is a HAL/runtime call" for coloring purposes,
|
|
52
|
+
* regardless of which Wiring-derived framework produced it.
|
|
53
|
+
*/
|
|
54
|
+
const HAL_API_NAMES = new Set([
|
|
55
|
+
// GPIO
|
|
56
|
+
"digitalWrite", "digitalRead", "analogWrite", "analogRead", "pinMode",
|
|
57
|
+
// Serial
|
|
58
|
+
"Serial", "println", "print", "read", "write",
|
|
59
|
+
// Audio
|
|
60
|
+
"tone", "noTone",
|
|
61
|
+
// Timing
|
|
62
|
+
"millis", "micros", "delay", "delayMicroseconds",
|
|
63
|
+
// Interrupts
|
|
64
|
+
"attachInterrupt", "detachInterrupt",
|
|
65
|
+
// SPI / Shift
|
|
66
|
+
"shiftOut", "shiftIn", "pulseIn",
|
|
67
|
+
// HAL event tokens (from Button, etc.)
|
|
68
|
+
"pressed", "released", "input",
|
|
69
|
+
]);
|
|
70
|
+
/** Recognize a HAL/runtime API call name for diagram coloring. */
|
|
71
|
+
function isHalApi(name) {
|
|
72
|
+
return HAL_API_NAMES.has(name);
|
|
63
73
|
}
|
|
64
74
|
// ── Execution Flow Diagram ──────────────────────────────────────────────────
|
|
65
75
|
/**
|
|
66
76
|
* Build a categorized architecture diagram showing how the sketch
|
|
67
|
-
* is organized: entry points, HAL objects,
|
|
77
|
+
* is organized: entry points, HAL objects, HAL APIs (grouped by
|
|
68
78
|
* subsystem), state variables, and custom functions.
|
|
69
79
|
*
|
|
70
80
|
* Noise symbols (true/false/HIGH/board chain) are filtered out.
|
|
@@ -133,7 +143,7 @@ export function buildExecutionFlowDiagram(flow, callGraph) {
|
|
|
133
143
|
if (entry.has(name) || isr.has(name) || async.has(name))
|
|
134
144
|
continue;
|
|
135
145
|
const node = callGraph.nodes.get(name);
|
|
136
|
-
if (
|
|
146
|
+
if (isHalApi(name)) {
|
|
137
147
|
apis.push(name);
|
|
138
148
|
}
|
|
139
149
|
else if (node?.kind === "function") {
|
|
@@ -187,9 +197,9 @@ export function buildExecutionFlowDiagram(flow, callGraph) {
|
|
|
187
197
|
lines.push(" end");
|
|
188
198
|
lines.push("");
|
|
189
199
|
}
|
|
190
|
-
// ---
|
|
200
|
+
// --- HAL APIs (grouped by subsystem) ─────────────────────────────
|
|
191
201
|
if (apis.length > 0) {
|
|
192
|
-
lines.push(" subgraph APIs[\"
|
|
202
|
+
lines.push(" subgraph APIs[\"HAL APIs\"]");
|
|
193
203
|
lines.push(" direction LR");
|
|
194
204
|
const groups = {
|
|
195
205
|
"GPIO": ["digitalWrite", "digitalRead", "analogWrite", "analogRead", "pinMode"],
|
|
@@ -333,7 +343,7 @@ export function buildInterruptMap(flow, callGraph) {
|
|
|
333
343
|
return;
|
|
334
344
|
visited.add(name);
|
|
335
345
|
const node = callGraph.nodes.get(name);
|
|
336
|
-
const style = isRoot ? ":::isr" : (
|
|
346
|
+
const style = isRoot ? ":::isr" : (isHalApi(name) ? ":::api" : ":::logic");
|
|
337
347
|
const shape = isRoot ? `[[${esc(name)}]]` : `(${esc(name)})`;
|
|
338
348
|
lines.push(` ${name}${shape}${style}`);
|
|
339
349
|
if (node) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComplianceContext } from "./compliance-context.js";
|
|
2
|
+
/**
|
|
3
|
+
* Render the deviation ledger as an AUTOSAR ARXML XML projection.
|
|
4
|
+
*
|
|
5
|
+
* ARXML is the AUTOSAR-standard interchange format consumed by tooling
|
|
6
|
+
* like Artop and DaVinci. This is a *projection* of the same ledger that
|
|
7
|
+
* `renderRegistryJson` produces — single source of truth, two renderings.
|
|
8
|
+
* Many teams only need the JSON; ARXML generation is gated behind
|
|
9
|
+
* `--autosar-arxml` so projects that don't need it pay no cost.
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderArxml(ctx: ComplianceContext, emittedArtifact: string): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Escape XML special characters in text/attribute content. */
|
|
2
|
+
function escapeXml(s) {
|
|
3
|
+
return s.replace(/[<>&'"]/g, (c) => c === "<" ? "<" :
|
|
4
|
+
c === ">" ? ">" :
|
|
5
|
+
c === "&" ? "&" :
|
|
6
|
+
c === "'" ? "'" :
|
|
7
|
+
""");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Render the deviation ledger as an AUTOSAR ARXML XML projection.
|
|
11
|
+
*
|
|
12
|
+
* ARXML is the AUTOSAR-standard interchange format consumed by tooling
|
|
13
|
+
* like Artop and DaVinci. This is a *projection* of the same ledger that
|
|
14
|
+
* `renderRegistryJson` produces — single source of truth, two renderings.
|
|
15
|
+
* Many teams only need the JSON; ARXML generation is gated behind
|
|
16
|
+
* `--autosar-arxml` so projects that don't need it pay no cost.
|
|
17
|
+
*/
|
|
18
|
+
export function renderArxml(ctx, emittedArtifact) {
|
|
19
|
+
const deviations = ctx.ledger().all();
|
|
20
|
+
const body = deviations.map((d) => {
|
|
21
|
+
const source = d.source
|
|
22
|
+
? `\n <SOURCE TS-FILE="${escapeXml(d.source.tsFile)}" TS-LINE="${d.source.tsLine}" KIND="${d.source.kind}"/>`
|
|
23
|
+
: "";
|
|
24
|
+
return ` <DEVIATION RULE="${d.ruleId}" LINE="${d.line}" END-LINE="${d.endLine}" STATUS="${d.reviewStatus}">
|
|
25
|
+
<JUSTIFICATION>${escapeXml(d.justification)}</JUSTIFICATION>
|
|
26
|
+
<SNIPPET>${escapeXml(d.snippet)}</SNIPPET>${source}
|
|
27
|
+
</DEVIATION>`;
|
|
28
|
+
}).join("\n");
|
|
29
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
30
|
+
<AUTOSAR xmlns="http://autosar.org/schema/r4.0" ARTIFACT="${escapeXml(emittedArtifact)}">
|
|
31
|
+
${body}
|
|
32
|
+
</AUTOSAR>
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DeviationLedger } from "./deviation-ledger.js";
|
|
2
|
+
import type { ComplianceMode, DeviationKind } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Per-emit compliance bag, threaded through EmitterContext. Owns the
|
|
5
|
+
* deviation ledger and exposes the renderer-facing API.
|
|
6
|
+
*
|
|
7
|
+
* When mode === "off" (the default), every method is a no-op: helpers
|
|
8
|
+
* return their input unchanged, nothing is recorded. This is what lets
|
|
9
|
+
* the feature land without changing any existing emitted output.
|
|
10
|
+
*/
|
|
11
|
+
export declare class ComplianceContext {
|
|
12
|
+
private readonly complianceMode;
|
|
13
|
+
private readonly ledgerInstance;
|
|
14
|
+
private readonly bannedKinds;
|
|
15
|
+
constructor(complianceMode: ComplianceMode);
|
|
16
|
+
mode(): ComplianceMode;
|
|
17
|
+
isEnabled(): boolean;
|
|
18
|
+
ledger(): Readonly<DeviationLedger>;
|
|
19
|
+
/**
|
|
20
|
+
* Record a deviation for a known unavoidable pattern (used by the
|
|
21
|
+
* self-check's knownPatterns mechanism). Unlike emitWithDeviation, this
|
|
22
|
+
* does NOT append an inline comment — the deviation is recorded in the
|
|
23
|
+
* ledger only, so the sidecar lists it. The emitted C++ text is unchanged.
|
|
24
|
+
*/
|
|
25
|
+
recordKnownDeviation(ruleId: string, justification: string, line: number, file: "source" | "header", snippet: string, source?: {
|
|
26
|
+
tsFile: string;
|
|
27
|
+
tsLine: number;
|
|
28
|
+
kind: DeviationKind;
|
|
29
|
+
}): void;
|
|
30
|
+
/** Renderer gate: should it avoid the banned spelling for this rule? */
|
|
31
|
+
isBanned(ruleId: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Returns `line` with an appended inline deviation comment, and records
|
|
34
|
+
* the deviation(s) to the ledger atomically. The two cannot drift apart.
|
|
35
|
+
*
|
|
36
|
+
* `line` is the C++ text the renderer was already going to emit (without
|
|
37
|
+
* a trailing newline). `currentLine` is the 1-based line number it will
|
|
38
|
+
* occupy in the emitted file (used for the ledger).
|
|
39
|
+
*/
|
|
40
|
+
emitWithDeviation(line: string, ruleId: string | string[], justification: string, currentLine?: number, file?: "source" | "header", source?: {
|
|
41
|
+
tsFile: string;
|
|
42
|
+
tsLine: number;
|
|
43
|
+
kind: DeviationKind;
|
|
44
|
+
}): string;
|
|
45
|
+
/**
|
|
46
|
+
* Opens a deviation region. Returns the opening line with the inline
|
|
47
|
+
* comment appended (the renderer emits this as the first line of the
|
|
48
|
+
* block). The matching closeDeviationRegion records the endLine.
|
|
49
|
+
*/
|
|
50
|
+
openDeviationRegion(ruleId: string, justification: string, startLine: number, openingSnippet: string, file?: "source" | "header"): string;
|
|
51
|
+
closeDeviationRegion(endLine: number): void;
|
|
52
|
+
/**
|
|
53
|
+
* Optional positive assertion: "I deliberately satisfied ruleId here."
|
|
54
|
+
* No-op today; recorded for future coverage tooling.
|
|
55
|
+
*/
|
|
56
|
+
noteCompliant(_ruleId: string): void;
|
|
57
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { DeviationLedger } from "./deviation-ledger.js";
|
|
2
|
+
import { getRule, rulesByCategory } from "./rules.js";
|
|
3
|
+
/**
|
|
4
|
+
* Per-emit compliance bag, threaded through EmitterContext. Owns the
|
|
5
|
+
* deviation ledger and exposes the renderer-facing API.
|
|
6
|
+
*
|
|
7
|
+
* When mode === "off" (the default), every method is a no-op: helpers
|
|
8
|
+
* return their input unchanged, nothing is recorded. This is what lets
|
|
9
|
+
* the feature land without changing any existing emitted output.
|
|
10
|
+
*/
|
|
11
|
+
export class ComplianceContext {
|
|
12
|
+
constructor(complianceMode) {
|
|
13
|
+
this.complianceMode = complianceMode;
|
|
14
|
+
this.ledgerInstance = new DeviationLedger();
|
|
15
|
+
// Precompute the set of [C]-category rule ids once.
|
|
16
|
+
this.bannedKinds = new Set(rulesByCategory("C").map((r) => r.id));
|
|
17
|
+
}
|
|
18
|
+
mode() {
|
|
19
|
+
return this.complianceMode;
|
|
20
|
+
}
|
|
21
|
+
isEnabled() {
|
|
22
|
+
return this.complianceMode !== "off";
|
|
23
|
+
}
|
|
24
|
+
ledger() {
|
|
25
|
+
return this.ledgerInstance;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Record a deviation for a known unavoidable pattern (used by the
|
|
29
|
+
* self-check's knownPatterns mechanism). Unlike emitWithDeviation, this
|
|
30
|
+
* does NOT append an inline comment — the deviation is recorded in the
|
|
31
|
+
* ledger only, so the sidecar lists it. The emitted C++ text is unchanged.
|
|
32
|
+
*/
|
|
33
|
+
recordKnownDeviation(ruleId, justification, line, file, snippet, source) {
|
|
34
|
+
if (!this.isEnabled())
|
|
35
|
+
return;
|
|
36
|
+
this.ledgerInstance.record({
|
|
37
|
+
ruleId,
|
|
38
|
+
file,
|
|
39
|
+
line,
|
|
40
|
+
snippet,
|
|
41
|
+
justification,
|
|
42
|
+
source,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Renderer gate: should it avoid the banned spelling for this rule? */
|
|
46
|
+
isBanned(ruleId) {
|
|
47
|
+
if (!this.isEnabled())
|
|
48
|
+
return false;
|
|
49
|
+
return this.bannedKinds.has(ruleId);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns `line` with an appended inline deviation comment, and records
|
|
53
|
+
* the deviation(s) to the ledger atomically. The two cannot drift apart.
|
|
54
|
+
*
|
|
55
|
+
* `line` is the C++ text the renderer was already going to emit (without
|
|
56
|
+
* a trailing newline). `currentLine` is the 1-based line number it will
|
|
57
|
+
* occupy in the emitted file (used for the ledger).
|
|
58
|
+
*/
|
|
59
|
+
emitWithDeviation(line, ruleId, justification, currentLine = 0, file = "source", source) {
|
|
60
|
+
if (!this.isEnabled())
|
|
61
|
+
return line;
|
|
62
|
+
const ids = Array.isArray(ruleId) ? ruleId : [ruleId];
|
|
63
|
+
for (const id of ids) {
|
|
64
|
+
if (!getRule(id)) {
|
|
65
|
+
throw new Error(`Unknown AUTOSAR rule id: ${id}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const tag = ids.join(", ");
|
|
69
|
+
for (const id of ids) {
|
|
70
|
+
this.ledgerInstance.record({
|
|
71
|
+
ruleId: id,
|
|
72
|
+
file,
|
|
73
|
+
line: currentLine,
|
|
74
|
+
snippet: line,
|
|
75
|
+
justification,
|
|
76
|
+
source,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return `${line} // AUTOSAR Deviation ${tag}: ${justification}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Opens a deviation region. Returns the opening line with the inline
|
|
83
|
+
* comment appended (the renderer emits this as the first line of the
|
|
84
|
+
* block). The matching closeDeviationRegion records the endLine.
|
|
85
|
+
*/
|
|
86
|
+
openDeviationRegion(ruleId, justification, startLine, openingSnippet, file = "source") {
|
|
87
|
+
if (!this.isEnabled())
|
|
88
|
+
return openingSnippet;
|
|
89
|
+
if (!getRule(ruleId)) {
|
|
90
|
+
throw new Error(`Unknown AUTOSAR rule id: ${ruleId}`);
|
|
91
|
+
}
|
|
92
|
+
this.ledgerInstance.openRegion({
|
|
93
|
+
ruleId,
|
|
94
|
+
file,
|
|
95
|
+
line: startLine,
|
|
96
|
+
snippet: openingSnippet,
|
|
97
|
+
justification,
|
|
98
|
+
});
|
|
99
|
+
return `${openingSnippet} // AUTOSAR Deviation ${ruleId}: ${justification}`;
|
|
100
|
+
}
|
|
101
|
+
closeDeviationRegion(endLine) {
|
|
102
|
+
if (!this.isEnabled())
|
|
103
|
+
return;
|
|
104
|
+
this.ledgerInstance.closeRegion(endLine);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Optional positive assertion: "I deliberately satisfied ruleId here."
|
|
108
|
+
* No-op today; recorded for future coverage tooling.
|
|
109
|
+
*/
|
|
110
|
+
noteCompliant(_ruleId) {
|
|
111
|
+
// intentionally no-op; reserved for future use.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Deviation } from "./types.js";
|
|
2
|
+
/** Input shape for recording a deviation (line + endLine derived from context). */
|
|
3
|
+
export interface DeviationInput {
|
|
4
|
+
ruleId: string;
|
|
5
|
+
file: "source" | "header";
|
|
6
|
+
line: number;
|
|
7
|
+
snippet: string;
|
|
8
|
+
justification: string;
|
|
9
|
+
source?: Deviation["source"];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Append-only store of deviations recorded during emit. Dedupes identical
|
|
13
|
+
* (ruleId, file, line) entries; supports region deviations via openRegion /
|
|
14
|
+
* closeRegion. The ledger is the single source of truth that the sidecar
|
|
15
|
+
* registry is rendered from.
|
|
16
|
+
*/
|
|
17
|
+
export declare class DeviationLedger {
|
|
18
|
+
private readonly entries;
|
|
19
|
+
private openRegionEntry;
|
|
20
|
+
record(input: DeviationInput): void;
|
|
21
|
+
openRegion(input: DeviationInput): void;
|
|
22
|
+
closeRegion(endLine: number): void;
|
|
23
|
+
all(): readonly Deviation[];
|
|
24
|
+
/** Used by the self-check to verify every ledger entry has a matching inline comment. */
|
|
25
|
+
hasEntry(file: "source" | "header", line: number, ruleId: string): boolean;
|
|
26
|
+
private append;
|
|
27
|
+
}
|