Example #1
0
        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)
                    {
Example #2
0
        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));
        }
Example #3
0
        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();
            }
        }
Example #4
0
        /// <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);
Example #5
0
        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)
                    {
Example #6
0
        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();
            }
        }
Example #7
0
        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) !));
        }
Example #9
0
        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();
            }
        }
Example #11
0
        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));
        }
Example #12
0
        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)
                {
Example #13
0
        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();
            }
        }
Example #14
0
        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);
            }
        }
Example #16
0
        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();
            }
        }
Example #17
0
        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();
            }
        }
Example #18
0
        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();
            }
        }
Example #19
0
        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)
                {
Example #20
0
        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();
                }
            }
        }
Example #22
0
        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();
            }
        }
Example #23
0
        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();
            }
        }
Example #24
0
        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));
        }