示例#1
0
        public void ProgramsShouldProduceExpectedUserDeclaredSymbols(DataSet dataSet)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));
            var model       = compilation.GetSemanticModel();

            var symbols = SymbolCollector
                          .CollectSymbols(model)
                          .OfType <DeclaredSymbol>();

            var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep);

            string getLoggingString(DeclaredSymbol symbol)
            {
                (_, var startChar) = TextCoordinateConverter.GetPosition(lineStarts, symbol.DeclaringSyntax.Span.Position);

                return($"{symbol.Kind} {symbol.Name}. Type: {symbol.Type}. Declaration start char: {startChar}, length: {symbol.DeclaringSyntax.Span.Length}");
            }

            var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(dataSet, symbols, symb => symb.NameSyntax.Span, getLoggingString);
            var resultsFile         = FileHelper.SaveResultFile(this.TestContext !, Path.Combine(dataSet.Name, DataSet.TestFileMainSymbols), sourceTextWithDiags);

            sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput(
                dataSet.Symbols,
                expectedLocation: OutputHelper.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainSymbols),
                actualLocation: resultsFile);
        }
示例#2
0
        public void LockedModeShouldBlockAccess()
        {
            const string expectedMessage = "Properties of the symbol context should not be accessed until name binding is completed.";

            var bindings       = new Dictionary <SyntaxBase, Symbol>();
            var cyclesBySyntax = new Dictionary <SyntaxBase, ImmutableArray <DeclaredSymbol> >();
            var context        = new SymbolContext(new TypeManager(TestResourceTypeProvider.CreateRegistrar(), bindings, cyclesBySyntax), bindings);

            Action byName = () =>
            {
                var tm = context.TypeManager;
            };

            byName.Should().Throw <InvalidOperationException>().WithMessage(expectedMessage);

            Action byNode = () =>
            {
                var b = context.Bindings;
            };

            byNode.Should().Throw <InvalidOperationException>().WithMessage(expectedMessage);

            context.Unlock();
            context.TypeManager.Should().NotBeNull();
            context.Bindings.Should().NotBeNull();
        }
示例#3
0
        public async Task GoToDefinitionRequestOnUnsupportedOrInvalidSyntaxNodeShouldReturnEmptyResponse(DataSet dataSet)
        {
            var uri = DocumentUri.From($"/{dataSet.Name}");

            using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri);

            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));
            var symbolTable = compilation.ReconstructSymbolTable();
            var lineStarts  = TextCoordinateConverter.GetLineStarts(dataSet.Bicep);

            var undeclaredSymbolBindings = symbolTable.Where(pair => !(pair.Value is DeclaredSymbol));

            foreach (var(syntax, _) in undeclaredSymbolBindings)
            {
                var response = await client.RequestDefinition(new DefinitionParams
                {
                    TextDocument = new TextDocumentIdentifier(uri),
                    Position     = PositionHelper.GetPosition(lineStarts, syntax.Span.Position)
                });

                // go to definition on a symbol that isn't declared by the user (like error or function symbol)
                // should produce an empty response
                response.Should().BeEmpty();
            }
        }
示例#4
0
        public async Task RenamingFunctionsShouldProduceEmptyEdit(DataSet dataSet)
        {
            var uri = DocumentUri.From($"/{dataSet.Name}");

            using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri);

            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));
            var symbolTable = compilation.ReconstructSymbolTable();
            var lineStarts  = TextCoordinateConverter.GetLineStarts(dataSet.Bicep);

            var validFunctionCallPairs = symbolTable
                                         .Where(pair => pair.Value.Kind == SymbolKind.Function)
                                         .Select(pair => pair.Key);

            foreach (var syntax in validFunctionCallPairs)
            {
                var edit = await client.RequestRename(new RenameParams
                {
                    NewName      = "NewIdentifier",
                    TextDocument = new TextDocumentIdentifier(uri),
                    Position     = PositionHelper.GetPosition(lineStarts, syntax.Span.Position)
                });

                edit.DocumentChanges.Should().BeNullOrEmpty();
                edit.Changes.Should().BeNull();
            }
        }
示例#5
0
        public async Task HighlightsShouldShowAllReferencesOfTheSymbol(DataSet dataSet)
        {
            var uri = DocumentUri.From($"/{dataSet.Name}");

            using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri);

            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), 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);

            foreach (var(syntax, symbol) in symbolTable.Where(s => s.Value.Kind != SymbolKind.Error))
            {
                var highlights = await client.RequestDocumentHighlight(new DocumentHighlightParams
                {
                    TextDocument = new TextDocumentIdentifier(uri),
                    Position     = PositionHelper.GetPosition(lineStarts, syntax.Span.Position)
                });

                // calculate expected highlights
                var expectedHighlights = symbolToSyntaxLookup[symbol].Select(node => CreateExpectedHighlight(lineStarts, node));

                // ranges should match what we got from our own symbol table
                highlights.Should().BeEquivalentTo(expectedHighlights);
            }
        }
示例#6
0
        public void EmptyProgram_SyntaxNodeShouldBePersisted()
        {
            var program     = SyntaxFactory.CreateFromText(DataSets.Empty.Bicep);
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), program);

            compilation.ProgramSyntax.Should().BeSameAs(program);
            compilation.GetSemanticModel().Should().NotBeNull();
        }
示例#7
0
        private EmitResult EmitTemplate(string text, string filePath)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(text));
            var emitter     = new TemplateEmitter(compilation.GetSemanticModel());

            using var stream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
            return(emitter.Emit(stream));
        }
示例#8
0
        private EmitResult EmitTemplate(string text, MemoryStream memoryStream)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(text));
            var emitter     = new TemplateEmitter(compilation.GetSemanticModel());

            TextWriter tw = new StreamWriter(memoryStream);

            return(emitter.Emit(tw));
        }
        public void NonDeclarationContextShouldReturnDeclarationTypeCompletions()
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(string.Empty));
            var provider    = new BicepCompletionProvider();

            var completions = provider.GetFilteredCompletions(compilation.GetSemanticModel(), new BicepCompletionContext(BicepCompletionContextKind.None));
            var declarationTypeCompletions = completions.Where(c => c.Kind == CompletionItemKind.Class).ToList();

            declarationTypeCompletions.Should().SatisfyRespectively(
                c =>
            {
                const string expected = "array";
                c.Label.Should().Be(expected);
                c.Kind.Should().Be(CompletionItemKind.Class);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be(expected);
                c.Detail.Should().Be(expected);
            },
                c =>
            {
                const string expected = "bool";
                c.Label.Should().Be(expected);
                c.Kind.Should().Be(CompletionItemKind.Class);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be(expected);
                c.Detail.Should().Be(expected);
            },
                c =>
            {
                const string expected = "int";
                c.Label.Should().Be(expected);
                c.Kind.Should().Be(CompletionItemKind.Class);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be(expected);
                c.Detail.Should().Be(expected);
            },
                c =>
            {
                const string expected = "object";
                c.Label.Should().Be(expected);
                c.Kind.Should().Be(CompletionItemKind.Class);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be(expected);
                c.Detail.Should().Be(expected);
            },
                c =>
            {
                const string expected = "string";
                c.Label.Should().Be(expected);
                c.Kind.Should().Be(CompletionItemKind.Class);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be(expected);
                c.Detail.Should().Be(expected);
            });
        }
示例#10
0
        public void Create_ShouldReturnValidCompilation()
        {
            var provider = new BicepCompilationProvider(TestResourceTypeProvider.CreateRegistrar());

            var context = provider.Create(DataSets.Parameters_LF.Bicep);

            context.Compilation.Should().NotBeNull();
            context.Compilation.GetSemanticModel().GetAllDiagnostics().Should().BeEmpty();
            context.LineStarts.Should().NotBeEmpty();
            context.LineStarts[0].Should().Be(0);
        }
        public void DeclarationContextShouldReturnKeywordCompletions()
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(string.Empty));

            compilation.GetSemanticModel().GetAllDiagnostics().Should().BeEmpty();

            var provider = new BicepCompletionProvider();

            var completions = provider.GetFilteredCompletions(compilation.GetSemanticModel(), new BicepCompletionContext(BicepCompletionContextKind.Declaration));

            var keywordCompletions = completions
                                     .Where(c => c.Kind == CompletionItemKind.Keyword)
                                     .OrderBy(c => c.Label)
                                     .ToList();

            keywordCompletions.Should().SatisfyRespectively(
                c =>
            {
                c.Label.Should().Be("output");
                c.Kind.Should().Be(CompletionItemKind.Keyword);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be("output");
                c.CommitCharacters.Should().OnlyContain(s => string.Equals(s, " "));
                c.Detail.Should().Be("Output keyword");
            },
                c =>
            {
                c.Label.Should().Be("param");
                c.Kind.Should().Be(CompletionItemKind.Keyword);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be("param");
                c.CommitCharacters.Should().OnlyContain(s => string.Equals(s, " "));
                c.Detail.Should().Be("Parameter keyword");
            },
                c =>
            {
                c.Label.Should().Be("resource");
                c.Kind.Should().Be(CompletionItemKind.Keyword);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be("resource");
                c.CommitCharacters.Should().OnlyContain(s => string.Equals(s, " "));
                c.Detail.Should().Be("Resource keyword");
            },
                c =>
            {
                c.Label.Should().Be("var");
                c.Kind.Should().Be(CompletionItemKind.Keyword);
                c.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
                c.InsertText.Should().Be("var");
                c.CommitCharacters.Should().OnlyContain(s => string.Equals(s, " "));
                c.Detail.Should().Be("Variable keyword");
            });
        }
示例#12
0
        private IEnumerable <string> GetAllDiagnostics(string text, string bicepFilePath)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(text));
            var lineStarts  = TextCoordinateConverter.GetLineStarts(text);

            return(compilation.GetSemanticModel()
                   .GetAllDiagnostics()
                   .Select(d =>
            {
                var(line, character) = TextCoordinateConverter.GetPosition(lineStarts, d.Span.Position);
                return $"{bicepFilePath}({line + 1},{character + 1}) : {d.Level} {d.Code}: {d.Message}";
            }));
        }
        public void DeclaringSymbolWithFunctionNameShouldHideTheFunctionCompletion()
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(@"
param concat string
var resourceGroup = true
resource base64 'Microsoft.Foo/foos@2020-09-01' = {
  name: 'foo'
}
output length int = 42
"));

            compilation.GetSemanticModel().GetAllDiagnostics().Should().BeEmpty();

            var provider    = new BicepCompletionProvider();
            var completions = provider.GetFilteredCompletions(compilation.GetSemanticModel(), new BicepCompletionContext(BicepCompletionContextKind.None)).ToList();

            AssertExpectedFunctions(completions, new[] { "concat", "resourceGroup", "base64" });

            // outputs can't be referenced so they should not show up in completions
            completions.Where(c => c.Kind == SymbolKind.Output.ToCompletionItemKind()).Should().BeEmpty();

            const string expectedVariable   = "resourceGroup";
            var          variableCompletion = completions.Single(c => c.Kind == SymbolKind.Variable.ToCompletionItemKind());

            variableCompletion.Label.Should().Be(expectedVariable);
            variableCompletion.Kind.Should().Be(CompletionItemKind.Variable);
            variableCompletion.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
            variableCompletion.InsertText.Should().Be(expectedVariable);
            variableCompletion.CommitCharacters.Should().BeNull();
            variableCompletion.Detail.Should().Be(expectedVariable);

            const string expectedResource   = "base64";
            var          resourceCompletion = completions.Single(c => c.Kind == SymbolKind.Resource.ToCompletionItemKind());

            resourceCompletion.Label.Should().Be(expectedResource);
            resourceCompletion.Kind.Should().Be(CompletionItemKind.Interface);
            resourceCompletion.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
            resourceCompletion.InsertText.Should().Be(expectedResource);
            resourceCompletion.CommitCharacters.Should().BeNull();
            resourceCompletion.Detail.Should().Be(expectedResource);

            const string expectedParam   = "concat";
            var          paramCompletion = completions.Single(c => c.Kind == SymbolKind.Parameter.ToCompletionItemKind());

            paramCompletion.Label.Should().Be(expectedParam);
            paramCompletion.Kind.Should().Be(CompletionItemKind.Field);
            paramCompletion.InsertTextFormat.Should().Be(InsertTextFormat.PlainText);
            paramCompletion.InsertText.Should().Be(expectedParam);
            paramCompletion.CommitCharacters.Should().BeNull();
            paramCompletion.Detail.Should().Be(expectedParam);
        }
示例#14
0
        public void NameBindingsShouldBeConsistent(DataSet dataSet)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));

            var symbolReferences = GetSymbolReferences(compilation.ProgramSyntax);

            // just a sanity check
            symbolReferences.Should().AllBeAssignableTo <ISymbolReference>();

            var model = compilation.GetSemanticModel();

            foreach (SyntaxBase symbolReference in symbolReferences)
            {
                var symbol = model.GetSymbolInfo(symbolReference);
                symbol.Should().NotBeNull();

                if (dataSet.IsValid)
                {
                    // valid cases should not return error symbols for any symbol reference node
                    symbol.Should().NotBeOfType <ErrorSymbol>();
                    symbol.Should().Match(s =>
                                          s is ParameterSymbol ||
                                          s is VariableSymbol ||
                                          s is ResourceSymbol ||
                                          s is OutputSymbol ||
                                          s is FunctionSymbol);
                }
                else
                {
                    // invalid files may return errors
                    symbol.Should().Match(s =>
                                          s is ErrorSymbol ||
                                          s is ParameterSymbol ||
                                          s is VariableSymbol ||
                                          s is ResourceSymbol ||
                                          s is OutputSymbol ||
                                          s is FunctionSymbol);
                }

                var foundRefs = model.FindReferences(symbol !);

                // the returned references should contain the original ref that we used to find the symbol
                foundRefs.Should().Contain(symbolReference);

                // each ref should map to the same exact symbol
                foreach (SyntaxBase foundRef in foundRefs)
                {
                    model.GetSymbolInfo(foundRef).Should().BeSameAs(symbol);
                }
            }
        }
        public void NonDeclarationContextInEmptyFileShouldReturnFunctionCompletions()
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(string.Empty));

            compilation.GetSemanticModel().GetAllDiagnostics().Should().BeEmpty();

            var provider = new BicepCompletionProvider();

            var completions = provider.GetFilteredCompletions(compilation.GetSemanticModel(), new BicepCompletionContext(BicepCompletionContextKind.None)).ToList();

            completions.Where(c => c.Kind == SymbolKind.Variable.ToCompletionItemKind()).Should().BeEmpty();
            completions.Where(c => c.Kind == SymbolKind.Output.ToCompletionItemKind()).Should().BeEmpty();
            completions.Where(c => c.Kind == SymbolKind.Resource.ToCompletionItemKind()).Should().BeEmpty();
            completions.Where(c => c.Kind == SymbolKind.Parameter.ToCompletionItemKind()).Should().BeEmpty();

            AssertExpectedFunctions(completions);
        }
示例#16
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.StartServerWithText(dataSet.Bicep, uri);

            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), 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     = PositionHelper.GetPosition(lineStarts, syntax.Span.Position)
                });

                edit.DocumentChanges.Should().BeNullOrEmpty();
                edit.Changes.Should().BeNull();
            }
        }
示例#17
0
        public void FindReferencesResultsShouldIncludeAllSymbolReferenceSyntaxNodes(DataSet dataSet)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));

            var symbolReferences = GetSymbolReferences(compilation.ProgramSyntax);

            var symbols = symbolReferences
                          .Select(symRef => compilation.GetSemanticModel().GetSymbolInfo(symRef))
                          .Distinct();

            symbols.Should().NotContainNulls();

            var foundReferences = symbols
                                  .SelectMany(s => compilation.GetSemanticModel().FindReferences(s !))
                                  .Where(refSyntax => !(refSyntax is IDeclarationSyntax));

            foundReferences.Should().BeEquivalentTo(symbolReferences);
        }
示例#18
0
        public void ShouldConvertExpressionsCorrectly(string text, string expected)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(string.Empty));

            var parsed = ParserHelper.ParseExpression(text);

            var converter = new ExpressionConverter(new EmitterContext(compilation.GetSemanticModel()));
            var converted = converter.ConvertExpression(parsed);

            var serializer = new ExpressionSerializer(new ExpressionSerializerSettings
            {
                IncludeOuterSquareBrackets = true, SingleStringHandling = ExpressionSerializerSingleStringHandling.SerializeAsString
            });

            var actual = serializer.SerializeExpression(converted);

            actual.Should().Be(expected);
        }
示例#19
0
        public async Task RenamingIdentifierAccessOrDeclarationShouldRenameDeclarationAndAllReferences(DataSet dataSet)
        {
            var uri = DocumentUri.From($"/{dataSet.Name}");

            using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri);

            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), 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 validVariableAccessPairs = symbolTable
                                           .Where(pair => (pair.Key is VariableAccessSyntax || pair.Key is IDeclarationSyntax) &&
                                                  pair.Value.Kind != SymbolKind.Error &&
                                                  pair.Value.Kind != SymbolKind.Function);

            const string expectedNewText = "NewIdentifier";

            foreach (var(syntax, symbol) in validVariableAccessPairs)
            {
                var edit = await client.RequestRename(new RenameParams
                {
                    NewName      = expectedNewText,
                    TextDocument = new TextDocumentIdentifier(uri),
                    Position     = PositionHelper.GetPosition(lineStarts, syntax.Span.Position)
                });

                edit.DocumentChanges.Should().BeNullOrEmpty();
                edit.Changes.Should().HaveCount(1);
                edit.Changes.Should().ContainKey(uri);

                var textEdits = edit.Changes[uri];
                textEdits.Should().NotBeEmpty();

                var expectedEdits = symbolToSyntaxLookup[symbol]
                                    .Select(node => CreateExpectedTextEdit(lineStarts, expectedNewText, node));

                textEdits.Should().BeEquivalentTo(expectedEdits);
            }
        }
示例#20
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.CreateRegistrar(), 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();
            }
        }
示例#21
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.CreateRegistrar(), 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();
            }
        }
示例#22
0
        public void ProgramsShouldProduceExpectedDiagnostics(DataSet dataSet)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));
            var model       = compilation.GetSemanticModel();

            string getLoggingString(Diagnostic diagnostic)
            {
                var spanText = OutputHelper.GetSpanText(dataSet.Bicep, diagnostic);

                return($"[{diagnostic.Code} ({diagnostic.Level})] {diagnostic.Message} |{spanText}|");
            }

            var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(dataSet, model.GetAllDiagnostics(), getLoggingString);
            var resultsFile         = FileHelper.SaveResultFile(this.TestContext !, Path.Combine(dataSet.Name, DataSet.TestFileMainDiagnostics), sourceTextWithDiags);

            sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput(
                dataSet.Diagnostics,
                expectedLocation: OutputHelper.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainDiagnostics),
                actualLocation: resultsFile);
        }
示例#23
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)
                {
示例#24
0
        public async Task FindReferencesWithoutDeclarationsShouldProduceCorrectResults(DataSet dataSet)
        {
            var uri = DocumentUri.From($"/{dataSet.Name}");

            using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri);

            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), 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);

            foreach (var(syntax, symbol) in symbolTable.Where(s => s.Value.Kind != SymbolKind.Error))
            {
                var locations = await client.RequestReferences(new ReferenceParams
                {
                    TextDocument = new TextDocumentIdentifier(uri),
                    Context      = new ReferenceContext
                    {
                        IncludeDeclaration = false
                    },
                    Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position)
                });

                // all URIs should be the same in the results
                locations.Select(r => r.Uri).Should().AllBeEquivalentTo(uri);

                // exclude declarations when calculating expected ranges
                var expectedRanges = symbolToSyntaxLookup[symbol]
                                     .Where(node => !(node is IDeclarationSyntax))
                                     .Select(node => PositionHelper.GetNameRange(lineStarts, node));

                // ranges should match what we got from our own symbol table
                locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges);
            }
        }
示例#25
0
        public async Task GoToDefinitionRequestOnValidSymbolReferenceShouldReturnLocationOfDeclaredSymbol(DataSet dataSet)
        {
            var uri = DocumentUri.From($"/{dataSet.Name}");

            using var client = await IntegrationTestHelper.StartServerWithText(dataSet.Bicep, uri);

            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));
            var symbolTable = compilation.ReconstructSymbolTable();
            var lineStarts  = TextCoordinateConverter.GetLineStarts(dataSet.Bicep);

            // filter out symbols that don't have locations
            var declaredSymbolBindings = symbolTable
                                         .Where(pair => pair.Value is DeclaredSymbol)
                                         .Select(pair => new KeyValuePair <SyntaxBase, DeclaredSymbol>(pair.Key, (DeclaredSymbol)pair.Value));

            foreach (var(syntax, symbol) in declaredSymbolBindings)
            {
                var response = await client.RequestDefinition(new DefinitionParams
                {
                    TextDocument = new TextDocumentIdentifier(uri),
                    Position     = PositionHelper.GetPosition(lineStarts, syntax.Span.Position)
                });

                var link = ValidateDefinitionResponse(response);

                // document should match the requested document
                link.TargetUri.Should().Be(uri);

                // target range should be the whole span of the symbol
                link.TargetRange.Should().Be(symbol.DeclaringSyntax.Span.ToRange(lineStarts));

                // selection range should be the span of the identifier of the symbol
                link.TargetSelectionRange.Should().Be(symbol.NameSyntax.Span.ToRange(lineStarts));

                // origin selection range should be the span of the syntax node that references the symbol
                link.OriginSelectionRange.Should().Be(syntax.ToRange(lineStarts));
            }
        }
示例#26
0
        public void ExampleIsValid(ExampleData example)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(example.BicepContents));
            var emitter     = new TemplateEmitter(compilation.GetSemanticModel());

            using var stream = new MemoryStream();
            var result = emitter.Emit(stream);

            result.Diagnostics.Should().BeEmpty();
            result.Status.Should().Be(EmitStatus.Succeeded);

            stream.Position = 0;
            var generated = new StreamReader(stream).ReadToEnd();

            var actual = JToken.Parse(generated);

            FileHelper.SaveResultFile(this.TestContext !, $"{example.BicepFileName}_Compiled_Actual.json", actual.ToString(Formatting.Indented));

            var expected = JToken.Parse(example.JsonContents !);

            FileHelper.SaveResultFile(this.TestContext !, $"{example.BicepFileName}_Compiled_Expected.json", expected.ToString(Formatting.Indented));

            JsonAssert.AreEqual(expected, actual, this.TestContext !, $"{example.BicepFileName}_Compiled_Delta.json");
        }
示例#27
0
        public void NormalUpsertAfterFatalException_ShouldReplaceDiagnostics()
        {
            PublishDiagnosticsParams?receivedParams = null;

            var document = CreateMockDocument(p => receivedParams = p);

            var server = CreateMockServer(document);

            var          provider        = Repository.Create <ICompilationProvider>();
            const string expectedMessage = "Internal bicep exception.";

            // start by failing
            bool failUpsert = true;

            provider
            .Setup(m => m.Create(It.IsAny <string>()))
            .Returns <string>(text => failUpsert ? throw new InvalidOperationException(expectedMessage) : new BicepCompilationProvider(TestResourceTypeProvider.CreateRegistrar()).Create(text));

            var manager = new BicepCompilationManager(server.Object, provider.Object);

            const long version = 74;
            var        uri     = DocumentUri.File(this.TestContext !.TestName);

            // upsert should fail because of the mock fatal exception
            manager.UpsertCompilation(uri, version, "fake").Should().BeNull();

            // diagnostics should have been published once
            document.Verify(m => m.SendNotification(It.IsAny <PublishDiagnosticsParams>()), Times.Once);

            receivedParams.Should().NotBeNull();
            receivedParams !.Uri.Should().Be(uri);
            receivedParams.Version.Should().Be(version);
            receivedParams.Diagnostics.Should().HaveCount(1);

            var fatalDiagnostic = receivedParams.Diagnostics.Single();

            fatalDiagnostic.Message.Should().Be(expectedMessage);
            fatalDiagnostic.Severity.Should().Be(DiagnosticSeverity.Error);

            // reset counts
            document.Invocations.Clear();

            // allow success
            failUpsert = false;

            // upsert should succeed because we allowed it
            var upserted = manager.UpsertCompilation(uri, version, "fake\nfake\nfake\n");

            upserted.Should().NotBeNull();

            // new diagnostics should have been published once
            document.Verify(m => m.SendNotification(It.IsAny <PublishDiagnosticsParams>()), Times.Once);

            receivedParams.Should().NotBeNull();
            receivedParams !.Uri.Should().Be(uri);
            receivedParams.Version.Should().Be(version);
            receivedParams.Diagnostics.Should().HaveCount(3);

            // none of the messages should be our fatal message
            receivedParams.Diagnostics
            .Select(diag => diag.Message)
            .All(message => string.Equals(message, expectedMessage) == false)
            .Should().BeTrue();
        }
        public void DeclarationSnippetsShouldBeValid()
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(string.Empty));

            compilation.GetSemanticModel().GetAllDiagnostics().Should().BeEmpty();

            var provider = new BicepCompletionProvider();

            var completions = provider.GetFilteredCompletions(compilation.GetSemanticModel(), new BicepCompletionContext(BicepCompletionContextKind.Declaration));

            var snippetCompletions = completions
                                     .Where(c => c.Kind == CompletionItemKind.Snippet)
                                     .OrderBy(c => c.Label)
                                     .ToList();

            snippetCompletions.Should().OnlyContain(c => c.Kind == CompletionItemKind.Snippet && c.InsertTextFormat == InsertTextFormat.Snippet);
            snippetCompletions.Should().OnlyContain(c => c.InsertTextFormat == InsertTextFormat.Snippet);
            snippetCompletions.Should().OnlyContain(c => LanguageConstants.DeclarationKeywords.Contains(c.Label));
            snippetCompletions.Should().OnlyContain(c => c.Documentation.HasMarkupContent && c.Documentation.MarkupContent.Kind == MarkupKind.Markdown);

            var snippetsByDetail = snippetCompletions.ToDictionary(c => c.Detail);

            var replacementsByDetail = new Dictionary <string, IList <string> >
            {
                ["Parameter declaration"] = new[] { string.Empty, "myParam", "string" },
                ["Parameter declaration with default value"] = new[] { string.Empty, "myParam", "string", "'myDefault'" },
                ["Parameter declaration with default and allowed values"] = new[] { string.Empty, "myParam", "string", "'myDefault'", "'val1'\n'val2'" },
                ["Parameter declaration with options"] = new[] { string.Empty, "myParam", "string", "default: 'myDefault'\nsecure: true" },
                ["Secure string parameter"]            = new[] { string.Empty, "myParam" },
                ["Variable declaration"]            = new[] { "'stringVal'", "myVariable" },
                ["Resource with defaults"]          = new[] { "prop1: 'val1'", "myResource", "myProvider", "myType", "2020-01-01", "'parent'", "'West US'" },
                ["Child Resource with defaults"]    = new[] { "prop1: 'val1'", "myResource", "myProvider", "myType", "myChildType", "2020-01-01", "'parent/child'" },
                ["Resource without defaults"]       = new[] { "properties: {\nprop1: 'val1'\n}", "myResource", "myProvider", "myType", "2020-01-01", "'parent'" },
                ["Child Resource without defaults"] = new[] { "properties: {\nprop1: 'val1'\n}", "myResource", "myProvider", "myType", "myChildType", "2020-01-01", "'parent/child'" },
                ["Output declaration"] = new[] { "'stringVal'", "myOutput", "string" }
            };

            snippetsByDetail.Keys.Should().BeEquivalentTo(replacementsByDetail.Keys);


            foreach (var(detail, completion) in snippetsByDetail)
            {
                // validate snippet
                var snippet = new Snippet(completion.InsertText);

                // if we don't have placeholders, why is it a snippet?
                snippet.Placeholders.Should().NotBeEmpty();

                // documentation should have the snippet without placeholders
                completion.Documentation.MarkupContent.Value.Should().Contain(snippet.FormatDocumentation());

                // perform the sample replacement
                var replacements = replacementsByDetail[detail];
                var replaced     = snippet.Format((s, placeholder) => placeholder.Index >= 0 && placeholder.Index < replacements.Count
                    ? replacements[placeholder.Index]
                    : string.Empty);

                var parser      = new Parser(replaced);
                var declaration = parser.Declaration();

                declaration.Should().BeAssignableTo <IDeclarationSyntax>($"because the snippet for '{detail}' failed to parse after replacements:\n{replaced}");
            }
        }
示例#29
0
        public void EndOfFileFollowingSpaceAfterParameterKeyWordShouldNotThrow()
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText("parameter "));

            compilation.GetSemanticModel().GetParseDiagnostics();
        }
示例#30
0
        public void CloseAfterUpsert_ShouldClearDiagnostics()
        {
            PublishDiagnosticsParams?receivedParams = null;

            var document = CreateMockDocument(p => receivedParams = p);

            var server = CreateMockServer(document);

            var manager = new BicepCompilationManager(server.Object, new BicepCompilationProvider(TestResourceTypeProvider.CreateRegistrar()));

            const long version = 42;
            var        uri     = DocumentUri.File(this.TestContext !.TestName);

            // first get should not return anything
            manager.GetCompilation(uri).Should().BeNull();

            // upsert the compilation
            manager.UpsertCompilation(uri, version, "hello");

            document.Verify(m => m.SendNotification(It.IsAny <PublishDiagnosticsParams>()), Times.Once);

            // there should have been 1 diagnostic
            receivedParams.Should().NotBeNull();
            receivedParams !.Uri.Should().Be(uri);
            receivedParams.Version.Should().Be(version);
            receivedParams.Diagnostics.Should().NotBeNullOrEmpty();
            receivedParams.Diagnostics.Count().Should().Be(1);

            // reset tracked calls
            document.Invocations.Clear();

            // get again
            var actual = manager.GetCompilation(uri);

            actual.Should().NotBeNull();

            // get should not have pushed diagnostics
            document.Verify(m => m.SendNotification(It.IsAny <PublishDiagnosticsParams>()), Times.Never);

            // 2nd get should be the same
            manager.GetCompilation(uri).Should().BeSameAs(actual);

            // get should not have pushed diagnostics
            document.Verify(m => m.SendNotification(It.IsAny <PublishDiagnosticsParams>()), Times.Never);

            // close compilation
            manager.CloseCompilation(uri);

            // close should have cleared diagnostics
            document.Verify(m => m.SendNotification(It.IsAny <PublishDiagnosticsParams>()), Times.Once);

            // expect zero diagnostics and 0 version
            receivedParams.Should().NotBeNull();
            receivedParams.Uri.Should().Be(uri);
            receivedParams.Version.Should().Be(0);
            receivedParams.Diagnostics.Should().BeEmpty();

            // reset call counts
            document.Invocations.Clear();

            // get again
            manager.GetCompilation(uri).Should().BeNull();

            // get should not have pushed diagnostics
            document.Verify(m => m.SendNotification(It.IsAny <PublishDiagnosticsParams>()), Times.Never);
        }