Skip to content

Commit f6fa0be

Browse files
authored
Merge pull request #70 from SSlinky/dev
Better Syntax Error Message
2 parents 308a171 + e0a5be3 commit f6fa0be

File tree

14 files changed

+151
-88
lines changed

14 files changed

+151
-88
lines changed

client/src/test/diagnostics.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ suite('Should get diagnostics', () => {
9191
range: toRange(28, 7, 32, 8),
9292
severity: vscode.DiagnosticSeverity.Error,
9393
source: 'ex'
94+
},
95+
{
96+
message: 'Invalid syntax: InvalidSubCall(arg)',
97+
range: toRange(43, 4, 43, 23),
98+
severity: vscode.DiagnosticSeverity.Error,
99+
source: 'ex'
94100
}
95101
]);
96102
});
@@ -156,8 +162,8 @@ async function testDiagnostics(docUri: vscode.Uri, expectedDiagnostics: vscode.D
156162

157163
expectedDiagnostics.forEach((expectedDiagnostic, i) => {
158164
const actualDiagnostic = actualDiagnostics[i];
159-
assert.equal(actualDiagnostic.message, expectedDiagnostic.message, "Message");
160-
assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range, "Range");
161-
assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity, "Severity");
165+
assert.equal(actualDiagnostic.message, expectedDiagnostic.message, `Message: expected '${expectedDiagnostic.message}' got '${actualDiagnostic.message}'.`);
166+
assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range, `Range: expected '${JSON.stringify(expectedDiagnostic.range)}' got '${JSON.stringify(actualDiagnostic.range)}'.`);
167+
assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity, `Severity: expected '${expectedDiagnostic.severity}' got '${actualDiagnostic.severity}'.`);
162168
});
163169
}

server/src/capabilities/capabilities.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import { ParserRuleContext, TerminalNode } from 'antlr4ng';
1414
// Project
1515
import { SemanticToken } from '../capabilities/semanticTokens';
1616
import { FoldingRange, FoldingRangeKind } from '../capabilities/folding';
17-
import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base';
17+
import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, BaseSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base';
1818
import { BaseDiagnostic, DuplicateDeclarationDiagnostic, MethodVariableIsPublicDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics';
1919
import { Services } from '../injection/services';
2020

2121

2222
abstract class BaseCapability {
23-
element: BaseContextSyntaxElement<ParserRuleContext>;
23+
element: BaseRuleSyntaxElement<ParserRuleContext> | BaseSyntaxElement;
2424

25-
constructor(element: BaseContextSyntaxElement<ParserRuleContext>) {
25+
constructor(element: BaseRuleSyntaxElement<ParserRuleContext> | BaseSyntaxElement) {
2626
this.element = element;
2727
}
2828
}
@@ -34,17 +34,20 @@ export class FoldingRangeCapability extends BaseCapability {
3434
closeWord?: string;
3535

3636
get foldingRange(): FoldingRange {
37-
const trailingLineCount = this.element.context.rule.countTrailingLineEndings();
38-
const start = this.element.context.range.start;
37+
// Cast the element to the same type we get in the constructor.
38+
const element = this.element as unknown as BaseRuleSyntaxElement<ParserRuleContext>;
39+
40+
const trailingLineCount = element.context.rule.countTrailingLineEndings();
41+
const start = element.context.range.start;
3942
const end = {
40-
line: this.element.context.range.end.line - trailingLineCount,
41-
character: this.element.context.range.end.character
43+
line: element.context.range.end.line - trailingLineCount,
44+
character: element.context.range.end.character
4245
};
4346
const range = Range.create(start, end);
4447
return new FoldingRange(range, this.foldingRangeKind, this.openWord, this.closeWord);
4548
}
4649

47-
constructor(element: BaseContextSyntaxElement<ParserRuleContext>, foldingRangeKind?: FoldingRangeKind) {
50+
constructor(element: BaseRuleSyntaxElement<ParserRuleContext>, foldingRangeKind?: FoldingRangeKind) {
4851
super(element);
4952
this.foldingRangeKind = foldingRangeKind;
5053
}
@@ -55,7 +58,7 @@ export class DiagnosticCapability extends BaseCapability {
5558
diagnostics: Diagnostic[] = [];
5659
evaluate: (...args: any[]) => Diagnostic[];
5760

58-
constructor(element: BaseContextSyntaxElement<ParserRuleContext>, evaluate?: (...args: any[]) => Diagnostic[]) {
61+
constructor(element: BaseSyntaxElement, evaluate?: (...args: any[]) => Diagnostic[]) {
5962
super(element);
6063
this.evaluate = evaluate ?? (() => this.diagnostics);
6164
}
@@ -69,7 +72,7 @@ export class SemanticTokenCapability extends BaseCapability {
6972
private overrideLength?: number;
7073

7174
get semanticToken(): SemanticToken {
72-
const element = this.element as BaseContextSyntaxElement<ParserRuleContext> & HasSemanticTokenCapability;
75+
const element = this.element as BaseRuleSyntaxElement<ParserRuleContext> & HasSemanticTokenCapability;
7376
const context = element.identifierCapability
7477
? new Context(element.identifierCapability.nameContext, element.context.document)
7578
: element.context;
@@ -89,7 +92,7 @@ export class SemanticTokenCapability extends BaseCapability {
8992
);
9093
}
9194

92-
constructor(element: BaseContextSyntaxElement<ParserRuleContext> & HasSemanticTokenCapability, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[], overrideRange?: Range, overrideLength?: number) {
95+
constructor(element: BaseRuleSyntaxElement<ParserRuleContext> & HasSemanticTokenCapability, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[], overrideRange?: Range, overrideLength?: number) {
9396
super(element);
9497
this.tokenType = tokenType;
9598
this.tokenModifiers = tokenModifiers;
@@ -119,7 +122,7 @@ export class SymbolInformationCapability extends BaseCapability {
119122
}
120123

121124
interface IdentifierArgs {
122-
element: BaseContextSyntaxElement<ParserRuleContext>,
125+
element: BaseRuleSyntaxElement<ParserRuleContext>,
123126
getNameContext?: () => ParserRuleContext | TerminalNode | null | undefined,
124127
formatName?: (name: string) => string,
125128
defaultName?: string;
@@ -217,7 +220,7 @@ export class ScopeItemCapability {
217220
visibilityModifierContext?: ParserRuleContext;
218221

219222
constructor(
220-
readonly element?: BaseContextSyntaxElement<ParserRuleContext>,
223+
readonly element?: BaseRuleSyntaxElement<ParserRuleContext>,
221224
public type: ItemType = ItemType.REFERENCE,
222225
public assignmentType: AssignmentType = AssignmentType.NONE,
223226
public parent?: ScopeItemCapability,

server/src/capabilities/semanticTokens.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { ParserRuleContext } from 'antlr4ng/dist/ParserRuleContext';
1313

1414
// Project
15-
import { BaseContextSyntaxElement, HasSemanticTokenCapability } from '../project/elements/base';
15+
import { BaseRuleSyntaxElement, HasSemanticTokenCapability } from '../project/elements/base';
1616

1717
const registeredTokenTypes = new Map<string, number>((Object.keys(SemanticTokenTypes) as (keyof typeof SemanticTokenTypes)[]).map((k, i) => ([k, i])));
1818
const registeredTokenModifiers = new Map<string, number>((Object.keys(SemanticTokenModifiers) as (keyof typeof SemanticTokenModifiers)[]).map((k, i) => ([k, 2 ** i])));
@@ -30,7 +30,7 @@ export function activateSemanticTokenProvider(result: InitializeResult) {
3030

3131

3232
type SemanticElementType = HasSemanticTokenCapability
33-
& BaseContextSyntaxElement<ParserRuleContext>;
33+
& BaseRuleSyntaxElement<ParserRuleContext>;
3434

3535
export class SemanticToken {
3636
line: uinteger;

server/src/project/document.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { SyntaxParser } from './parser/vbaParser';
1111
import { FoldingRange } from '../capabilities/folding';
1212
import { SemanticTokensManager } from '../capabilities/semanticTokens';
1313
import {
14-
BaseContextSyntaxElement,
14+
BaseRuleSyntaxElement,
1515
BaseSyntaxElement,
1616
DeclarableElement,
1717
HasDiagnosticCapability,
@@ -54,7 +54,7 @@ export abstract class BaseProjectDocument {
5454
protected foldableElements: FoldingRange[] = [];
5555
protected hasDiagnosticElements: HasDiagnosticCapability[] = [];
5656
protected properties: Dictionary<string, PropertyDeclarationElement> = new Dictionary(() => new PropertyDeclarationElement());
57-
protected redactedElements: BaseContextSyntaxElement<ParserRuleContext>[] = [];
57+
protected redactedElements: BaseRuleSyntaxElement<ParserRuleContext>[] = [];
5858
protected semanticTokens: SemanticTokensManager = new SemanticTokensManager();
5959
protected symbolInformations: SymbolInformation[] = [];
6060
protected unhandledNamedElements: [] = [];
@@ -255,7 +255,7 @@ export abstract class BaseProjectDocument {
255255
return this;
256256
};
257257

258-
registerSubtractElement = (element: BaseContextSyntaxElement<ParserRuleContext>) => {
258+
registerSubtractElement = (element: BaseRuleSyntaxElement<ParserRuleContext>) => {
259259
this.redactedElements.push(element);
260260
return this;
261261
};

server/src/project/elements/base.ts

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Position, Range } from 'vscode-languageserver';
33
import { TextDocument } from 'vscode-languageserver-textdocument';
44

55
// Antlr
6-
import { ParserRuleContext, TerminalNode } from 'antlr4ng';
6+
import { Parser, ParserRuleContext, TerminalNode } from 'antlr4ng';
77

88
// Project
99
import {
@@ -16,9 +16,9 @@ import {
1616
} from '../../capabilities/capabilities';
1717

1818

19-
export abstract class BaseSyntaxElement<T extends ParserRuleContext> {
19+
export abstract class BaseSyntaxElement {
2020
// Base Properties
21-
context?: Context<T>;
21+
context?: Context<ParserRuleContext | TerminalNode>;
2222
identifierCapability?: IdentifierCapability;
2323

2424
// Capabilities
@@ -28,21 +28,33 @@ export abstract class BaseSyntaxElement<T extends ParserRuleContext> {
2828
symbolInformationCapability?: SymbolInformationCapability;
2929
scopeItemCapability?: ScopeItemCapability;
3030

31-
constructor();
32-
constructor(ctx: T, doc: TextDocument)
33-
constructor(ctx?: T, doc?: TextDocument) {
34-
if (!!ctx && !!doc) this.context = new Context(ctx, doc);
31+
/**
32+
*
33+
*/
34+
constructor() {
35+
let x: TerminalNode | ParserRuleContext;
36+
37+
}
38+
}
39+
40+
41+
export abstract class BaseRuleSyntaxElement<T extends ParserRuleContext> extends BaseSyntaxElement {
42+
context!: Context<T>;
43+
44+
constructor(ctx: T, doc: TextDocument) {
45+
super();
46+
this.context = new Context(ctx, doc);
3547
}
3648

3749
/**
3850
* Checks if this element is a child of another element.
3951
* @returns True if the element is a child of the passed element.
4052
*/
4153
isChildOf(range: Range): boolean;
42-
isChildOf(element: BaseSyntaxElement<T>): boolean;
43-
isChildOf(elementOrRange: BaseSyntaxElement<T> | Range): boolean {
54+
isChildOf(element: BaseRuleSyntaxElement<T>): boolean;
55+
isChildOf(elementOrRange: BaseRuleSyntaxElement<T> | Range): boolean {
4456
const a = this.context?.range;
45-
const b = ((o: any): o is BaseSyntaxElement<T> => 'context' in o)(elementOrRange)
57+
const b = ((o: any): o is BaseRuleSyntaxElement<T> => 'context' in o)(elementOrRange)
4658
? elementOrRange.context?.range
4759
: elementOrRange;
4860

@@ -59,7 +71,7 @@ export abstract class BaseSyntaxElement<T extends ParserRuleContext> {
5971
* Compare two syntax elements for equality.
6072
* @returns True if the document, range, and text match.
6173
*/
62-
equals(element: BaseSyntaxElement<T>): boolean {
74+
equals(element: BaseRuleSyntaxElement<T>): boolean {
6375
const a = this.context;
6476
const b = element.context;
6577

@@ -71,16 +83,7 @@ export abstract class BaseSyntaxElement<T extends ParserRuleContext> {
7183
}
7284

7385

74-
export abstract class BaseContextSyntaxElement<T extends ParserRuleContext> extends BaseSyntaxElement<T> implements HasContext<T> {
75-
context!: Context<T>;
76-
77-
constructor(ctx: T, doc: TextDocument) {
78-
super(ctx, doc);
79-
}
80-
}
81-
82-
83-
export abstract class BaseIdentifyableSyntaxElement<T extends ParserRuleContext> extends BaseContextSyntaxElement<T> implements IsIdentifiable {
86+
export abstract class BaseIdentifyableSyntaxElement<T extends ParserRuleContext> extends BaseRuleSyntaxElement<T> implements IsIdentifiable {
8487
abstract identifierCapability: IdentifierCapability;
8588

8689
constructor(ctx: T, doc: TextDocument) {
@@ -155,4 +158,4 @@ export interface HasFoldingRangeCapability {
155158
// Compound Types
156159
// ---------------------------------------------------------
157160

158-
export type DeclarableElement = BaseContextSyntaxElement<ParserRuleContext> & IsIdentifiable & HasDiagnosticCapability;
161+
export type DeclarableElement = BaseRuleSyntaxElement<ParserRuleContext> & IsIdentifiable & HasDiagnosticCapability;

server/src/project/elements/flow.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import { AnyOperatorContext, IfStatementContext, WhileStatementContext } from '.
66

77
// Project
88
import { DiagnosticCapability, FoldingRangeCapability } from '../../capabilities/capabilities';
9-
import { BaseContextSyntaxElement, HasDiagnosticCapability } from './base';
9+
import { BaseRuleSyntaxElement, HasDiagnosticCapability } from './base';
1010
import { MultipleOperatorsDiagnostic, WhileWendDeprecatedDiagnostic } from '../../capabilities/diagnostics';
1111

12-
export class IfElseBlock extends BaseContextSyntaxElement<IfStatementContext> {
12+
export class IfElseBlock extends BaseRuleSyntaxElement<IfStatementContext> {
1313
constructor(context: IfStatementContext, document: TextDocument) {
1414
super(context, document);
1515
this.foldingRangeCapability = new FoldingRangeCapability(this);
@@ -19,7 +19,7 @@ export class IfElseBlock extends BaseContextSyntaxElement<IfStatementContext> {
1919
}
2020

2121

22-
export class WhileLoopElement extends BaseContextSyntaxElement<WhileStatementContext> implements HasDiagnosticCapability {
22+
export class WhileLoopElement extends BaseRuleSyntaxElement<WhileStatementContext> implements HasDiagnosticCapability {
2323
diagnosticCapability: DiagnosticCapability;
2424

2525
constructor(context: WhileStatementContext, document: TextDocument) {
@@ -35,7 +35,7 @@ export class WhileLoopElement extends BaseContextSyntaxElement<WhileStatementCon
3535
}
3636

3737

38-
export class DuplicateOperatorElement extends BaseContextSyntaxElement<AnyOperatorContext> implements HasDiagnosticCapability {
38+
export class DuplicateOperatorElement extends BaseRuleSyntaxElement<AnyOperatorContext> implements HasDiagnosticCapability {
3939
diagnosticCapability: DiagnosticCapability;
4040

4141
constructor(context: AnyOperatorContext, document: TextDocument) {

server/src/project/elements/generic.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
import { TextDocument } from 'vscode-languageserver-textdocument';
33

44
// Antlr
5-
import { ErrorNode, ParserRuleContext } from 'antlr4ng';
5+
import { ErrorNode, ParserRuleContext, TerminalNode } from 'antlr4ng';
66

77
// Project
8-
import { BaseContextSyntaxElement, BaseSyntaxElement } from './base';
8+
import { BaseRuleSyntaxElement, BaseSyntaxElement, Context } from './base';
99
import { DiagnosticCapability } from '../../capabilities/capabilities';
1010
import { ParserErrorDiagnostic } from '../../capabilities/diagnostics';
1111

1212

13-
export class ErrorRuleElement extends BaseContextSyntaxElement<ParserRuleContext> {
13+
export class ErrorRuleElement extends BaseSyntaxElement {
14+
context: Context<TerminalNode>;
1415
constructor(node: ErrorNode, doc: TextDocument) {
15-
super(node.parent as ParserRuleContext, doc);
16+
super();
17+
this.context = new Context(node, doc);
1618
this.diagnosticCapability = new DiagnosticCapability(this);
1719
this.diagnosticCapability.diagnostics.push(
1820
new ParserErrorDiagnostic(this.context.range, node.getText())

server/src/project/elements/module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from '../../antlr/out/vbaParser';
1717

1818
// Project
19-
import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, HasDiagnosticCapability } from './base';
19+
import { BaseRuleSyntaxElement, BaseIdentifyableSyntaxElement, HasDiagnosticCapability } from './base';
2020
import { DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities';
2121
import { DuplicateAttributeDiagnostic, IgnoredAttributeDiagnostic, MissingAttributeDiagnostic, MissingOptionExplicitDiagnostic } from '../../capabilities/diagnostics';
2222

@@ -185,7 +185,7 @@ export class ClassElement extends BaseModuleElement<ClassModuleContext> {
185185
}
186186

187187

188-
export class ModuleIgnoredAttributeElement extends BaseContextSyntaxElement<ParserRuleContext> implements HasDiagnosticCapability {
188+
export class ModuleIgnoredAttributeElement extends BaseRuleSyntaxElement<ParserRuleContext> implements HasDiagnosticCapability {
189189
diagnosticCapability: DiagnosticCapability;
190190

191191
constructor(ctx: IgnoredClassAttrContext | IgnoredProceduralAttrContext, doc: TextDocument) {

server/src/project/elements/precompiled.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import { CompilerConditionalBlockContext, CompilerDefaultBlockContext, CompilerI
66

77
// Project
88
import { DiagnosticCapability, FoldingRangeCapability } from '../../capabilities/capabilities';
9-
import { BaseContextSyntaxElement } from '../elements/base';
9+
import { BaseRuleSyntaxElement } from '../elements/base';
1010
import { UnreachableCodeDiagnostic } from '../../capabilities/diagnostics';
1111

1212

13-
export class CompilerLogicalBlock extends BaseContextSyntaxElement<CompilerIfBlockContext> {
13+
export class CompilerLogicalBlock extends BaseRuleSyntaxElement<CompilerIfBlockContext> {
1414
conditionalBlocks: CompilerConditionBlock[] = [];
1515
inactiveBlocks: CompilerConditionBlock[] = [];
1616

@@ -38,7 +38,7 @@ export class CompilerLogicalBlock extends BaseContextSyntaxElement<CompilerIfBlo
3838
}
3939

4040

41-
class CompilerConditionBlock extends BaseContextSyntaxElement<CompilerConditionalBlockContext | CompilerDefaultBlockContext> {
41+
class CompilerConditionBlock extends BaseRuleSyntaxElement<CompilerConditionalBlockContext | CompilerDefaultBlockContext> {
4242
readonly documentSettings: { environment: { os: string, version: string } };
4343

4444
constructor(ctx: CompilerConditionalBlockContext | CompilerDefaultBlockContext, doc: TextDocument, env: { environment: { os: string, version: string } }) {

server/src/project/elements/procedure.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ import {
1515
} from '../../antlr/out/vbaParser';
1616

1717
// Project
18-
import { BaseContextSyntaxElement, HasDiagnosticCapability, HasSymbolInformationCapability } from './base';
18+
import { BaseRuleSyntaxElement, HasDiagnosticCapability, HasSymbolInformationCapability } from './base';
1919
import { AssignmentType, DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities';
2020

2121
interface HasProcedureScope {
2222
procedureScope(): ProcedureScopeContext | null
2323
}
2424

25-
abstract class BaseProcedureElement<T extends ParserRuleContext & HasProcedureScope> extends BaseContextSyntaxElement<T> implements HasDiagnosticCapability, HasSymbolInformationCapability {
25+
abstract class BaseProcedureElement<T extends ParserRuleContext & HasProcedureScope> extends BaseRuleSyntaxElement<T> implements HasDiagnosticCapability, HasSymbolInformationCapability {
2626
diagnosticCapability: DiagnosticCapability;
2727
foldingRangeCapability: FoldingRangeCapability;
2828
symbolInformationCapability: SymbolInformationCapability;

0 commit comments

Comments
 (0)