import { templateKeys } from "../../common" ; import { TLE } from "../../extension.bundle" ; import { TemplateScope } from "../documents/templates/scopes/TemplateScope" ; import { UserFunctionDefinition } from "../documents/templates/UserFunctionDefinition" ; import { UserFunctionNamespaceDefinition } from "../documents/templates/UserFunctionNamespaceDefinition" ; import { BuiltinFunctionMetadata , FunctionsMetadata } from "../language/expressions/AzureRMAssets" ; import { FunctionCallValue , StringValue , TleVisitor , Value } from "../language/expressions/TLE" ; import { INamedDefinition } from "../language/INamedDefinition" ; import { Issue } from "../language/Issue" ; import { IssueKind } from "../language/IssueKind" ; import * as Reference from "../language/ReferenceList" ; import { Span } from "../language/Span" ; import { UnrecognizedBuiltinFunctionIssue , UnrecognizedUserFunctionIssue , UnrecognizedUserNamespaceIssue } from "./UnrecognizedFunctionIssues" ; import { validateBuiltInFunctionCallArgCounts , validateUserFunctionCallArgCounts } from "./validateFunctionCallArgCounts" ; /**
 * Finds all references to all definitions inside a JSON string.  While doing so, records all undefined
 * variable and parameter errors.
 */ export class FindReferencesAndErrorsVisitor extends TleVisitor { constructor ( private readonly _scope , private readonly _jsonStringStartIndex , private readonly _functionsMetadata , private readonly _referenceListsMap , private readonly _errors ) { super ( ) ; } private addReference ( definition , span ) { let list = this . _referenceListsMap . get ( definition ) ; if ( ! list ) { list = new Reference . ReferenceList ( definition . definitionKind ) ; this . _referenceListsMap . set ( definition , list ) ; } list . add ( { document : this . _scope . document , span : span . translate ( this . _jsonStringStartIndex ) } ) ; } private addError ( error ) { this . _errors . push ( error . translate ( this . _jsonStringStartIndex ) ) ; } private addErrorIf ( error ) { if ( error ) { this . addError ( error ) ; } } public visitFunctionCall ( tleFunctionCall ) { if ( ! tleFunctionCall . nameToken || ! tleFunctionCall . name ) { return ; } const namespaceName = tleFunctionCall . namespace ; const name = tleFunctionCall . name ; if ( tleFunctionCall . namespaceToken ) { if ( namespaceName ) { const userNamespaceDefinition = this . _scope . getFunctionNamespaceDefinition ( namespaceName ) ; if ( userNamespaceDefinition ) { this . addReference ( userNamespaceDefinition , tleFunctionCall . namespaceToken . span ) ; const userFunctionDefinition = this . _scope . getUserFunctionDefinition ( userNamespaceDefinition , name ) ; if ( userFunctionDefinition ) { this . addReference ( userFunctionDefinition , tleFunctionCall . nameToken . span ) ; this . addErrorIf ( validateUserFunctionCallArgCounts ( tleFunctionCall , userNamespaceDefinition , userFunctionDefinition ) ) ; } else { this . addError ( new UnrecognizedUserFunctionIssue ( tleFunctionCall . nameToken . span , namespaceName , tleFunctionCall . name ) ) ; } } else { this . addError ( new UnrecognizedUserNamespaceIssue ( tleFunctionCall . namespaceToken . span , namespaceName ) ) ; } } } if ( ! namespaceName ) { const metadata = this . _functionsMetadata . findbyName ( tleFunctionCall . name ) ; this . addBuiltFunctionRefOrError ( metadata , name , tleFunctionCall . nameToken ) ; if ( metadata ) { this . addErrorIf ( validateBuiltInFunctionCallArgCounts ( tleFunctionCall , metadata ) ) ; switch ( metadata . lowerCaseName ) { case templateKeys . parameters : this . addParameterRefOrError ( tleFunctionCall ) ; break ; case templateKeys . variables : this . addVariableRefOrError ( tleFunctionCall ) ; break ; default : break ; } } } super . visitFunctionCall ( tleFunctionCall ) ; } private addBuiltFunctionRefOrError ( metadata , functionName , functionNameToken ) { if ( metadata ) { this . addReference ( metadata , functionNameToken . span ) ; } else { this . addErrorIf ( new UnrecognizedBuiltinFunctionIssue ( functionNameToken . span , functionName ) ) ; } } private addVariableRefOrError ( tleFunctionCall ) { if ( tleFunctionCall . argumentExpressions . length === 1 ) { const arg = tleFunctionCall . argumentExpressions [ 0 ] ; if ( arg instanceof StringValue ) { const argName = arg . toString ( ) ; const varDefinition = this . _scope . getVariableDefinition ( argName ) ; if ( varDefinition ) { this . addReference ( varDefinition , arg . unquotedSpan ) ; } else { if ( this . _scope . isInUserFunction ) { this . addError ( new Issue ( arg . token . span , "User functions cannot reference variables" , IssueKind . varInUdf ) ) ; } else { this . addError ( new Issue ( arg . token . span , `Undefined variable reference: ${ arg . quotedValue }` , IssueKind . undefinedVar ) ) ; } } } } } private addParameterRefOrError ( tleFunctionCall ) { if ( tleFunctionCall . argumentExpressions . length === 1 ) { const arg = tleFunctionCall . argumentExpressions [ 0 ] ; if ( arg instanceof StringValue ) { const argName = arg . toString ( ) ; const paramDefinition = this . _scope . getParameterDefinition ( argName ) ; if ( paramDefinition ) { this . addReference ( paramDefinition , arg . unquotedSpan ) ; } else { this . addError ( new Issue ( arg . token . span , `Undefined parameter reference: ${ arg . quotedValue }` , IssueKind . undefinedParam ) ) ; } } } } public static visit ( scope , jsonStringStartIndex , tleValue , metadata , referenceListsMap , errors ) { const visitor = new FindReferencesAndErrorsVisitor ( scope , jsonStringStartIndex , metadata , referenceListsMap , errors ) ; if ( tleValue ) { tleValue . accept ( visitor ) ; } return visitor ; } }