• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / brighterscript / #13978

10 Mar 2025 01:00PM UTC coverage: 86.759% (-2.4%) from 89.113%
#13978

push

web-flow
Merge 96004e807 into 700183e7d

12688 of 15459 branches covered (82.08%)

Branch coverage included in aggregate %.

28 of 28 new or added lines in 1 file covered. (100.0%)

924 existing lines in 53 files now uncovered.

13614 of 14857 relevant lines covered (91.63%)

19945.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.1
/src/astUtils/visitors.ts
1
/* eslint-disable no-bitwise */
2
import type { CancellationToken } from 'vscode-languageserver';
3
import type { Body, AssignmentStatement, Block, ExpressionStatement, FunctionStatement, IfStatement, IncrementStatement, PrintStatement, GotoStatement, LabelStatement, ReturnStatement, EndStatement, StopStatement, ForStatement, ForEachStatement, WhileStatement, DottedSetStatement, IndexedSetStatement, LibraryStatement, NamespaceStatement, ImportStatement, ClassStatement, EnumStatement, EnumMemberStatement, DimStatement, TryCatchStatement, CatchStatement, ThrowStatement, InterfaceStatement, InterfaceFieldStatement, InterfaceMethodStatement, FieldStatement, MethodStatement, ConstStatement, ContinueStatement, TypecastStatement, AliasStatement, ConditionalCompileStatement, ConditionalCompileErrorStatement, ConditionalCompileConstStatement, AugmentedAssignmentStatement, ExitStatement } from '../parser/Statement';
4
import type { AALiteralExpression, AAMemberExpression, AnnotationExpression, ArrayLiteralExpression, BinaryExpression, CallExpression, CallfuncExpression, DottedGetExpression, EscapedCharCodeLiteralExpression, FunctionExpression, FunctionParameterExpression, GroupingExpression, IndexedGetExpression, LiteralExpression, NewExpression, NullCoalescingExpression, RegexLiteralExpression, SourceLiteralExpression, TaggedTemplateStringExpression, TemplateStringExpression, TemplateStringQuasiExpression, TernaryExpression, TypecastExpression, TypeExpression, UnaryExpression, VariableExpression, XmlAttributeGetExpression } from '../parser/Expression';
5
import { isExpression, isStatement } from './reflection';
1✔
6
import type { Editor } from './Editor';
7
import type { Statement, Expression, AstNode } from '../parser/AstNode';
8

9
/**
10
 * Walks the statements of a block and descendent sub-blocks, and allow replacing statements
11
 */
12
export function walkStatements(
1✔
13
    statement: Statement,
14
    visitor: (statement: Statement, parent?: Statement, owner?: any, key?: any) => Statement | void,
15
    cancel?: CancellationToken
16
): void {
17
    statement.walk(visitor as any, {
9✔
18
        walkMode: WalkMode.visitStatements,
19
        cancel: cancel
20
    });
21
}
22

23
export type WalkVisitor = <T = AstNode>(node: AstNode, parent?: AstNode, owner?: any, key?: any) => void | T;
24

25
/**
26
 * A helper function for Statement and Expression `walkAll` calls.
27
 * @returns a new AstNode if it was changed by returning from the visitor, or undefined if not
28
 */
29
export function walk<T>(owner: T, key: keyof T, visitor: WalkVisitor, options: WalkOptions, parent?: AstNode): AstNode | void {
1✔
30
    let returnValue: AstNode | void;
31

32
    //stop processing if canceled
33
    if (options.cancel?.isCancellationRequested) {
335,237✔
34
        return returnValue;
420✔
35
    }
36

37
    //the object we're visiting
38
    let element = owner[key] as any as AstNode;
334,817✔
39
    if (!element) {
334,817✔
40
        return returnValue;
52,884✔
41
    }
42

43
    //link this node to its parent
44
    parent = parent ?? owner as unknown as AstNode;
281,933✔
45
    element.parent = parent;
281,933✔
46

47
    //get current bsConsts
48
    if (!options.bsConsts) {
281,933✔
49
        options.bsConsts = element.getBsConsts();
27,149✔
50
    }
51

52
    //notify the visitor of this element
53
    if (element.visitMode & options.walkMode) {
281,933✔
54
        returnValue = visitor?.(element, element.parent as any, owner, key);
268,828✔
55

56
        //replace the value on the parent if the visitor returned a Statement or Expression (this is how visitors can edit AST)
57
        if (returnValue && (isExpression(returnValue) || isStatement(returnValue))) {
268,823✔
58
            //if we have an editor, use that to modify the AST
59
            if (options.editor) {
4✔
60
                options.editor.setProperty(owner, key, returnValue as any);
2✔
61

62
                //we don't have an editor, modify the AST directly
63
            } else {
64
                (owner as any)[key] = returnValue;
2✔
65
            }
66
        }
67
    }
68

69
    //stop processing if canceled
70
    if (options.cancel?.isCancellationRequested) {
281,928✔
71
        return returnValue;
1,684✔
72
    }
73

74
    //do not walk children if skipped
75
    if (options.skipChildren?.shouldSkipChildren) {
280,244✔
76
        options.skipChildren.reset();
3,762✔
77
        return;
3,762✔
78
    }
79

80
    //get the element again in case it was replaced by the visitor
81
    element = owner[key] as any as AstNode;
276,482✔
82
    if (!element) {
276,482✔
83
        return returnValue;
2✔
84
    }
85

86
    //set the parent of this new expression
87
    element.parent = parent;
276,480✔
88

89
    if (!element.walk) {
276,480!
UNCOV
90
        throw new Error(`${owner.constructor.name}["${String(key)}"]${parent ? ` for ${parent.constructor.name}` : ''} does not contain a "walk" method`);
×
91
    }
92
    //walk the child expressions
93
    element.walk(visitor, options);
276,480✔
94

95
    return returnValue;
276,469✔
96
}
97

98
/**
99
 * Helper for AST elements to walk arrays when visitors might change the array size (to delete/insert items).
100
 * @param array the array to walk
101
 * @param visitor the visitor function to call on match
102
 * @param options the walk optoins
103
 * @param parent the parent AstNode of each item in the array
104
 * @param filter a function used to filter items from the array. return true if that item should be walked
105
 */
106
export function walkArray<T extends AstNode = AstNode>(array: Array<T>, visitor: WalkVisitor, options: WalkOptions, parent?: AstNode, filter?: <T>(element: T) => boolean) {
1✔
107
    let processedNodes = new Set<AstNode>();
92,898✔
108

109
    for (let i = 0; i < array?.length; i++) {
92,898!
110
        if (!filter || filter(array[i])) {
104,819!
111
            let item = array[i];
104,819✔
112
            //skip already processed nodes for this array walk
113
            if (processedNodes.has(item)) {
104,819✔
114
                continue;
22✔
115
            }
116
            processedNodes.add(item);
104,797✔
117

118
            //if the walk produced a new node, we will assume the original node was handled, and the new node's children were walked, so we can skip it if we enter recovery mode
119
            const newNode = walk(array, i, visitor, options, parent);
104,797✔
120
            if (newNode) {
104,789✔
121
                processedNodes.add(newNode);
18✔
122
            }
123

124
            //if the current item changed, restart the entire loop (we'll skip any already-processed items)
125
            if (array[i] !== item) {
104,789✔
126
                i = -1;
38✔
127
            }
128
        }
129
    }
130
}
131

132
/**
133
 * Creates an optimized visitor function.
134
 * Conventional visitors will need to inspect each incoming Statement/Expression, leading to many if statements.
135
 * This function will compare the constructor of the Statement/Expression, and perform a SINGLE logical check
136
 * to know which function to call.
137
 */
138
export function createVisitor(
1✔
139
    visitor: {
140
        /**
141
         * Called for every Statement or Expression encountered by a walker (while still honoring the WalkMode options)
142
         * The more specific visitor functions will still be called.
143
         */
144
        AstNode?: (node: Statement | Expression, parent?: AstNode, owner?: any, key?: any) => AstNode | void;
145
        //statements
146
        Body?: (statement: Body, parent?: Statement, owner?: any, key?: any) => Statement | void;
147
        AssignmentStatement?: (statement: AssignmentStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
148
        Block?: (statement: Block, parent?: Statement, owner?: any, key?: any) => Statement | void;
149
        ExpressionStatement?: (statement: ExpressionStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
150
        ExitStatement?: (statement: ExitStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
151
        FunctionStatement?: (statement: FunctionStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
152
        IfStatement?: (statement: IfStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
153
        IncrementStatement?: (statement: IncrementStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
154
        PrintStatement?: (statement: PrintStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
155
        DimStatement?: (statement: DimStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
156
        GotoStatement?: (statement: GotoStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
157
        LabelStatement?: (statement: LabelStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
158
        ReturnStatement?: (statement: ReturnStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
159
        EndStatement?: (statement: EndStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
160
        StopStatement?: (statement: StopStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
161
        ForStatement?: (statement: ForStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
162
        ForEachStatement?: (statement: ForEachStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
163
        WhileStatement?: (statement: WhileStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
164
        DottedSetStatement?: (statement: DottedSetStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
165
        IndexedSetStatement?: (statement: IndexedSetStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
166
        LibraryStatement?: (statement: LibraryStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
167
        NamespaceStatement?: (statement: NamespaceStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
168
        ImportStatement?: (statement: ImportStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
169
        TypecastStatement?: (statement: TypecastStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
170
        InterfaceStatement?: (statement: InterfaceStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
171
        InterfaceFieldStatement?: (statement: InterfaceFieldStatement, parent?: Statement) => Statement | void;
172
        InterfaceMethodStatement?: (statement: InterfaceMethodStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
173
        ClassStatement?: (statement: ClassStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
174
        ContinueStatement?: (statement: ContinueStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
175
        MethodStatement?: (statement: MethodStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
176
        FieldStatement?: (statement: FieldStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
177
        TryCatchStatement?: (statement: TryCatchStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
178
        CatchStatement?: (statement: CatchStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
179
        ThrowStatement?: (statement: ThrowStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
180
        EnumStatement?: (statement: EnumStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
181
        EnumMemberStatement?: (statement: EnumMemberStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
182
        ConstStatement?: (statement: ConstStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
183
        ConditionalCompileStatement?: (statement: ConditionalCompileStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
184
        ConditionalCompileConstStatement?: (statement: ConditionalCompileConstStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
185
        ConditionalCompileErrorStatement?: (statement: ConditionalCompileErrorStatement, parent?: Statement, owner?: any, key?: any) => Statement | void;
186
        AliasStatement?: (statement: AliasStatement, parent?: AstNode, owner?: any, key?: any) => Statement | void;
187
        AugmentedAssignmentStatement?: (statement: AugmentedAssignmentStatement, parent?: AstNode, owner?: any, key?: any) => Statement | void;
188
        //expressions
189
        BinaryExpression?: (expression: BinaryExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
190
        CallExpression?: (expression: CallExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
191
        FunctionExpression?: (expression: FunctionExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
192
        FunctionParameterExpression?: (expression: FunctionParameterExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
193
        DottedGetExpression?: (expression: DottedGetExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
194
        XmlAttributeGetExpression?: (expression: XmlAttributeGetExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
195
        IndexedGetExpression?: (expression: IndexedGetExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
196
        GroupingExpression?: (expression: GroupingExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
197
        LiteralExpression?: (expression: LiteralExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
198
        EscapedCharCodeLiteralExpression?: (expression: EscapedCharCodeLiteralExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
199
        ArrayLiteralExpression?: (expression: ArrayLiteralExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
200
        AAMemberExpression?: (expression: AAMemberExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
201
        AALiteralExpression?: (expression: AALiteralExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
202
        UnaryExpression?: (expression: UnaryExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
203
        VariableExpression?: (expression: VariableExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
204
        SourceLiteralExpression?: (expression: SourceLiteralExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
205
        NewExpression?: (expression: NewExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
206
        CallfuncExpression?: (expression: CallfuncExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
207
        TemplateStringQuasiExpression?: (expression: TemplateStringQuasiExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
208
        TemplateStringExpression?: (expression: TemplateStringExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
209
        TaggedTemplateStringExpression?: (expression: TaggedTemplateStringExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
210
        AnnotationExpression?: (expression: AnnotationExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
211
        TernaryExpression?: (expression: TernaryExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
212
        NullCoalescingExpression?: (expression: NullCoalescingExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
213
        RegexLiteralExpression?: (expression: RegexLiteralExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
214
        TypeExpression?: (expression: TypeExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
215
        TypecastExpression?: (expression: TypecastExpression, parent?: AstNode, owner?: any, key?: any) => Expression | void;
216
    }
217
) {
218
    return <WalkVisitor>((statement: Statement, parent?: Statement, owner?: any, key?: any): Statement | void => {
12,476✔
219
        //call the generic AstNode visitor first (if defined)
220
        visitor.AstNode?.(statement, parent, owner, key);
193,006✔
221
        //now call the specifically-named visitor
222
        return visitor[statement.constructor.name]?.(statement, parent, owner, key);
193,006✔
223
    });
224
}
225

226
export interface WalkOptions {
227
    /**
228
     * What mode should the walker walk?
229
     * You can use the unique enums, or apply bitwise and to combine the various modes you're interested in
230
     */
231
    walkMode: WalkMode;
232
    /**
233
     * A token that can be used to cancel the walk operation
234
     */
235
    cancel?: CancellationToken;
236
    /**
237
     * If provided, any AST replacements will be done using this Editor instead of directly against the AST itself
238
     */
239
    editor?: Editor;
240
    /**
241
     * A token that can be used to stop the walk from going any deeper in the current node,
242
     * but will continue walking sibling nodes
243
     */
244
    skipChildren?: ChildrenSkipper;
245
    /**
246
     * Map of Conditional compilation flags, with names in lowercase
247
     */
248
    bsConsts?: Map<string, boolean>;
249
}
250

251
export class ChildrenSkipper {
1✔
252
    private isSkipped = false;
3,233✔
253

254
    public reset() {
255
        this.isSkipped = false;
3,762✔
256
    }
257

258
    public skip() {
259
        this.isSkipped = true;
3,762✔
260
    }
261

262
    get shouldSkipChildren() {
263
        return this.isSkipped;
16,792✔
264
    }
265
}
266

267

268
/**
269
 * An enum used to denote the specific WalkMode options (without
270
 */
271
export enum InternalWalkMode {
1✔
272
    /**
273
     * Walk statements
274
     */
275
    walkStatements = 1,
1✔
276
    /**
277
     * Call the visitor for every statement encountered by a walker
278
     */
279
    visitStatements = 2,
1✔
280
    /**
281
     * Walk expressions.
282
     */
283
    walkExpressions = 4,
1✔
284
    /**
285
     * Call the visitor for every expression encountered by a walker
286
     */
287
    visitExpressions = 8,
1✔
288
    /**
289
     * If child function expressions are encountered, this will allow the walker to step into them.
290
     */
291
    recurseChildFunctions = 16,
1✔
292
    /**
293
     * Step into conditional compilation blocks that are guarded by a flag that evaluates to false
294
     */
295
    visitFalseConditionalCompilationBlocks = 64
1✔
296
}
297

298
/* eslint-disable @typescript-eslint/prefer-literal-enum-member */
299
export enum WalkMode {
1✔
300
    /**
301
     * Walk statements, but does NOT step into child functions
302
     */
303
    walkStatements = InternalWalkMode.walkStatements,
1✔
304
    /**
305
     * Walk and visit statements, but does NOT step into child functions
306
     */
307
    visitStatements = InternalWalkMode.walkStatements | InternalWalkMode.visitStatements,
1✔
308
    /**
309
     * Walk expressions, but does NOT step into child functions
310
     */
311
    walkExpressions = InternalWalkMode.walkExpressions,
1✔
312
    /**
313
     * Walk and visit expressions of the statement, but doesn't walk child statements
314
     */
315
    visitLocalExpressions = InternalWalkMode.walkExpressions | InternalWalkMode.visitExpressions,
1✔
316
    /**
317
     * Walk and visit expressions, but does NOT step into child functions
318
     */
319
    visitExpressions = InternalWalkMode.walkStatements | InternalWalkMode.walkExpressions | InternalWalkMode.visitExpressions,
1✔
320
    /**
321
     * Visit all descendent statements and expressions, but does NOT step into child functions
322
     */
323
    visitAll = InternalWalkMode.walkStatements | InternalWalkMode.visitStatements | InternalWalkMode.walkExpressions | InternalWalkMode.visitExpressions,
1✔
324
    /**
325
     * If child function expressions are encountered, this will allow the walker to step into them.
326
     * This includes `WalkMode.walkExpressions`
327
     */
328
    recurseChildFunctions = InternalWalkMode.recurseChildFunctions | InternalWalkMode.walkExpressions,
1✔
329
    /**
330
     * Visit all descendent statements, and DOES step into child functions
331
     */
332
    visitStatementsRecursive = InternalWalkMode.walkStatements | InternalWalkMode.visitStatements | InternalWalkMode.walkExpressions | InternalWalkMode.recurseChildFunctions,
1✔
333
    /**
334
     * Visit all descendent expressions, and DOES step into child functions
335
     */
336
    visitExpressionsRecursive = InternalWalkMode.walkStatements | InternalWalkMode.walkExpressions | InternalWalkMode.visitExpressions | InternalWalkMode.recurseChildFunctions,
1✔
337
    /**
338
     * Visit all descendent statements and expressions, and DOES step into child functions
339
     */
340
    visitAllRecursive = InternalWalkMode.walkStatements | InternalWalkMode.visitStatements | InternalWalkMode.walkExpressions | InternalWalkMode.visitExpressions | InternalWalkMode.recurseChildFunctions
1✔
341
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc