public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet) { var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _, out var fileUri); var uri = DocumentUri.From(fileUri); var client = await IntegrationTestHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri, resourceTypeProvider : AzResourceTypeProvider.CreateWithAzTypes(), fileResolver : BicepTestConstants.FileResolver); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var symbolReferences = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (node is ISymbolReference || node is ITopLevelNamedDeclarationSyntax) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated); foreach (var symbolReference in symbolReferences) { // by default, request a hover on the first character of the syntax, but for certain syntaxes, this doesn't make sense. // for example on an instance function call 'az.resourceGroup()', it only makes sense to request a hover on the 3rd character. var nodeForHover = symbolReference switch { ITopLevelDeclarationSyntax d => d.Keyword, ResourceAccessSyntax r => r.ResourceName, FunctionCallSyntaxBase f => f.Name, _ => symbolReference, }; var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), Position = TextCoordinateConverter.GetPosition(lineStarts, nodeForHover.Span.Position) }); // fancy method to give us some annotated source code to look at if any assertions fail :) using (new AssertionScope().WithVisualCursor(compilation.SyntaxTreeGrouping.EntryPoint, nodeForHover.Span.ToZeroLengthSpan())) { if (!symbolTable.TryGetValue(symbolReference, out var symbol)) { if (symbolReference is InstanceFunctionCallSyntax && compilation.GetEntrypointSemanticModel().GetSymbolInfo(symbolReference) is FunctionSymbol ifcSymbol) { ValidateHover(hover, ifcSymbol); break; } // symbol ref not bound to a symbol hover.Should().BeNull(); continue; } switch (symbol !.Kind) {
private IEnumerable <Diagnostic> ValidateIdentifierAccess(ParameterDeclarationSyntax syntax) { return(SyntaxAggregator.Aggregate(syntax, new List <Diagnostic>(), (accumulated, current) => { if (current is VariableAccessSyntax) { var symbol = bindings[current]; // Error: already has error info attached, no need to add more // Parameter: references are permitted in other parameters' default values as long as there is not a cycle (BCP080) // Function: we already validate that a function cannot be used as a variable (BCP063) // Output: we already validate that outputs cannot be referenced in expressions (BCP058) if (symbol.Kind != SymbolKind.Error && symbol.Kind != SymbolKind.Parameter && symbol.Kind != SymbolKind.Function && symbol.Kind != SymbolKind.Output) { accumulated.Add(DiagnosticBuilder.ForPosition(current).CannotReferenceSymbolInParamDefaultValue()); } } return accumulated; }, accumulated => accumulated)); }
public async Task NonFunctionCallSyntaxShouldProvideNoSignatureHelp(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var tree = compilation.SyntaxTreeGrouping.EntryPoint; var nonFunctions = SyntaxAggregator.Aggregate( tree.ProgramSyntax, new List <SyntaxBase>(), (accumulated, current) => { if (current is not FunctionCallSyntaxBase) { accumulated.Add(current); } return(accumulated); }, accumulated => accumulated, // requesting signature help on non-function nodes that are placed inside function call nodes will produce signature help // since we don't want that, stop the visitor from visiting inner nodes when a function call is encountered (accumulated, current) => current is not FunctionCallSyntaxBase); foreach (var nonFunction in nonFunctions) { var position = PositionHelper.GetPosition(tree.LineStarts, nonFunction.Span.Position); var signatureHelp = await RequestSignatureHelp(client, position, uri); signatureHelp.Should().BeNull(); } }
/// <summary> /// Returns all syntax nodes that represent a reference to the specified symbol. This includes the definitions of the symbol as well. /// Unusued declarations will return 1 result. Unused and undeclared symbols (functions, namespaces, for example) may return an empty list. /// </summary> /// <param name="symbol">The symbol</param> public IEnumerable <SyntaxBase> FindReferences(Symbol symbol) => SyntaxAggregator.Aggregate(this.SyntaxTree.ProgramSyntax, new List <SyntaxBase>(), (accumulated, current) => { if (object.ReferenceEquals(symbol, this.GetSymbolInfo(current))) { accumulated.Add(current); } return(accumulated); }, accumulated => accumulated);
public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri, resourceTypeProvider : new AzResourceTypeProvider(new TypeLoader())); // construct a parallel compilation var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var symbolReferences = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (node is ISymbolReference || node is ITopLevelNamedDeclarationSyntax) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated); foreach (SyntaxBase symbolReference in symbolReferences) { var nodeForHover = symbolReference switch { ITopLevelDeclarationSyntax d => d.Keyword, ResourceAccessSyntax r => r.ResourceName, _ => symbolReference, }; var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, nodeForHover.Span.Position) }); // fancy method to give us some annotated source code to look at if any assertions fail :) using (CreateAssertionScopeWithContext(compilation.SyntaxTreeGrouping.EntryPoint, hover, nodeForHover.Span.ToZeroLengthSpan())) { if (symbolTable.TryGetValue(symbolReference, out var symbol) == false) { // symbol ref not bound to a symbol hover.Should().BeNull(); continue; } switch (symbol !.Kind) {
public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { // local function bool IsUnboundNode(IDictionary <SyntaxBase, Symbol> dictionary, SyntaxBase syntax) => dictionary.ContainsKey(syntax) == false && !(syntax is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var unboundNodes = SyntaxAggregator.Aggregate( source: compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, seed: new List <SyntaxBase>(), function: (accumulated, syntax) => { if (IsUnboundNode(symbolTable, syntax) && !(syntax is ProgramSyntax)) { // only collect unbound nodes non-program nodes accumulated.Add(syntax); } return(accumulated); }, resultSelector: accumulated => accumulated, // visit children only if current node is not bound continuationFunction: (accumulated, syntax) => IsUnboundNode(symbolTable, syntax)); foreach (var syntax in unboundNodes) { var offset = syntax switch { // base expression could be a variable access which is bound and will throw off the test PropertyAccessSyntax propertyAccess => propertyAccess.PropertyName.Span.Position, ArrayAccessSyntax arrayAccess => arrayAccess.OpenSquare.Span.Position, _ => syntax.Span.Position }; var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, offset) }); // go to definition on a syntax node that isn't bound to a symbol should produce an empty response response.Should().BeEmpty(); } }
private static List <SyntaxBase> GetAllBoundSymbolReferences(ProgramSyntax program, SemanticModel semanticModel) { return(SyntaxAggregator.Aggregate( program, new List <SyntaxBase>(), (accumulated, current) => { if (current is ISymbolReference symbolReference && TestSyntaxHelper.NodeShouldBeBound(symbolReference)) { accumulated.Add(current); } return accumulated; },
public static IDictionary <SyntaxBase, Symbol> ReconstructSymbolTable(this Compilation compilation) { var model = compilation.GetEntrypointSemanticModel(); var syntaxNodes = SyntaxAggregator.Aggregate(compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { accumulated.Add(node); return(accumulated); }, accumulated => accumulated); return(syntaxNodes .Where(syntax => model.GetSymbolInfo(syntax) != null) .ToDictionary(syntax => syntax, syntax => model.GetSymbolInfo(syntax) !)); }
public static IDictionary <SyntaxBase, Symbol> ReconstructSymbolTable(this Compilation compilation) { var model = compilation.GetEntrypointSemanticModel(); return(SyntaxAggregator.Aggregate(compilation.SourceFileGrouping.EntryPoint.ProgramSyntax, new Dictionary <SyntaxBase, Symbol>(), (accumulated, node) => { if (model.GetSymbolInfo(node) is Symbol symbol) { accumulated[node] = symbol; } return accumulated; }, accumulated => accumulated)); }
public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { // local function bool IsUnboundNode(IDictionary <SyntaxBase, Symbol> dictionary, SyntaxBase syntax) => dictionary.ContainsKey(syntax) == false && !(syntax is Token); var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SourceFileGrouping.EntryPoint.LineStarts; var unboundNodes = SyntaxAggregator.Aggregate( source: compilation.SourceFileGrouping.EntryPoint.ProgramSyntax, seed: new List <SyntaxBase>(), function: (accumulated, syntax) => { if (IsUnboundNode(symbolTable, syntax) && !(syntax is ProgramSyntax)) { // only collect unbound nodes non-program nodes accumulated.Add(syntax); } return(accumulated); }, resultSelector: accumulated => accumulated, // visit children only if current node is not bound continuationFunction: (accumulated, syntax) => IsUnboundNode(symbolTable, syntax)); for (int i = 0; i < unboundNodes.Count(); i++) { var syntax = unboundNodes[i]; if (ValidUnboundNode(unboundNodes, i)) { continue; } var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); // go to definition on a syntax node that isn't bound to a symbol should produce an empty response response.Should().BeEmpty(); } }
private static List <SyntaxBase> GetSymbolReferences(ProgramSyntax program) { return(SyntaxAggregator.Aggregate( program, new List <SyntaxBase>(), (accumulated, current) => { if (current is ISymbolReference) { accumulated.Add(current); } return accumulated; }, accumulated => accumulated)); }
public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); // construct a parallel compilation var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var symbolReferences = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (node is ISymbolReference || node is INamedDeclarationSyntax) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated); foreach (SyntaxBase symbolReference in symbolReferences) { var syntaxPosition = symbolReference is IDeclarationSyntax declaration ? declaration.Keyword.Span.Position : symbolReference.Span.Position; var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, syntaxPosition) }); if (symbolTable.TryGetValue(symbolReference, out var symbol) == false) { // symbol ref not bound to a symbol hover.Should().BeNull(); continue; } switch (symbol !.Kind) {
public async Task RenamingNonSymbolsShouldProduceEmptyEdit(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is IDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolToSyntaxLookup = symbolTable .Where(pair => pair.Value.Kind != SymbolKind.Error) .ToLookup(pair => pair.Value, pair => pair.Key); var wrongNodes = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var edit = await client.RequestRename(new RenameParams { NewName = "NewIdentifier", TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); edit.DocumentChanges.Should().BeNullOrEmpty(); edit.Changes.Should().BeNull(); } }
public void NonEmtyFile_GetParent_ShouldReturnExpectedNode() { var hierarchy = new SyntaxHierarchy(); var program = SyntaxFactory.CreateFromText("param foo string\r\nvar bar = 42"); hierarchy.AddRoot(program); hierarchy.GetParent(program).Should().BeNull(); var nodes = SyntaxAggregator.Aggregate(program, new List <SyntaxBase>(), (accumulated, current) => { accumulated.Add(current); return(accumulated); }, accumulated => accumulated); var paramDecl = nodes.OfType <ParameterDeclarationSyntax>().Single(); hierarchy.GetParent(paramDecl).Should().BeSameAs(program); var varDecl = nodes.OfType <VariableDeclarationSyntax>().Single(); hierarchy.GetParent(varDecl).Should().BeSameAs(program); var newLine = nodes.OfType <Token>().Single(t => t.Type == TokenType.NewLine); hierarchy.GetParent(newLine).Should().BeSameAs(program); var paramIdSyntax = nodes.OfType <IdentifierSyntax>().Single(id => string.Equals(id.IdentifierName, "foo")); hierarchy.GetParent(paramIdSyntax).Should().BeSameAs(paramDecl); var varIdSyntax = nodes.OfType <IdentifierSyntax>().Single(id => string.Equals(id.IdentifierName, "bar")); hierarchy.GetParent(varIdSyntax).Should().BeSameAs(varDecl); var paramTypeSyntax = nodes.OfType <TypeSyntax>().Single(); hierarchy.GetParent(paramTypeSyntax).Should().BeSameAs(paramDecl); var paramTypeToken = nodes.OfType <Token>().Single(t => t.Type == TokenType.Identifier && string.Equals(t.Text, "string")); hierarchy.GetParent(paramTypeToken).Should().BeSameAs(paramTypeSyntax); }
public async Task ShouldProvideSignatureHelpBetweenFunctionParentheses(DataSet dataSet) { var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var symbolTable = compilation.ReconstructSymbolTable(); var tree = compilation.SourceFileGrouping.EntryPoint; var functionCalls = SyntaxAggregator.Aggregate( tree.ProgramSyntax, new List <FunctionCallSyntaxBase>(), (accumulated, current) => { if (current is FunctionCallSyntaxBase functionCallBase) { accumulated.Add(functionCallBase); } return(accumulated); }, accumulated => accumulated); foreach (FunctionCallSyntaxBase functionCall in functionCalls) { var expectDecorator = compilation.GetEntrypointSemanticModel().Binder.GetParent(functionCall) is DecoratorSyntax; var symbol = compilation.GetEntrypointSemanticModel().GetSymbolInfo(functionCall); // if the cursor is present immediate after the function argument opening paren, // the signature help can only show the signature of the enclosing function var startOffset = functionCall.OpenParen.GetEndPosition(); await ValidateOffset(client, uri, tree, startOffset, symbol as FunctionSymbol, expectDecorator); // if the cursor is present immediately before the function argument closing paren, // the signature help can only show the signature of the enclosing function var endOffset = functionCall.CloseParen.Span.Position; await ValidateOffset(client, uri, tree, endOffset, symbol as FunctionSymbol, expectDecorator); } }
public async Task GoToDefinitionOnUnboundSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet) { // local function bool IsUnboundNode(IDictionary <SyntaxBase, Symbol> dictionary, SyntaxBase syntax) => dictionary.ContainsKey(syntax) == false && !(syntax is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var unboundNodes = SyntaxAggregator.Aggregate( source: compilation.ProgramSyntax, seed: new List <SyntaxBase>(), function: (accumulated, syntax) => { if (IsUnboundNode(symbolTable, syntax) && !(syntax is ProgramSyntax)) { // only collect unbound nodes non-program nodes accumulated.Add(syntax); } return(accumulated); }, resultSelector: accumulated => accumulated, // visit children only if current node is not bound continuationFunction: (accumulated, syntax) => IsUnboundNode(symbolTable, syntax)); foreach (var syntax in unboundNodes) { var response = await client.RequestDefinition(new DefinitionParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); // go to definition on a syntax node that isn't bound to a symbol should produce an empty response response.Should().BeEmpty(); } }
public async Task FindReferencesOnNonSymbolsShouldProduceEmptyResult(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is ITopLevelNamedDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var wrongNodes = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = false }, Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); locations.Should().BeEmpty(); } }
public async Task FindReferencesOnNonSymbolsShouldProduceEmptyResult(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is IDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(TestResourceTypeProvider.Create(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var wrongNodes = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var locations = await client.RequestReferences(new ReferenceParams { TextDocument = new TextDocumentIdentifier(uri), Context = new ReferenceContext { IncludeDeclaration = false }, Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); locations.Should().BeEmpty(); } }
public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); // construct a parallel compilation var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep)); var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var symbolReferences = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (node is ISymbolReference || node is IDeclarationSyntax) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated); foreach (SyntaxBase symbolReference in symbolReferences) { var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, symbolReference.Span.Position) }); if (symbolTable.TryGetValue(symbolReference, out var symbol) == false) { // symbol ref not bound to a symbol ValidateEmptyHover(hover); continue; } switch (symbol !.Kind) {
public async Task ShouldProvideSignatureHelpBetweenFunctionParentheses(DataSet dataSet) { var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var symbolTable = compilation.ReconstructSymbolTable(); var tree = compilation.SyntaxTreeGrouping.EntryPoint; var functionCalls = SyntaxAggregator.Aggregate( tree.ProgramSyntax, new List <FunctionCallSyntaxBase>(), (accumulated, current) => { if (current is FunctionCallSyntaxBase functionCallBase) { accumulated.Add(functionCallBase); } return(accumulated); }, accumulated => accumulated); foreach (FunctionCallSyntaxBase functionCall in functionCalls) { symbolTable.TryGetValue(functionCall, out var symbol); // if the cursor is present immediate after the function argument opening paren, // the signature help can only show the signature of the enclosing function var startOffset = functionCall.OpenParen.GetEndPosition(); await ValidateOffset(client, uri, tree, startOffset, symbol as FunctionSymbol); // if the cursor is present immediately before the function argument closing paren, // the signature help can only show the signature of the enclosing function var endOffset = functionCall.CloseParen.Span.Position; await ValidateOffset(client, uri, tree, endOffset, symbol as FunctionSymbol); } }
public async Task NonFunctionCallSyntaxShouldProvideNoSignatureHelp(DataSet dataSet) { var(compilation, _, fileUri) = await dataSet.SetupPrerequisitesAndCreateCompilation(TestContext); var uri = DocumentUri.From(fileUri); using var helper = await LanguageServerHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri); var client = helper.Client; var bicepFile = compilation.SourceFileGrouping.EntryPoint; var nonFunctions = SyntaxAggregator.Aggregate( bicepFile.ProgramSyntax, new List <SyntaxBase>(), (accumulated, current) => { if (current is not FunctionCallSyntaxBase) { accumulated.Add(current); } return(accumulated); }, accumulated => accumulated, // requesting signature help on non-function nodes that are placed inside function call nodes will produce signature help // since we don't want that, stop the visitor from visiting inner nodes when a function call is encountered (accumulated, current) => current is not FunctionCallSyntaxBase); foreach (var nonFunction in nonFunctions) { using (new AssertionScope().WithVisualCursor(bicepFile, nonFunction.Span.ToZeroLengthSpan())) { var position = PositionHelper.GetPosition(bicepFile.LineStarts, nonFunction.Span.Position); var signatureHelp = await RequestSignatureHelp(client, position, uri); signatureHelp.Should().BeNull(); } } }
public async Task RequestingHighlightsForWrongNodeShouldProduceNoHighlights(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is INamedDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithTextAsync(dataSet.Bicep, uri); var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; var wrongNodes = SyntaxAggregator.Aggregate( compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var highlights = await client.RequestDocumentHighlight(new DocumentHighlightParams { TextDocument = new TextDocumentIdentifier(uri), Position = IntegrationTestHelper.GetPosition(lineStarts, syntax) }); highlights.Should().BeNull(); } }
public async Task RequestingHighlightsForWrongNodeShouldProduceNoHighlights(DataSet dataSet) { // local function bool IsWrongNode(SyntaxBase node) => !(node is ISymbolReference) && !(node is IDeclarationSyntax) && !(node is Token); var uri = DocumentUri.From($"/{dataSet.Name}"); using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri); var compilation = new Compilation(SyntaxFactory.CreateFromText(dataSet.Bicep)); var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep); var wrongNodes = SyntaxAggregator.Aggregate( compilation.ProgramSyntax, new List <SyntaxBase>(), (accumulated, node) => { if (IsWrongNode(node) && !(node is ProgramSyntax)) { accumulated.Add(node); } return(accumulated); }, accumulated => accumulated, (accumulated, node) => IsWrongNode(node)); foreach (var syntax in wrongNodes) { var highlights = await client.RequestDocumentHighlight(new DocumentHighlightParams { TextDocument = new TextDocumentIdentifier(uri), Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); highlights.Should().BeEmpty(); } }
private IEnumerable <ErrorDiagnostic> ValidateIdentifierAccess() { return(SyntaxAggregator.Aggregate(this.DeclaringParameter, new List <ErrorDiagnostic>(), (accumulated, current) => { if (current is VariableAccessSyntax) { Symbol?symbol = this.Context.Bindings.TryGetValue(current); // excluded symbol kinds already generate errors - no need to duplicate if (symbol != null && symbol.Kind != SymbolKind.Error && symbol.Kind != SymbolKind.Parameter && symbol.Kind != SymbolKind.Function && symbol.Kind != SymbolKind.Output) { accumulated.Add(DiagnosticBuilder.ForPosition(current).CannotReferenceSymbolInParamDefaultValue()); } } return accumulated; }, accumulated => accumulated)); }