Skip to content

Commit

Permalink
Unknown annotations are flagged as 'cannot-find-name'
Browse files Browse the repository at this point in the history
  • Loading branch information
markwpearce committed Jan 8, 2025
1 parent 8a14002 commit 0123053
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 19 deletions.
30 changes: 29 additions & 1 deletion src/bscPlugin/validation/BrsFileValidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { FloatType } from '../../types/FloatType';
import { IntegerType } from '../../types/IntegerType';
import { InterfaceType } from '../../types/InterfaceType';
import { StringType } from '../../types/StringType';
import { DynamicType, TypedFunctionType } from '../../types';
import { DynamicType, TypedFunctionType, VoidType } from '../../types';
import { ParseMode } from '../../parser/Parser';
import type { ExtraSymbolData } from '../../interfaces';

Expand Down Expand Up @@ -1332,4 +1332,32 @@ describe('BrsFileValidator', () => {
expectDiagnostics(program, []);
});
});

describe('annotations', () => {
it('validates when unknown annotation is used', () => {
program.setFile<BrsFile>('source/main.bs', `
@unknownAnnotation
sub someFunc()
print "hello"
end sub
`);
program.validate();
expectDiagnostics(program, [
DiagnosticMessages.cannotFindName('unknownAnnotation')
]);
});

it('allows known annotations', () => {
program.pluginAnnotationTable.addSymbol('knownAnnotation', { pluginName: 'Test' }, new TypedFunctionType(VoidType.instance).setName('knownAnnotation'), SymbolTypeFlag.annotation);

program.setFile<BrsFile>('source/main.bs', `
@knownAnnotation
sub someFunc()
print "hello"
end sub
`);
program.validate();
expectZeroDiagnostics(program);
});
});
});
66 changes: 48 additions & 18 deletions src/bscPlugin/validation/BrsFileValidator.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { isAliasStatement, isArrayType, isBlock, isBody, isClassStatement, isConditionalCompileConstStatement, isConditionalCompileErrorStatement, isConditionalCompileStatement, isConstStatement, isDottedGetExpression, isDottedSetStatement, isEnumStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isImportStatement, isIndexedGetExpression, isIndexedSetStatement, isInterfaceStatement, isInvalidType, isLibraryStatement, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTypecastExpression, isTypecastStatement, isUnaryExpression, isVariableExpression, isVoidType, isWhileStatement } from '../../astUtils/reflection';
import { isAliasStatement, isArrayType, isBlock, isBody, isClassStatement, isConditionalCompileConstStatement, isConditionalCompileErrorStatement, isConditionalCompileStatement, isConstStatement, isDottedGetExpression, isDottedSetStatement, isEnumStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isImportStatement, isIndexedGetExpression, isIndexedSetStatement, isInterfaceStatement, isInvalidType, isLibraryStatement, isLiteralExpression, isMethodStatement, isNamespaceStatement, isStatement, isTypecastExpression, isTypecastStatement, isUnaryExpression, isVariableExpression, isVoidType, isWhileStatement } from '../../astUtils/reflection';
import { createVisitor, WalkMode } from '../../astUtils/visitors';
import { DiagnosticMessages } from '../../DiagnosticMessages';
import type { BrsFile } from '../../files/BrsFile';
import type { ExtraSymbolData, OnFileValidateEvent } from '../../interfaces';
import { TokenKind } from '../../lexer/TokenKind';
import type { AstNode, Expression, Statement } from '../../parser/AstNode';
import { CallExpression, type FunctionExpression, type LiteralExpression } from '../../parser/Expression';
import type { FunctionExpression, LiteralExpression } from '../../parser/Expression';
import { CallExpression } from '../../parser/Expression';
import { ParseMode } from '../../parser/Parser';
import type { ContinueStatement, EnumMemberStatement, EnumStatement, ForEachStatement, ForStatement, ImportStatement, LibraryStatement, Body, WhileStatement, TypecastStatement, Block, AliasStatement } from '../../parser/Statement';
import { SymbolTypeFlag } from '../../SymbolTypeFlag';
Expand Down Expand Up @@ -277,23 +278,10 @@ export class BrsFileValidator {

},
AstNode: (node) => {
//check for doc comments
if (!node.leadingTrivia || node.leadingTrivia.length === 0) {
return;
}
const doc = brsDocParser.parseNode(node);
if (doc.tags.length === 0) {
return;
}

let funcExpr = node.findAncestor<FunctionExpression>(isFunctionExpression);
if (funcExpr) {
// handle comment tags inside a function expression
this.processDocTagsInFunction(doc, node, funcExpr);
} else {
//handle comment tags outside of a function expression
this.processDocTagsAtTopLevel(doc, node);
if (isStatement(node)) {
this.validateAnnotations(node);
}
this.handleDocTags(node);
}
});

Expand All @@ -304,6 +292,27 @@ export class BrsFileValidator {
});
}


private handleDocTags(node: AstNode) {
//check for doc comments
if (!node.leadingTrivia || node.leadingTrivia.length === 0) {
return;
}
const doc = brsDocParser.parseNode(node);
if (doc.tags.length === 0) {
return;
}

let funcExpr = node.findAncestor<FunctionExpression>(isFunctionExpression);
if (funcExpr) {
// handle comment tags inside a function expression
this.processDocTagsInFunction(doc, node, funcExpr);
} else {
//handle comment tags outside of a function expression
this.processDocTagsAtTopLevel(doc, node);
}
}

private processDocTagsInFunction(doc: BrightScriptDoc, node: AstNode, funcExpr: FunctionExpression) {
//TODO: Handle doc tags that influence the function they're in

Expand Down Expand Up @@ -663,4 +672,25 @@ export class BrsFileValidator {
}
}
}

private validateAnnotations(statement: Statement) {
if (!statement.annotations || statement.annotations.length < 1) {
return;
}

const symbolTable = this.event.program.pluginAnnotationTable;
const extraData: ExtraSymbolData = {};

for (const annotation of statement.annotations) {
const annotationSymbol = symbolTable.getSymbolType(annotation.name, { flags: SymbolTypeFlag.annotation, data: extraData });

if (!annotationSymbol) {
this.event.program.diagnostics.register({
...DiagnosticMessages.cannotFindName(annotation.name),
location: brsDocParser.getTypeLocationFromToken(annotation.tokens.name) ?? annotation.location
});
}
}
}

}

0 comments on commit 0123053

Please sign in to comment.