al-sem 0.0.1
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/LICENSE +21 -0
- package/README.md +361 -0
- package/package.json +64 -0
- package/scripts/d40-diff.ts +44 -0
- package/scripts/fetch-native-parser.ts +179 -0
- package/scripts/precision-sample.ts +99 -0
- package/scripts/precision-study.ts +42 -0
- package/scripts/precision-tabulate.ts +52 -0
- package/src/cli/baseline.ts +31 -0
- package/src/cli/diff.ts +199 -0
- package/src/cli/events-chains.ts +56 -0
- package/src/cli/events-fanout.ts +87 -0
- package/src/cli/exit-code.ts +30 -0
- package/src/cli/fingerprint-indexes.ts +130 -0
- package/src/cli/fingerprint-query.ts +543 -0
- package/src/cli/fingerprint-witness.ts +493 -0
- package/src/cli/fingerprint.ts +292 -0
- package/src/cli/format-compact-json.ts +45 -0
- package/src/cli/format-events.ts +77 -0
- package/src/cli/format-fingerprint.ts +295 -0
- package/src/cli/format-html.ts +503 -0
- package/src/cli/format-json.ts +13 -0
- package/src/cli/format-policy.ts +95 -0
- package/src/cli/format-sarif.ts +186 -0
- package/src/cli/format-terminal.ts +153 -0
- package/src/cli/index.ts +566 -0
- package/src/cli/policy.ts +204 -0
- package/src/config/roots-config.ts +302 -0
- package/src/deps/cache-versions.ts +74 -0
- package/src/deps/canonical-json.ts +27 -0
- package/src/deps/dependency-artifact.ts +144 -0
- package/src/deps/dependency-cache.ts +262 -0
- package/src/deps/dependency-dag.ts +128 -0
- package/src/deps/dependency-package-discovery.ts +85 -0
- package/src/deps/dependency-pipeline.ts +483 -0
- package/src/deps/dependency-projection.ts +211 -0
- package/src/deps/dependency-resolver.ts +154 -0
- package/src/deps/workspace-dependencies.ts +114 -0
- package/src/detectors/capability-query.ts +145 -0
- package/src/detectors/confidence.ts +52 -0
- package/src/detectors/d1-db-op-in-loop.ts +457 -0
- package/src/detectors/d10-self-modifying-loop.ts +114 -0
- package/src/detectors/d11-modify-without-get.ts +129 -0
- package/src/detectors/d12-dead-integration-event.ts +81 -0
- package/src/detectors/d13-cross-app-internal-call.ts +105 -0
- package/src/detectors/d14-dead-routine.ts +151 -0
- package/src/detectors/d16-obsolete-routine-call.ts +94 -0
- package/src/detectors/d17-min-version-drift.ts +157 -0
- package/src/detectors/d18-constant-filter-in-loop.ts +151 -0
- package/src/detectors/d19-unused-parameter.ts +116 -0
- package/src/detectors/d2-event-fanout-in-loop.ts +240 -0
- package/src/detectors/d20-unreachable-after-exit.ts +92 -0
- package/src/detectors/d21-read-without-load.ts +128 -0
- package/src/detectors/d22-flowfield-without-calcfields.ts +168 -0
- package/src/detectors/d29-subscriber-modify-on-event-record.ts +163 -0
- package/src/detectors/d3-load-state.ts +72 -0
- package/src/detectors/d3-missing-setloadfields.ts +234 -0
- package/src/detectors/d32-constant-boolean-parameter.ts +185 -0
- package/src/detectors/d33-unfiltered-bulk-write.ts +173 -0
- package/src/detectors/d34-commit-in-loop.ts +206 -0
- package/src/detectors/d35-commit-in-event-subscriber.ts +138 -0
- package/src/detectors/d36-late-setloadfields.ts +162 -0
- package/src/detectors/d37-validate-without-persist.ts +271 -0
- package/src/detectors/d38-subscriber-to-obsolete-event.ts +140 -0
- package/src/detectors/d39-record-left-dirty-across-chain.ts +165 -0
- package/src/detectors/d4-repeated-lookup-in-loop.ts +128 -0
- package/src/detectors/d40-transitive-load-missing.ts +217 -0
- package/src/detectors/d41-transitive-filter-loss.ts +200 -0
- package/src/detectors/d42-cross-call-wrong-setloadfields.ts +243 -0
- package/src/detectors/d43-event-ishandled-skip.ts +257 -0
- package/src/detectors/d44-event-multi-subscriber-overlap.ts +223 -0
- package/src/detectors/d45-event-transitive-table-exposure.ts +159 -0
- package/src/detectors/d5-set-based-opportunity.ts +162 -0
- package/src/detectors/d7-recursive-event-expansion.ts +151 -0
- package/src/detectors/d8-commit-in-transaction.ts +132 -0
- package/src/detectors/d9-transaction-span-summary.ts +107 -0
- package/src/detectors/detector-context.ts +121 -0
- package/src/detectors/finding-grouping.ts +61 -0
- package/src/detectors/path-merge.ts +174 -0
- package/src/detectors/registry.ts +176 -0
- package/src/detectors/table-display.ts +42 -0
- package/src/diff/diff-abi.ts +195 -0
- package/src/diff/diff-capabilities.ts +179 -0
- package/src/diff/diff-engine.ts +146 -0
- package/src/diff/diff-events.ts +323 -0
- package/src/diff/diff-identity.ts +73 -0
- package/src/diff/diff-indexes.ts +199 -0
- package/src/diff/diff-permissions.ts +260 -0
- package/src/diff/diff-policy.ts +101 -0
- package/src/diff/diff-preflight.ts +66 -0
- package/src/diff/diff-renames.ts +104 -0
- package/src/diff/diff-schema.ts +232 -0
- package/src/diff/format-diff.ts +148 -0
- package/src/engine/attribute-parser.ts +50 -0
- package/src/engine/capability-cone.ts +531 -0
- package/src/engine/combined-graph.ts +357 -0
- package/src/engine/control-flow-walker.ts +1317 -0
- package/src/engine/dispatch-sites.ts +199 -0
- package/src/engine/effect-lattice.ts +81 -0
- package/src/engine/entry-points.ts +57 -0
- package/src/engine/event-flow.ts +524 -0
- package/src/engine/event-relay.ts +92 -0
- package/src/engine/op-classification.ts +92 -0
- package/src/engine/path-walker.ts +189 -0
- package/src/engine/reverse-call-graph.ts +23 -0
- package/src/engine/root-classifier-overlay.ts +194 -0
- package/src/engine/root-classifier.ts +135 -0
- package/src/engine/scc.ts +110 -0
- package/src/engine/source-anchor.ts +25 -0
- package/src/engine/summary-context.ts +104 -0
- package/src/engine/summary-engine.ts +296 -0
- package/src/engine/summary-runner.ts +560 -0
- package/src/engine/transaction-spans.ts +112 -0
- package/src/engine/uncertainty-util.ts +54 -0
- package/src/hash.ts +31 -0
- package/src/index/attribute-from-node.ts +141 -0
- package/src/index/callee-from-node.ts +181 -0
- package/src/index/capability/background.ts +90 -0
- package/src/index/capability/commit.ts +44 -0
- package/src/index/capability/dispatch.ts +164 -0
- package/src/index/capability/events.ts +65 -0
- package/src/index/capability/extractor.ts +124 -0
- package/src/index/capability/file-blob.ts +137 -0
- package/src/index/capability/http.ts +159 -0
- package/src/index/capability/hyperlink.ts +60 -0
- package/src/index/capability/isolated-storage.ts +179 -0
- package/src/index/capability/table.ts +113 -0
- package/src/index/capability/telemetry.ts +84 -0
- package/src/index/capability/ui.ts +55 -0
- package/src/index/capability/value-source.ts +202 -0
- package/src/index/expression-from-node.ts +117 -0
- package/src/index/indexer.ts +102 -0
- package/src/index/intraprocedural-body.ts +1467 -0
- package/src/index/intraprocedural-ops.ts +253 -0
- package/src/index/intraprocedural-refs.ts +188 -0
- package/src/index/object-indexer.ts +279 -0
- package/src/index/routine-indexer.ts +282 -0
- package/src/index/routine-signature.ts +46 -0
- package/src/index/variable-indexer.ts +134 -0
- package/src/index/variable-initializer-extractor.ts +155 -0
- package/src/index/variable-type-normalizer.ts +83 -0
- package/src/index.ts +267 -0
- package/src/mcp/server.ts +72 -0
- package/src/mcp/session.ts +49 -0
- package/src/mcp/tools/explain-path.ts +75 -0
- package/src/mcp/tools/get-analysis-health.ts +62 -0
- package/src/mcp/tools/get-finding.ts +47 -0
- package/src/mcp/tools/get-routine-summary.ts +126 -0
- package/src/mcp/tools/list-findings.ts +85 -0
- package/src/mcp/tools/list-hotspots.ts +78 -0
- package/src/mcp/tools/list-rollups.ts +103 -0
- package/src/mcp/tools/validators.ts +25 -0
- package/src/model/attributes.ts +120 -0
- package/src/model/callee.ts +45 -0
- package/src/model/capability.ts +187 -0
- package/src/model/coverage.ts +85 -0
- package/src/model/entities.ts +628 -0
- package/src/model/expression.ts +98 -0
- package/src/model/finding.ts +110 -0
- package/src/model/graph-edge.ts +93 -0
- package/src/model/graph.ts +62 -0
- package/src/model/identity.ts +81 -0
- package/src/model/ids.ts +90 -0
- package/src/model/index.ts +13 -0
- package/src/model/model.ts +51 -0
- package/src/model/permission.ts +76 -0
- package/src/model/root-classification.ts +116 -0
- package/src/model/stable-identity.ts +102 -0
- package/src/model/summary.ts +96 -0
- package/src/parser/ast.ts +82 -0
- package/src/parser/native/ffi.ts +145 -0
- package/src/parser/native/parse-index-pool.ts +148 -0
- package/src/parser/native/parse-index-worker.ts +94 -0
- package/src/parser/native/wrapper.ts +353 -0
- package/src/parser/parser-init.ts +43 -0
- package/src/perf/profiler.ts +66 -0
- package/src/policy/policy-default.yaml +83 -0
- package/src/policy/policy-engine.ts +339 -0
- package/src/policy/policy-loader.ts +257 -0
- package/src/policy/policy-schema.json +379 -0
- package/src/policy/policy-types.ts +81 -0
- package/src/policy/predicate-compiler.ts +151 -0
- package/src/policy/predicate-evaluator.ts +267 -0
- package/src/policy/predicate-fields.ts +439 -0
- package/src/projection/actionable-anchor.ts +48 -0
- package/src/projection/finding-filters.ts +44 -0
- package/src/projection/finding-fingerprint.ts +54 -0
- package/src/projection/finding-groups.ts +41 -0
- package/src/projection/finding-summary.ts +110 -0
- package/src/projection/rollup-findings.ts +105 -0
- package/src/providers/discover.ts +88 -0
- package/src/providers/external.ts +46 -0
- package/src/providers/types.ts +36 -0
- package/src/providers/workspace.ts +117 -0
- package/src/resolve/call-resolver.ts +117 -0
- package/src/resolve/coverage.ts +61 -0
- package/src/resolve/event-graph.ts +166 -0
- package/src/resolve/implicit-edges.ts +53 -0
- package/src/resolve/record-types.ts +36 -0
- package/src/resolve/resolver.ts +23 -0
- package/src/resolve/semantic-graph.ts +29 -0
- package/src/resolve/symbol-table.ts +69 -0
- package/src/snapshot/app-snapshot.ts +74 -0
- package/src/snapshot/compose.ts +100 -0
- package/src/snapshot/derive/callsite-evidence.ts +76 -0
- package/src/snapshot/derive/capability-facts.ts +70 -0
- package/src/snapshot/derive/contracts.ts +131 -0
- package/src/snapshot/derive/coverage.ts +35 -0
- package/src/snapshot/derive/event-declarations.ts +140 -0
- package/src/snapshot/derive/identity-table.ts +58 -0
- package/src/snapshot/derive/inputs.ts +91 -0
- package/src/snapshot/derive/operation-evidence.ts +70 -0
- package/src/snapshot/derive/permissions.ts +186 -0
- package/src/snapshot/derive/root-classifications.ts +56 -0
- package/src/snapshot/derive/schema.ts +130 -0
- package/src/snapshot/derive/typed-edges.ts +60 -0
- package/src/snapshot/derive/workspace-fingerprint.ts +19 -0
- package/src/snapshot/deserialize.ts +40 -0
- package/src/snapshot/serialize-cbor-gz.ts +12 -0
- package/src/snapshot/serialize-cbor.ts +19 -0
- package/src/snapshot/serialize-json.ts +22 -0
- package/src/snapshot/shard.ts +134 -0
- package/src/snapshot/types.ts +181 -0
- package/src/symbols/app-manifest.ts +96 -0
- package/src/symbols/app-package-zip.ts +50 -0
- package/src/symbols/embedded-source-reader.ts +41 -0
- package/src/symbols/package-hash.ts +81 -0
- package/src/symbols/symbol-reader.ts +101 -0
- package/src/symbols/symbol-reference-parser.ts +378 -0
- package/src/symbols/symbol-reference-reader.ts +27 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import type { AttributeInfo } from "./attributes.ts";
|
|
2
|
+
import type { Callee } from "./callee.ts";
|
|
3
|
+
import type { ValueSource } from "./capability.ts";
|
|
4
|
+
import type { ExpressionInfo } from "./expression.ts";
|
|
5
|
+
import type { SourceAnchor } from "./identity.ts";
|
|
6
|
+
import type {
|
|
7
|
+
CallsiteId,
|
|
8
|
+
CanonicalRoutineKey,
|
|
9
|
+
FieldId,
|
|
10
|
+
KeyId,
|
|
11
|
+
LoopId,
|
|
12
|
+
ObjectId,
|
|
13
|
+
OperationId,
|
|
14
|
+
RecordVariableId,
|
|
15
|
+
RoutineId,
|
|
16
|
+
RoutineKind,
|
|
17
|
+
TableId,
|
|
18
|
+
} from "./ids.ts";
|
|
19
|
+
import type { RoutineSummary } from "./summary.ts";
|
|
20
|
+
|
|
21
|
+
/** Whether an entity is under analysis ("primary") or merged-in context only ("dependency"). */
|
|
22
|
+
export type AnalysisRole = "primary" | "dependency";
|
|
23
|
+
|
|
24
|
+
/** Resolve an entity's analysis role; an absent `analysisRole` means "primary". */
|
|
25
|
+
export function roleOf(x: { analysisRole?: AnalysisRole }): AnalysisRole {
|
|
26
|
+
return x.analysisRole ?? "primary";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Output scope for CLI/report surfaces. "primary" keeps only the workspace's own
|
|
30
|
+
* app(s); "all" keeps the full merged model (workspace + dependencies). */
|
|
31
|
+
export type Scope = "primary" | "all";
|
|
32
|
+
|
|
33
|
+
export type RecordOpType =
|
|
34
|
+
| "FindSet"
|
|
35
|
+
| "FindFirst"
|
|
36
|
+
| "FindLast"
|
|
37
|
+
| "Find"
|
|
38
|
+
| "Get"
|
|
39
|
+
| "CalcFields"
|
|
40
|
+
| "CalcSums"
|
|
41
|
+
| "TestField"
|
|
42
|
+
| "Modify"
|
|
43
|
+
| "ModifyAll"
|
|
44
|
+
| "Insert"
|
|
45
|
+
| "Delete"
|
|
46
|
+
| "DeleteAll"
|
|
47
|
+
| "SetLoadFields"
|
|
48
|
+
| "AddLoadFields"
|
|
49
|
+
| "SetRange"
|
|
50
|
+
| "SetFilter"
|
|
51
|
+
| "SetCurrentKey"
|
|
52
|
+
| "Reset"
|
|
53
|
+
| "Copy"
|
|
54
|
+
| "TransferFields"
|
|
55
|
+
| "Validate"
|
|
56
|
+
| "Init"
|
|
57
|
+
| "Next"
|
|
58
|
+
| "Count"
|
|
59
|
+
| "CountApprox"
|
|
60
|
+
| "IsEmpty"
|
|
61
|
+
| "LockTable";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Whether a record variable is temporary. Can be caller-dependent when the record is a
|
|
65
|
+
* by-var/value parameter — see spec Section 2.
|
|
66
|
+
*/
|
|
67
|
+
export type TempState =
|
|
68
|
+
| { kind: "known"; value: boolean }
|
|
69
|
+
| { kind: "unknown" }
|
|
70
|
+
| { kind: "parameter-dependent"; parameterIndex: number };
|
|
71
|
+
|
|
72
|
+
export interface ParameterSymbol {
|
|
73
|
+
index: number;
|
|
74
|
+
name: string;
|
|
75
|
+
typeText: string; // raw type string, e.g. 'Record "Sales Line"'
|
|
76
|
+
isVar: boolean; // passed by reference
|
|
77
|
+
isRecord: boolean;
|
|
78
|
+
tableName?: string; // when isRecord
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface App {
|
|
82
|
+
appGuid: string;
|
|
83
|
+
publisher: string;
|
|
84
|
+
name: string;
|
|
85
|
+
version: string;
|
|
86
|
+
analysisRole?: AnalysisRole;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ObjectDecl {
|
|
90
|
+
id: ObjectId;
|
|
91
|
+
appGuid: string;
|
|
92
|
+
objectType: string; // "Codeunit", "Table", "TableExtension", "Page", ...
|
|
93
|
+
objectNumber: number;
|
|
94
|
+
name: string;
|
|
95
|
+
sourceUnitId: string;
|
|
96
|
+
sourceHash: string;
|
|
97
|
+
sourceAnchor: SourceAnchor;
|
|
98
|
+
analysisRole?: AnalysisRole;
|
|
99
|
+
/**
|
|
100
|
+
* Codeunit Subtype property value when declared (e.g. "Install", "Upgrade",
|
|
101
|
+
* "Test", "Normal"). Undefined for non-codeunit objects or when not declared.
|
|
102
|
+
* Verbatim from the AL source (preserves casing).
|
|
103
|
+
*/
|
|
104
|
+
objectSubtype?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Page PageType property value when declared (e.g. "API", "List", "Card").
|
|
107
|
+
* Undefined for non-page objects or when not declared. Verbatim from the AL source.
|
|
108
|
+
*/
|
|
109
|
+
pageType?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** FieldClass + ownership. Ownership follows the declaring object (may be a tableextension). */
|
|
113
|
+
export interface Field {
|
|
114
|
+
id: FieldId;
|
|
115
|
+
physicalTableId: TableId;
|
|
116
|
+
declaringObjectId: ObjectId;
|
|
117
|
+
declaringAppId: string; // appGuid
|
|
118
|
+
fieldNumber: number;
|
|
119
|
+
name: string;
|
|
120
|
+
fieldClass: "Normal" | "FlowField" | "FlowFilter";
|
|
121
|
+
dataType: string;
|
|
122
|
+
isBlobLike: boolean;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface Key {
|
|
126
|
+
id: KeyId;
|
|
127
|
+
physicalTableId: TableId;
|
|
128
|
+
declaringObjectId: ObjectId;
|
|
129
|
+
fields: FieldId[];
|
|
130
|
+
sumIndexFields?: FieldId[];
|
|
131
|
+
isEnabled?: boolean | "unknown";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface Table {
|
|
135
|
+
id: TableId;
|
|
136
|
+
appGuid: string;
|
|
137
|
+
tableNumber: number;
|
|
138
|
+
name: string;
|
|
139
|
+
fields: Field[];
|
|
140
|
+
keys: Key[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export type LoopType = "repeat" | "for" | "foreach" | "while";
|
|
144
|
+
|
|
145
|
+
export interface LoopNode {
|
|
146
|
+
id: LoopId;
|
|
147
|
+
type: LoopType;
|
|
148
|
+
sourceAnchor: SourceAnchor;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Per-argument structural binding at a CallSite. Replaces text-matching on
|
|
153
|
+
* argumentTexts (which is retained for display only). Populated by
|
|
154
|
+
* routine-indexer.ts when the callsite is constructed.
|
|
155
|
+
*
|
|
156
|
+
* Detectors must check `bindingResolution === "resolved"` before trusting
|
|
157
|
+
* `sourceParameterIndex` / `calleeParameterIsVar` for cross-call semantics —
|
|
158
|
+
* an unresolved callee still emits positional bindings, but the var-ness
|
|
159
|
+
* information is not authoritative.
|
|
160
|
+
*/
|
|
161
|
+
export interface CallArgumentBinding {
|
|
162
|
+
/** Positional 0-based index, callee-side. */
|
|
163
|
+
parameterIndex: number;
|
|
164
|
+
|
|
165
|
+
/** Where the argument value comes from on the caller side. */
|
|
166
|
+
sourceKind: "parameter" | "local" | "global" | "implicit-rec" | "expression" | "unknown";
|
|
167
|
+
|
|
168
|
+
/** Lowercased source variable name when sourceKind is parameter/local/global/implicit-rec.
|
|
169
|
+
* Diagnostic / cross-reference only — never used to drive semantics by name. */
|
|
170
|
+
sourceVariableName?: string;
|
|
171
|
+
|
|
172
|
+
/** Stable record-variable id from routine.features.recordVariables[].id, when the
|
|
173
|
+
* source is a record variable. Detectors should prefer this over sourceVariableName. */
|
|
174
|
+
sourceRecordVariableId?: string;
|
|
175
|
+
|
|
176
|
+
/** When sourceKind === "parameter", the source parameter's index in the enclosing
|
|
177
|
+
* routine. Drives entry-requirement composition (c1a). */
|
|
178
|
+
sourceParameterIndex?: number;
|
|
179
|
+
|
|
180
|
+
/** Whether the source-side variable is declared `var` (when sourceKind === "parameter").
|
|
181
|
+
* Drives exit-effect composition (c1b). */
|
|
182
|
+
callerSourceParameterIsVar?: boolean;
|
|
183
|
+
|
|
184
|
+
/** Whether the callee-side parameter is declared `var`. Drives exit-effect composition. */
|
|
185
|
+
calleeParameterIsVar: boolean;
|
|
186
|
+
|
|
187
|
+
/** Resolved table for the source record when available. */
|
|
188
|
+
sourceTableId?: TableId | "unknown";
|
|
189
|
+
|
|
190
|
+
/** Tempness of the source record when known. */
|
|
191
|
+
sourceTempState?: TempState;
|
|
192
|
+
|
|
193
|
+
/** Source anchor for the argument expression itself. Detectors anchor evidence at
|
|
194
|
+
* the argument level when emitting record-flow findings. */
|
|
195
|
+
argumentAnchor: SourceAnchor;
|
|
196
|
+
|
|
197
|
+
/** Did al-sem resolve enough to trust this binding for semantics?
|
|
198
|
+
* "resolved" — callee signature known, positional binding sound.
|
|
199
|
+
* "unresolved-callee" — callee not resolved; var-ness must NOT be trusted.
|
|
200
|
+
* "ambiguous" — overload ambiguity; same caveats.
|
|
201
|
+
* "non-record-arg" — argument is not a record (literal/expression). */
|
|
202
|
+
bindingResolution: "resolved" | "unresolved-callee" | "ambiguous" | "non-record-arg";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface CallSite {
|
|
206
|
+
id: CallsiteId;
|
|
207
|
+
operationId: OperationId;
|
|
208
|
+
/** Raw callee expression text, e.g. 'EnrichLine' or 'Customer.Get'. Compatibility/
|
|
209
|
+
* display only — semantic consumers must read `callee` instead. */
|
|
210
|
+
calleeText: string;
|
|
211
|
+
/**
|
|
212
|
+
* Structured, tree-sitter-derived classification of the callee. Built at L2 index
|
|
213
|
+
* time from the `call_expression` node — bare / member / object-run / unknown.
|
|
214
|
+
* Drives `resolve/call-resolver.ts` dispatch directly; no re-parsing of `calleeText`.
|
|
215
|
+
*/
|
|
216
|
+
callee: Callee;
|
|
217
|
+
/**
|
|
218
|
+
* Raw text of each positional argument. Compatibility/display only — semantic
|
|
219
|
+
* consumers must read `argumentInfos` (typed) instead.
|
|
220
|
+
*/
|
|
221
|
+
argumentTexts: string[];
|
|
222
|
+
/**
|
|
223
|
+
* Structured, tree-sitter-derived classification of each positional argument —
|
|
224
|
+
* same order as `argumentTexts`. Detectors that need literal-vs-non-literal
|
|
225
|
+
* decisions (D18) or quoted-vs-unquoted name disambiguation read these via
|
|
226
|
+
* `model/expression.ts` helpers. Avoid switching on `kind` in call sites —
|
|
227
|
+
* use `isLiteralExpression` / `isStringLikeLiteral` / `unquotedFieldName`.
|
|
228
|
+
*/
|
|
229
|
+
argumentInfos: ExpressionInfo[];
|
|
230
|
+
/** Structural per-argument bindings. Populated by routine-indexer.ts;
|
|
231
|
+
* UPGRADED IN PLACE by src/resolve/call-resolver.ts after edge resolution
|
|
232
|
+
* (calleeParameterIsVar + bindingResolution become "resolved").
|
|
233
|
+
*
|
|
234
|
+
* Mutation contract: ONLY upgradeBindings() in call-resolver.ts is permitted
|
|
235
|
+
* to write to existing entries. Detectors and downstream consumers must treat
|
|
236
|
+
* the array as readonly. Re-entrant resolution is not supported; any future
|
|
237
|
+
* incremental / parallel resolution must migrate ownership to CallEdge first.
|
|
238
|
+
*
|
|
239
|
+
* `argumentTexts` is retained for display only — use bindings for any
|
|
240
|
+
* semantic reasoning. */
|
|
241
|
+
argumentBindings: CallArgumentBinding[];
|
|
242
|
+
loopStack: LoopId[]; // loops in THIS routine enclosing the call
|
|
243
|
+
sourceAnchor: SourceAnchor;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface FieldAccess {
|
|
247
|
+
recordVariableName: string;
|
|
248
|
+
fieldName: string;
|
|
249
|
+
sourceAnchor: SourceAnchor;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface RecordVariable {
|
|
253
|
+
id: RecordVariableId;
|
|
254
|
+
name: string;
|
|
255
|
+
tableName?: string;
|
|
256
|
+
tableId?: TableId; // resolved in Phase 2; undefined in Phase 1
|
|
257
|
+
tempState: TempState;
|
|
258
|
+
isParameter: boolean;
|
|
259
|
+
parameterIndex?: number;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* A general variable in a routine's lexical scope — parameter or local declaration.
|
|
264
|
+
* Covers ALL declared types, not just records. Used by Phase 0b capability
|
|
265
|
+
* extractors to resolve member-call receivers structurally (`MyVar.Send(...)`)
|
|
266
|
+
* rather than by text matching the receiver name.
|
|
267
|
+
*
|
|
268
|
+
* `name` is lowercased per AL's case-insensitive identifier rules.
|
|
269
|
+
* `declaredType` is the normalized type string — `"HttpClient"`,
|
|
270
|
+
* `"Codeunit \"Sales-Post\""`, `"Record Customer"`, `"List of [Text]"`, etc.
|
|
271
|
+
* Use `declaredType` for dispatch (e.g. capability extractors match on the
|
|
272
|
+
* string prefix `"HttpClient"`, the prefix `"Codeunit "`, `"Record "`, etc.).
|
|
273
|
+
*
|
|
274
|
+
* `targetObjectId` is populated for object-typed variables (Codeunit, Page,
|
|
275
|
+
* Report, XmlPort, Query) when the object can be resolved at index time.
|
|
276
|
+
* `tableId` is populated for record-typed variables when the table can be
|
|
277
|
+
* resolved. Both are undefined when resolution fails (opaque dep, unknown
|
|
278
|
+
* object/table name).
|
|
279
|
+
*
|
|
280
|
+
* `initializer` is the simplest `ValueSource` describing the right-hand side
|
|
281
|
+
* of the variable's initial assignment, when there is one. Phase 0b-α resolves
|
|
282
|
+
* ONE HOP — chains like `A := B; B := 'literal'` resolve A's initializer to
|
|
283
|
+
* `{kind: "constant-var", varName: "B", initializer: {kind: "unknown"}}`.
|
|
284
|
+
* The `{kind: "unknown"}` placeholder is Phase 0b-β's `value-source.ts`
|
|
285
|
+
* classifier's job to chase (it looks up B in the same routine's
|
|
286
|
+
* `variables[]` and reads B's `initializer`). For "no initializer present"
|
|
287
|
+
* the field is `undefined`.
|
|
288
|
+
*
|
|
289
|
+
* Record variables also appear in `IntraproceduralFeatures.recordVariables` for
|
|
290
|
+
* backward compatibility — the two arrays overlap by design. New code should
|
|
291
|
+
* prefer `variables` and let `recordVariables` deprecate naturally.
|
|
292
|
+
*/
|
|
293
|
+
export interface VariableSymbol {
|
|
294
|
+
/** Lowercased per AL identifier rules. */
|
|
295
|
+
name: string;
|
|
296
|
+
/** Normalized type string — see JSDoc above. */
|
|
297
|
+
declaredType: string;
|
|
298
|
+
/** For object-typed variables, the resolved target object ID. */
|
|
299
|
+
targetObjectId?: ObjectId;
|
|
300
|
+
/** For record-typed variables, mirror of the corresponding
|
|
301
|
+
* `RecordVariable.tableId`. */
|
|
302
|
+
tableId?: TableId;
|
|
303
|
+
isParameter: boolean;
|
|
304
|
+
/** Set when `isParameter === true`. 0-based callee-side position. */
|
|
305
|
+
parameterIndex?: number;
|
|
306
|
+
/** Right-hand side of the initial assignment, as a one-hop ValueSource.
|
|
307
|
+
* Phase 0b-β chases chains; Phase 0b-α captures one level. */
|
|
308
|
+
initializer?: ValueSource;
|
|
309
|
+
sourceAnchor: SourceAnchor;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Identifier reference in a control-predicate position (if-test,
|
|
314
|
+
* while-test, repeat-until, case-of subject). Drives D43 dispatch-site
|
|
315
|
+
* guard recognition. The substrate is intentionally syntactic — it
|
|
316
|
+
* reports identifier participation, not guard semantics. Consumers
|
|
317
|
+
* (D43's slicer) interpret polarity (`if X` vs `if not X`).
|
|
318
|
+
*
|
|
319
|
+
* `identifier` is lowercased. `statementAnchor` is the owning if/while/
|
|
320
|
+
* repeat/case node; `referenceAnchor` is where the identifier appears.
|
|
321
|
+
*/
|
|
322
|
+
export interface ConditionReference {
|
|
323
|
+
identifier: string;
|
|
324
|
+
conditionKind: "if" | "while" | "repeat-until" | "case";
|
|
325
|
+
statementAnchor: SourceAnchor;
|
|
326
|
+
referenceAnchor: SourceAnchor;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Minimal var-assignment stream captured by the indexer. Covers
|
|
331
|
+
* `<identifier> := <expression>` AND `<member_expression> := <expression>`
|
|
332
|
+
* statements in the routine body. Phase 3 D43 (IsHandled-skip) consumes
|
|
333
|
+
* this to detect explicit `IsHandled := true` writes.
|
|
334
|
+
*
|
|
335
|
+
* `lhsName` is lowercased per AL identifier rules. `rhsLiteralValue` is
|
|
336
|
+
* the textual literal when the RHS parses as a boolean / integer / string
|
|
337
|
+
* literal; absent otherwise.
|
|
338
|
+
*/
|
|
339
|
+
export interface VarAssignment {
|
|
340
|
+
/** Lowercased LHS identifier. For member expressions (`Rec.Field := ...`),
|
|
341
|
+
* the trailing member name. */
|
|
342
|
+
lhsName: string;
|
|
343
|
+
/** Lowercased RHS literal text when the RHS is a `boolean` /
|
|
344
|
+
* `integer` / `string_literal`; absent otherwise. */
|
|
345
|
+
rhsLiteralValue?: string;
|
|
346
|
+
sourceAnchor: SourceAnchor;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export type OperationSiteKind =
|
|
350
|
+
| "record-op"
|
|
351
|
+
| "call"
|
|
352
|
+
| "event-publish"
|
|
353
|
+
| "commit"
|
|
354
|
+
| "lock"
|
|
355
|
+
| "external-call"
|
|
356
|
+
| "dynamic-dispatch";
|
|
357
|
+
|
|
358
|
+
export interface OperationSite {
|
|
359
|
+
id: OperationId;
|
|
360
|
+
routineId: RoutineId;
|
|
361
|
+
kind: OperationSiteKind;
|
|
362
|
+
sourceAnchor: SourceAnchor;
|
|
363
|
+
loopStack: LoopId[];
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** An OperationSite specialized for record operations. */
|
|
367
|
+
export interface RecordOperation {
|
|
368
|
+
id: OperationId;
|
|
369
|
+
routineId: RoutineId;
|
|
370
|
+
op: RecordOpType;
|
|
371
|
+
recordVariableName: string;
|
|
372
|
+
recordVariableId?: RecordVariableId;
|
|
373
|
+
tableId?: TableId; // resolved in Phase 2
|
|
374
|
+
tempState: TempState;
|
|
375
|
+
/**
|
|
376
|
+
* Raw text of each positional argument for SetRange/SetFilter/SetLoadFields/
|
|
377
|
+
* AddLoadFields/SetCurrentKey/CalcFields/Get/Find. Compatibility/display only —
|
|
378
|
+
* semantic consumers must use `fieldArgumentInfos`.
|
|
379
|
+
*/
|
|
380
|
+
fieldArguments?: string[];
|
|
381
|
+
/**
|
|
382
|
+
* Structured arguments — same order as `fieldArguments`. D4 / D18 / D22 read
|
|
383
|
+
* these via `model/expression.ts` helpers (no quote/literal regex on the text).
|
|
384
|
+
*/
|
|
385
|
+
fieldArgumentInfos?: ExpressionInfo[];
|
|
386
|
+
loopStack: LoopId[];
|
|
387
|
+
sourceAnchor: SourceAnchor;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* One position in a code_block where a statement (and any siblings after it) is
|
|
392
|
+
* guaranteed to never execute because a previous sibling unconditionally exits the
|
|
393
|
+
* routine. The indexer recognises:
|
|
394
|
+
* - `exit_statement` (`Exit;` / `Exit(value);`)
|
|
395
|
+
* - bare call `Error(...)` / `error(...)` — al-sem treats the call as runtime-fatal
|
|
396
|
+
* - member call `CurrReport.Quit(...)` — report runtime exit
|
|
397
|
+
* The detector D20 walks these.
|
|
398
|
+
*/
|
|
399
|
+
export interface UnreachableStatement {
|
|
400
|
+
id: string;
|
|
401
|
+
exitKind: "exit" | "error" | "currreport-quit";
|
|
402
|
+
exitAnchor: SourceAnchor;
|
|
403
|
+
unreachableAnchor: SourceAnchor;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Compact representation of a routine body's control flow, used by the
|
|
408
|
+
* path-aware control-flow walker (src/engine/control-flow-walker.ts) in
|
|
409
|
+
* Phase 6+ to do branch-aware analysis.
|
|
410
|
+
*
|
|
411
|
+
* Each node represents one syntactic position. Branching nodes (if/case/case-branch)
|
|
412
|
+
* use `children` for the primary branch(es) and `elseChildren` for the else branch
|
|
413
|
+
* (only `if` populates elseChildren). Loop nodes use `children` for the body.
|
|
414
|
+
*
|
|
415
|
+
* `op` and `call` leaves link back to entries in `IntraproceduralFeatures.recordOperations`
|
|
416
|
+
* and `.callSites` via their ids — the walker uses these to interleave structural
|
|
417
|
+
* traversal with the existing flat feature lists.
|
|
418
|
+
*
|
|
419
|
+
* `other` is a catch-all for statement kinds the walker doesn't need to distinguish
|
|
420
|
+
* (assignment, message, etc.) — they're treated as straight-line opaque.
|
|
421
|
+
*
|
|
422
|
+
* Kind contracts:
|
|
423
|
+
* - `if` nodes always have at least one of `children` (then-branch) or `elseChildren`
|
|
424
|
+
* (else-branch) populated under normal parses. Both being empty implies a
|
|
425
|
+
* parse-incomplete body and the walker should treat such a node conservatively.
|
|
426
|
+
* - `try` nodes currently have empty `children` because the AL grammar
|
|
427
|
+
* (tree-sitter-al) does not define a `try_statement` node — AL's TryFunction is an
|
|
428
|
+
* [TryFunction] attribute on a procedure, not a statement. Reserved for future grammar
|
|
429
|
+
* additions; the walker should treat `try` as opaque-with-possible-exit until the
|
|
430
|
+
* grammar+CFN builder catch up.
|
|
431
|
+
* - `error` covers ONLY bare unqualified `Error(...)` / `error(...)` / `ERROR(...)`
|
|
432
|
+
* calls (case-insensitive callee identifier). Qualified calls (`System.Error(...)`,
|
|
433
|
+
* codeunit-method dispatch, etc.) go through `call`. `CurrReport.Quit(...)` and
|
|
434
|
+
* similar exit-equivalents are NOT classified as `error`; see `unconditionalExitKind`
|
|
435
|
+
* in intraprocedural-body.ts for the full exit-shape inventory. P6.T2 walker should
|
|
436
|
+
* cross-reference there if it needs to widen the set of "raises and never returns" leaves.
|
|
437
|
+
* - `exit` is the bare `exit_statement` (covers `Exit;` and `Exit(value);`).
|
|
438
|
+
*
|
|
439
|
+
* Single-statement branch bodies: the AL grammar lets `if X then S` / `while X do S` /
|
|
440
|
+
* `case X of 1: S` omit `begin`/`end`. In those cases the `then_branch` / `body` field
|
|
441
|
+
* points DIRECTLY at the statement (not a wrapping `code_block`). The CFN builder
|
|
442
|
+
* synthesizes a `block` node wrapping the single statement so the walker can treat all
|
|
443
|
+
* branch bodies uniformly.
|
|
444
|
+
*
|
|
445
|
+
* Grammar notes (tree-sitter-al):
|
|
446
|
+
* - `if_statement` exposes `then_branch` and `else_branch` named fields.
|
|
447
|
+
* - `case_statement` children include `case_branch` and `case_else_branch` nodes.
|
|
448
|
+
* - `case_branch` exposes a `body` field; `case_else_branch` has no named body field
|
|
449
|
+
* (search namedChildren for a `code_block`, or fall back to the first non-keyword child).
|
|
450
|
+
* - All loop kinds (`for_statement`, `foreach_statement`, `while_statement`) expose a `body` field.
|
|
451
|
+
* - `repeat_statement` has no body field; its body children are all namedChildren before
|
|
452
|
+
* the `until_keyword`.
|
|
453
|
+
*/
|
|
454
|
+
export interface ControlFlowNode {
|
|
455
|
+
kind:
|
|
456
|
+
| "block"
|
|
457
|
+
| "if"
|
|
458
|
+
| "case"
|
|
459
|
+
| "case-branch"
|
|
460
|
+
| "while"
|
|
461
|
+
| "repeat"
|
|
462
|
+
| "for"
|
|
463
|
+
| "foreach"
|
|
464
|
+
| "try"
|
|
465
|
+
| "exit"
|
|
466
|
+
| "error"
|
|
467
|
+
| "call"
|
|
468
|
+
| "op"
|
|
469
|
+
| "other";
|
|
470
|
+
sourceAnchor: SourceAnchor;
|
|
471
|
+
/** Children for branching/looping nodes' primary branch (then-branch of if; body of loops; cases of case). */
|
|
472
|
+
children?: ControlFlowNode[];
|
|
473
|
+
/** Else-branch children. Only `if` populates this. */
|
|
474
|
+
elseChildren?: ControlFlowNode[];
|
|
475
|
+
/** For `op` leaves: id of the RecordOperation (or OperationSite). */
|
|
476
|
+
operationId?: OperationId;
|
|
477
|
+
/** For `call` leaves: id of the CallSite. */
|
|
478
|
+
callsiteId?: CallsiteId;
|
|
479
|
+
/**
|
|
480
|
+
* Expression-position record-ops / callsites harvested from condition
|
|
481
|
+
* expressions, argument expressions, and chained receivers of this node (P7.5).
|
|
482
|
+
*
|
|
483
|
+
* Populated by the indexer for nodes whose AL syntax has expression-position
|
|
484
|
+
* sub-trees that may contain record-ops or function calls — e.g.
|
|
485
|
+
* `if Cust.FindSet() then ...`, `until Cust.Next() = 0`, `case Rec.Find('-') of`,
|
|
486
|
+
* `Helper(Cust.FindSet())`, `Helper(C).FindSet()`. The walker processes these
|
|
487
|
+
* leaves at the appropriate point relative to the node's main children:
|
|
488
|
+
*
|
|
489
|
+
* - `if` / `case` / `while` / `for` / `foreach`: BEFORE walking `children` /
|
|
490
|
+
* `elseChildren`. For `if`/`case` the condition runs once before branch
|
|
491
|
+
* selection; for loops it's a virtual prefix to each iteration (the
|
|
492
|
+
* condition / range / iterable evaluates before each loop body iteration).
|
|
493
|
+
* - `repeat`: AFTER the body inside each iteration (the `until` expression
|
|
494
|
+
* evaluates at the END of each iteration).
|
|
495
|
+
* - `op` / `call` / `error`: BEFORE the leaf's own effect — argument-position
|
|
496
|
+
* and chained-receiver expressions evaluate before the outer call/op runs.
|
|
497
|
+
* - `other`: emitted by `buildCFNForStatement` as a fallback wrapper for
|
|
498
|
+
* expression-position call_expressions that the visit pass did NOT
|
|
499
|
+
* register as op or callsite (e.g. unresolved or AST-shape-mismatched
|
|
500
|
+
* calls). The wrapper carries the harvested argument/receiver leaves so
|
|
501
|
+
* they aren't dropped from the tree.
|
|
502
|
+
*
|
|
503
|
+
* Nesting is possible. A `call` leaf may itself carry `conditionLeaves` —
|
|
504
|
+
* `Helper(Cust.FindSet())` produces a call-leaf for `Helper(...)` whose own
|
|
505
|
+
* `conditionLeaves` includes the inner FindSet op-leaf (from the argument).
|
|
506
|
+
* For chained-receiver shapes like `Helper(C).FindSet()`, the inner
|
|
507
|
+
* `Helper(C)` callsite is harvested as a SIBLING of the outer FindSet op
|
|
508
|
+
* leaf in the parent's `conditionLeaves` (AL evaluates the receiver before
|
|
509
|
+
* the outer call, and the walker preserves that left-to-right order).
|
|
510
|
+
*
|
|
511
|
+
* The harvest never reaches into statement-position sub-trees — e.g. an
|
|
512
|
+
* `if`-condition harvest stops at the boundary of the then-branch, which
|
|
513
|
+
* has its own statement-tree children.
|
|
514
|
+
*/
|
|
515
|
+
conditionLeaves?: ControlFlowNode[];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** Raw intraprocedural extraction for one routine body. */
|
|
519
|
+
export interface IntraproceduralFeatures {
|
|
520
|
+
loops: LoopNode[];
|
|
521
|
+
operationSites: OperationSite[];
|
|
522
|
+
recordOperations: RecordOperation[];
|
|
523
|
+
callSites: CallSite[];
|
|
524
|
+
fieldAccesses: FieldAccess[];
|
|
525
|
+
recordVariables: RecordVariable[];
|
|
526
|
+
nestingDepth: number;
|
|
527
|
+
unreachableStatements: UnreachableStatement[];
|
|
528
|
+
/**
|
|
529
|
+
* True when the routine body contains any branching control flow that simple
|
|
530
|
+
* straight-line analyses (e.g. the Phase 4 control-flow walker) cannot soundly
|
|
531
|
+
* reason about. Set by the indexer when it encounters an `if_statement`,
|
|
532
|
+
* `case_statement`, `case_branch`, or `try_statement`.
|
|
533
|
+
*
|
|
534
|
+
* Loops are counted separately by `nestingDepth`; unconditional early exits
|
|
535
|
+
* are counted by `unreachableStatements`. Consumers should treat
|
|
536
|
+
* `hasBranching || nestingDepth > 0 || unreachableStatements.length > 0`
|
|
537
|
+
* as "this routine is not straight-line".
|
|
538
|
+
*/
|
|
539
|
+
hasBranching: boolean;
|
|
540
|
+
/**
|
|
541
|
+
* Compact control-flow tree for the routine body. Populated by the indexer
|
|
542
|
+
* in P6.T1; consumed by the path-aware walker in P6.T2. Undefined for
|
|
543
|
+
* routines without a body (symbol-only projections).
|
|
544
|
+
*/
|
|
545
|
+
statementTree?: ControlFlowNode;
|
|
546
|
+
/**
|
|
547
|
+
* The set of identifier names referenced as VALUES anywhere in the routine
|
|
548
|
+
* body — lowercased, sorted, deduped. Field names (member of
|
|
549
|
+
* `member_expression`), enum member names (`value` of
|
|
550
|
+
* `qualified_enum_value`), and bare type names in enum-type position are
|
|
551
|
+
* excluded; receivers, callees, arguments, assignment targets, and loop
|
|
552
|
+
* iteration variables are all included.
|
|
553
|
+
*
|
|
554
|
+
* Used by D19 (unused-parameter) instead of regex-scanning body fragment
|
|
555
|
+
* text; future detectors that need name-reaches-routine should query this
|
|
556
|
+
* set as well.
|
|
557
|
+
*/
|
|
558
|
+
identifierReferences: string[];
|
|
559
|
+
/**
|
|
560
|
+
* Phase 0b-α addition (capability-stack roadmap §3.9). All declared
|
|
561
|
+
* variables in the routine's lexical scope — parameters + local var
|
|
562
|
+
* declarations, covering ALL types not just records. Record variables
|
|
563
|
+
* also appear here (in addition to `recordVariables` for backward
|
|
564
|
+
* compatibility). Phase 0b-β capability extractors use this for
|
|
565
|
+
* structural receiver-type resolution (`MyVar.Send(...)`).
|
|
566
|
+
*
|
|
567
|
+
* Populated by `routine-indexer.ts` for primary routines and by
|
|
568
|
+
* dependency-projection's symbol-only path (`variables: []`) for
|
|
569
|
+
* opaque dep routines. Optional so pre-Phase-0b-α test mock-fixture
|
|
570
|
+
* sites that haven't been updated continue to compile cleanly.
|
|
571
|
+
*/
|
|
572
|
+
variables?: VariableSymbol[];
|
|
573
|
+
/**
|
|
574
|
+
* Var-assignment stream. Captured by the indexer in Phase 3 — drives
|
|
575
|
+
* D43 IsHandled-skip detection. Sorted by sourceAnchor.
|
|
576
|
+
*/
|
|
577
|
+
varAssignments: VarAssignment[];
|
|
578
|
+
/**
|
|
579
|
+
* Identifiers referenced in a control-predicate position. Captured by
|
|
580
|
+
* the indexer in Phase 3.1 — drives D43 dispatch-site guard detection.
|
|
581
|
+
* Sorted by referenceAnchor.
|
|
582
|
+
*/
|
|
583
|
+
conditionReferences: ConditionReference[];
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* The `local`/`internal`/`protected` modifier on a primary-app procedure declaration.
|
|
588
|
+
* Absent = AL's default ("public"). Detectors use this to distinguish demonstrably
|
|
589
|
+
* scope-limited procedures (local) from procedures that may be called externally
|
|
590
|
+
* (default/public). Triggers and dependency-role routines do not carry this.
|
|
591
|
+
*/
|
|
592
|
+
export type ProcedureAccessModifier = "local" | "internal" | "protected";
|
|
593
|
+
|
|
594
|
+
export interface Routine {
|
|
595
|
+
id: RoutineId;
|
|
596
|
+
canonical: CanonicalRoutineKey;
|
|
597
|
+
objectId: ObjectId;
|
|
598
|
+
name: string;
|
|
599
|
+
kind: RoutineKind;
|
|
600
|
+
parameters: ParameterSymbol[];
|
|
601
|
+
/**
|
|
602
|
+
* Raw text of each `attribute_item` immediately preceding the routine, in source order.
|
|
603
|
+
* Compatibility/display only — semantic consumers must use `attributesParsed`.
|
|
604
|
+
*/
|
|
605
|
+
attributes: string[];
|
|
606
|
+
/**
|
|
607
|
+
* Structured, tree-sitter-derived attributes — same order as `attributes`. Built at
|
|
608
|
+
* L2 index time for native AL routines (from `attribute_item` nodes) and projected
|
|
609
|
+
* from `SymbolReference.json` for dependency routines. Detectors and resolvers
|
|
610
|
+
* query attributes through this field via `findAttribute` / `stringArg` /
|
|
611
|
+
* `qualifiedArg` helpers in `model/attributes.ts` — no `[…]` text shredding.
|
|
612
|
+
*/
|
|
613
|
+
attributesParsed: AttributeInfo[];
|
|
614
|
+
/**
|
|
615
|
+
* Declaration-visibility modifier. For primary-app source this is the `modifier`
|
|
616
|
+
* field on a `procedure` node (`local`/`internal`/`protected`); for dependency
|
|
617
|
+
* (.app symbol) routines it is projected from `IsInternal` / `IsLocal` flags.
|
|
618
|
+
* Undefined = AL default ("public") or trigger.
|
|
619
|
+
*/
|
|
620
|
+
accessModifier?: ProcedureAccessModifier;
|
|
621
|
+
bodyAvailable: boolean; // false = opaque .app symbol
|
|
622
|
+
parseIncomplete: boolean;
|
|
623
|
+
sourceHash: string;
|
|
624
|
+
sourceAnchor: SourceAnchor;
|
|
625
|
+
features: IntraproceduralFeatures;
|
|
626
|
+
summary?: RoutineSummary; // computed in Phase 2
|
|
627
|
+
analysisRole?: AnalysisRole;
|
|
628
|
+
}
|