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

rokucommunity / brighterscript / #13342

25 Nov 2024 08:44PM UTC coverage: 89.053% (+2.2%) from 86.874%
#13342

push

web-flow
Merge 961502182 into c5674f5d8

7359 of 8712 branches covered (84.47%)

Branch coverage included in aggregate %.

55 of 64 new or added lines in 9 files covered. (85.94%)

544 existing lines in 54 files now uncovered.

9724 of 10471 relevant lines covered (92.87%)

1825.46 hits per line

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

93.55
/src/parser/AstNode.ts
1
import type { WalkVisitor, WalkOptions } from '../astUtils/visitors';
2
import { WalkMode } from '../astUtils/visitors';
1✔
3
import type { Position, Range } from 'vscode-languageserver';
4
import { CancellationTokenSource } from 'vscode-languageserver';
1✔
5
import { InternalWalkMode } from '../astUtils/visitors';
1✔
6
import type { SymbolTable } from '../SymbolTable';
7
import type { BrsTranspileState } from './BrsTranspileState';
8
import type { TranspileResult } from '../interfaces';
9
import type { AnnotationExpression } from './Expression';
10
import util from '../util';
1✔
11

12
/**
13
 * A BrightScript AST node
14
 */
15
export abstract class AstNode {
1✔
16
    /**
17
     *  The starting and ending location of the node.
18
     */
19
    public abstract range: Range | undefined;
20

21
    public abstract transpile(state: BrsTranspileState): TranspileResult;
22

23
    /**
24
     * When being considered by the walk visitor, this describes what type of element the current class is.
25
     */
26
    public visitMode = InternalWalkMode.visitStatements;
25,396✔
27

28
    public abstract walk(visitor: WalkVisitor, options: WalkOptions);
29

30
    /**
31
     * The parent node for this statement. This is set dynamically during `onFileValidate`, and should not be set directly.
32
     */
33
    public parent?: AstNode;
34

35
    /**
36
     * Certain expressions or statements can have a symbol table (such as blocks, functions, namespace bodies, etc).
37
     * If you're interested in getting the closest SymbolTable, use `getSymbolTable` instead.
38
     */
39
    public symbolTable?: SymbolTable;
40

41
    /**
42
     * Get the closest symbol table for this node
43
     */
44
    public getSymbolTable(): SymbolTable {
45
        let node: AstNode = this;
5,711✔
46
        while (node) {
5,711✔
47
            if (node.symbolTable) {
9,581✔
48
                return node.symbolTable;
5,711✔
49
            }
50
            node = node.parent!;
3,870✔
51
        }
52

53
        //justification: we are following a chain of nodes until we get to one with a SymbolTable,
54
        //and the top-level node will always have a SymbolTable. So we'll never hit this undefined,
55
        //but it is not so easy to convince the typechecker of this.
UNCOV
56
        return undefined as any;
×
57
    }
58

59
    /**
60
     * Walk upward and return the first node that results in `true` from the matcher.
61
     * @param matcher a function called for each node. If you return true, this function returns the specified node. If you return a node, that node is returned. all other return values continue the loop
62
     *                The function's second parameter is a cancellation token. If you'd like to short-circuit the walk, call `cancellationToken.cancel()`, then this function will return `undefined`
63
     */
64
    public findAncestor<TNode extends AstNode = AstNode>(matcher: (node: AstNode, cancellationToken: CancellationTokenSource) => boolean | AstNode | undefined | void): TNode | undefined {
65
        let node = this.parent;
12,234✔
66

67
        const cancel = new CancellationTokenSource();
12,234✔
68
        while (node) {
12,234✔
69
            let matcherValue = matcher(node, cancel);
24,259✔
70
            if (cancel.token.isCancellationRequested) {
24,259✔
71
                return;
1✔
72
            }
73
            if (matcherValue) {
24,258✔
74
                cancel.cancel();
2,129✔
75
                return (matcherValue === true ? node : matcherValue) as TNode;
2,129✔
76

77
            }
78
            node = node.parent;
22,129✔
79
        }
80
    }
81

82
    /**
83
     * Find the first child where the matcher evaluates to true.
84
     * @param matcher a function called for each node. If you return true, this function returns the specified node. If you return a node, that node is returned. all other return values continue the loop
85
     */
86
    public findChild<TNode = AstNode>(matcher: (node: AstNode, cancellationSource) => boolean | AstNode | undefined | void, options?: WalkOptions): TNode | undefined {
87
        const cancel = new CancellationTokenSource();
1,703✔
88
        let result: AstNode | undefined;
89
        this.walk((node) => {
1,703✔
90
            const matcherValue = matcher(node, cancel);
3,336✔
91
            if (matcherValue) {
3,336✔
92
                cancel.cancel();
1,510✔
93
                result = matcherValue === true ? node : matcherValue;
1,510✔
94
            }
95
        }, {
96
            walkMode: WalkMode.visitAllRecursive,
97
            ...options ?? {},
5,109!
98
            cancel: cancel.token
99
        });
100
        return result as unknown as TNode;
1,703✔
101
    }
102

103
    /**
104
     * FInd the deepest child that includes the given position
105
     */
106
    public findChildAtPosition<TNodeType extends AstNode = AstNode>(position: Position, options?: WalkOptions): TNodeType | undefined {
107
        return this.findChild<TNodeType>((node) => {
1,456✔
108
            //if the current node includes this range, keep that node
109
            if (util.rangeContains(node.range, position)) {
2,577✔
110
                return node.findChildAtPosition(position, options) ?? node;
1,272✔
111
            }
112
        }, options);
113
    }
114

115
    /**
116
     * Links all child nodes to their parent AstNode, and the same with symbol tables. This performs a full AST walk, so you should use this sparingly
117
     */
118
    public link() {
119
        //the act of walking causes the nodes to be linked
120
        this.walk(() => { }, {
2,912✔
121
            walkMode: WalkMode.visitAllRecursive
122
        });
123
    }
124

125
    /**
126
     * Clone this node and all of its children. This creates a completely detached and identical copy of the AST.
127
     * All tokens, statements, expressions, range, and location are cloned.
128
     */
129
    public abstract clone();
130

131
    /**
132
     * Helper function for creating a clone. This will clone any attached annotations, as well as reparent the cloned node's children to the clone
133
     */
134
    protected finalizeClone<T extends AstNode>(
135
        clone: T,
136
        propsToReparent?: Array<{ [K in keyof T]: T[K] extends AstNode | AstNode[] ? K : never }[keyof T]>
137
    ) {
138
        //clone the annotations if they exist
139
        if (Array.isArray((this as unknown as Statement).annotations)) {
893✔
140
            (clone as unknown as Statement).annotations = (this as unknown as Statement).annotations?.map(x => x.clone());
8!
141
        }
142
        //reparent all of the supplied props
143
        for (let key of propsToReparent ?? []) {
893✔
144
            const children = (Array.isArray(clone?.[key]) ? clone[key] : [clone?.[key]]) as any[];
840!
145
            for (let child of children ?? []) {
840!
146
                if (child) {
852✔
147
                    (clone[key as any] as AstNode).parent = clone;
738✔
148
                }
149
            }
150
        }
151
        return clone;
893✔
152
    }
153

154
}
155

156
export abstract class Statement extends AstNode {
1✔
157
    /**
158
     * When being considered by the walk visitor, this describes what type of element the current class is.
159
     */
160
    public visitMode = InternalWalkMode.visitStatements;
13,692✔
161
    /**
162
     * Annotations for this statement
163
     */
164
    public annotations: AnnotationExpression[] | undefined;
165
}
166

167

168
/** A BrightScript expression */
169
export abstract class Expression extends AstNode {
1✔
170
    /**
171
     * When being considered by the walk visitor, this describes what type of element the current class is.
172
     */
173
    public visitMode = InternalWalkMode.visitExpressions;
11,704✔
174
}
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