private static bool RewriteSyntax(IResourceTypeProvider resourceTypeProvider, Workspace workspace, Uri entryUri, Func <SemanticModel, SyntaxRewriteVisitor> rewriteVisitorBuilder)
        {
            var hasChanges         = false;
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), workspace, entryUri);
            var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

            foreach (var(fileUri, syntaxTree) in workspace.GetActiveSyntaxTrees())
            {
                var entryFile  = syntaxTreeGrouping.EntryPoint;
                var entryModel = compilation.GetEntrypointSemanticModel();

                var newProgramSyntax = rewriteVisitorBuilder(compilation.GetSemanticModel(syntaxTree)).Rewrite(syntaxTree.ProgramSyntax);

                if (!object.ReferenceEquals(syntaxTree.ProgramSyntax, newProgramSyntax))
                {
                    hasChanges = true;
                    var newSyntaxTree = new SyntaxTree(fileUri, ImmutableArray <int> .Empty, newProgramSyntax);
                    workspace.UpsertSyntaxTrees(newSyntaxTree.AsEnumerable());

                    syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), workspace, entryUri);
                    compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);
                }
            }

            return(hasChanges);
        }
Exemple #2
0
        public int Decompile(ILogger logger, DecompileArguments arguments)
        {
            logger.LogWarning(
                "WARNING: Decompilation is a best-effort process, as there is no guaranteed mapping from ARM JSON to Bicep.\n" +
                "You may need to fix warnings and errors in the generated bicep file(s), or decompilation may fail entirely if an accurate conversion is not possible.\n" +
                "If you would like to report any issues or inaccurate conversions, please see https://github.com/Azure/bicep/issues.");

            var diagnosticLogger = new BicepDiagnosticLogger(logger);
            var jsonPath         = PathHelper.ResolvePath(arguments.InputFile);

            try
            {
                var(bicepUri, filesToSave) = TemplateDecompiler.DecompileFileWithModules(resourceTypeProvider, new FileResolver(), PathHelper.FilePathToFileUrl(jsonPath));
                foreach (var(fileUri, bicepOutput) in filesToSave)
                {
                    File.WriteAllText(fileUri.LocalPath, bicepOutput);
                }

                var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), bicepUri);
                var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

                return(LogDiagnosticsAndCheckSuccess(diagnosticLogger, compilation) ? 0 : 1);
            }
            catch (Exception exception)
            {
                this.errorWriter.WriteLine($"{jsonPath}: Decompilation failed with fatal error \"{exception.Message}\"");
                return(1);
            }
        }
Exemple #3
0
        private int DecompileToStdout(IDiagnosticLogger logger, string jsonPath)
        {
            var tempOutputPath = Path.ChangeExtension(Path.GetTempFileName(), "bicep");

            try
            {
                var(_, filesToSave) = TemplateDecompiler.DecompileFileWithModules(resourceTypeProvider, new FileResolver(), PathHelper.FilePathToFileUrl(jsonPath));
                foreach (var(_, bicepOutput) in filesToSave)
                {
                    this.outputWriter.Write(bicepOutput);
                    File.WriteAllText(tempOutputPath, bicepOutput);
                }

                var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(tempOutputPath));
                var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

                return(LogDiagnosticsAndCheckSuccess(logger, compilation) ? 0 : 1);
            }
            catch (Exception exception)
            {
                this.errorWriter.WriteLine($"{jsonPath}: Decompilation failed with fatal error \"{exception.Message}\"");
                return(1);
            } finally
            {
                if (File.Exists(tempOutputPath))
                {
                    File.Delete(tempOutputPath);
                }
            }
        }
Exemple #4
0
        public static Compilation CopyFilesAndCreateCompilation(this DataSet dataSet, TestContext testContext, out string outputDirectory)
        {
            outputDirectory = dataSet.SaveFilesToTestDirectory(testContext, dataSet.Name);
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), Path.Combine(outputDirectory, DataSet.TestFileMain));

            return(new Compilation(TestResourceTypeProvider.Create(), syntaxTreeGrouping));
        }
Exemple #5
0
        public CompilationContext Create(IReadOnlyWorkspace workspace, DocumentUri documentUri)
        {
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(fileResolver, workspace, documentUri.ToUri());
            var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

            return(new CompilationContext(compilation));
        }
Exemple #6
0
        public void ValidBicepTextWriter_TemplateEmiterTemplateHashCheck(DataSet dataSet)
        {
            var          outputDirectory = dataSet.SaveFilesToTestDirectory(TestContext, dataSet.Name);
            var          bicepFilePath   = Path.Combine(outputDirectory, DataSet.TestFileMain);
            MemoryStream memoryStream    = new MemoryStream();

            // emitting the template should be successful
            var result = this.EmitTemplate(SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath)), memoryStream, ThisAssembly.AssemblyFileVersion);

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

            var actual   = JToken.ReadFrom(new JsonTextReader(new StreamReader(new MemoryStream(memoryStream.ToArray()))));
            var compiled = JToken.Parse(dataSet.Compiled !);

            // TemplateHash should not be the same with difference assembly versions
            actual.SelectToken("metadata._generator.templateHash") !.ToString().Should().NotBe(
                compiled.SelectToken("metadata._generator.templateHash") !.ToString()
                );
            actual.SelectToken("metadata._generator.version") !.ToString().Should().Be(ThisAssembly.AssemblyFileVersion);

            // Aside from the different metadata, the templates should be the same
            ((JObject)actual).Remove("metadata");
            ((JObject)compiled).Remove("metadata");

            var compiledFilePath = FileHelper.SaveResultFile(this.TestContext, Path.Combine(dataSet.Name, DataSet.TestFileMainCompiled), actual.ToString(Formatting.Indented));

            actual.Should().EqualWithJsonDiffOutput(
                TestContext,
                compiled,
                expectedLocation: DataSet.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainCompiled),
                actualLocation: compiledFilePath);
        }
Exemple #7
0
        private void BuildManyFilesToStdOut(IDiagnosticLogger logger, string[] bicepPaths)
        {
            using var writer = new JsonTextWriter(this.outputWriter)
                  {
                      Formatting = Formatting.Indented
                  };

            if (bicepPaths.Length > 1)
            {
                writer.WriteStartArray();
            }
            foreach (var bicepPath in bicepPaths)
            {
                var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepPath));
                var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

                var success = LogDiagnosticsAndCheckSuccess(logger, compilation);
                if (success)
                {
                    var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel());

                    emitter.Emit(writer);
                }
            }
            if (bicepPaths.Length > 1)
            {
                writer.WriteEndArray();
            }
        }
Exemple #8
0
        public static Compilation CopyFilesAndCreateCompilation(this DataSet dataSet, TestContext testContext, out string outputDirectory)
        {
            outputDirectory = dataSet.SaveFilesToTestDirectory(testContext, dataSet.Name);
            var fileUri            = PathHelper.FilePathToFileUrl(Path.Combine(outputDirectory, DataSet.TestFileMain));
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), fileUri);

            return(new Compilation(new AzResourceTypeProvider(TypeLoader), syntaxTreeGrouping));
        }
Exemple #9
0
        public CompilationContext Create(DocumentUri documentUri, string text)
        {
            var mainFileName       = documentUri.GetFileSystemPath();
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.BuildWithPreloadedFile(fileResolver, mainFileName, text);
            var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

            return(new CompilationContext(compilation));
        }
Exemple #10
0
        public static Compilation CopyFilesAndCreateCompilation(this DataSet dataSet, TestContext testContext, out string outputDirectory, out Uri fileUri)
        {
            outputDirectory = dataSet.SaveFilesToTestDirectory(testContext);
            fileUri         = PathHelper.FilePathToFileUrl(Path.Combine(outputDirectory, DataSet.TestFileMain));
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(BicepTestConstants.FileResolver, new Workspace(), fileUri);

            return(new Compilation(AzResourceTypeProvider.CreateWithAzTypes(), syntaxTreeGrouping));
        }
Exemple #11
0
        public static SyntaxTreeGrouping CreateForFiles(IReadOnlyDictionary <Uri, string> files, Uri entryFileUri)
        {
            var workspace   = new Workspace();
            var syntaxTrees = files.Select(kvp => SyntaxTree.Create(kvp.Key, kvp.Value));

            workspace.UpsertSyntaxTrees(syntaxTrees);

            return(SyntaxTreeGroupingBuilder.Build(new FileResolver(), workspace, entryFileUri));
        }
Exemple #12
0
        private static Compilation GetCompilation(string text)
        {
            var fileName     = "/main.bicep";
            var fileResolver = new InMemoryFileResolver(new Dictionary <string, string> {
                [fileName] = text
            }, filePath => "Modules are not supported in the Bicep Playground");
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(fileResolver, fileName);

            return(new Compilation(resourceTypeProvider, syntaxTreeGrouping));
        }
        public void InvalidBicep_TemplateEmiterShouldNotProduceAnyTemplate(DataSet dataSet)
        {
            var    outputDirectory = dataSet.SaveFilesToTestDirectory(TestContext, dataSet.Name);
            var    bicepFilePath   = Path.Combine(outputDirectory, DataSet.TestFileMain);
            string filePath        = FileHelper.GetResultFilePath(this.TestContext, $"{dataSet.Name}_Compiled_Original.json");

            // emitting the template should fail
            var result = this.EmitTemplate(SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath)), filePath);

            result.Status.Should().Be(EmitStatus.Failed);
            result.Diagnostics.Should().NotBeEmpty();
        }
Exemple #14
0
        private static Compilation GetCompilation(string fileContents)
        {
            var fileUri    = new Uri("inmemory:///main.bicep");
            var workspace  = new Workspace();
            var syntaxTree = SyntaxTree.Create(fileUri, fileContents);

            workspace.UpsertSyntaxTrees(syntaxTree.AsEnumerable());

            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), workspace, fileUri);

            return(new Compilation(resourceTypeProvider, syntaxTreeGrouping));
        }
Exemple #15
0
        public Compilation Compile(string inputPath)
        {
            var inputUri = PathHelper.FilePathToFileUrl(inputPath);

            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(this.fileResolver, this.workspace, inputUri);

            var compilation = new Compilation(this.invocationContext.ResourceTypeProvider, syntaxTreeGrouping);

            LogDiagnostics(compilation);

            return(compilation);
        }
Exemple #16
0
        private void BuildToFile(IDiagnosticLogger logger, string bicepPath, string outputPath)
        {
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepPath));
            var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

            var success = LogDiagnosticsAndCheckSuccess(logger, compilation);

            if (success)
            {
                var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), this.assemblyFileVersion);

                using var outputStream = CreateFileStream(outputPath);
                emitter.Emit(outputStream);
            }
        }
Exemple #17
0
        public void ExampleIsValid(ExampleData example)
        {
            // save all the files in the containing directory to disk so that we can test module resolution
            var parentStream    = Path.GetDirectoryName(example.BicepStreamName) !.Replace('\\', '/');
            var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix(TestContext, typeof(ExamplesTests).Assembly, example.OutputFolderName, parentStream);
            var bicepFileName   = Path.Combine(outputDirectory, Path.GetFileName(example.BicepStreamName));
            var jsonFileName    = Path.Combine(outputDirectory, Path.GetFileName(example.JsonStreamName));

            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFileName));
            var compilation        = new Compilation(new AzResourceTypeProvider(), syntaxTreeGrouping);
            var emitter            = new TemplateEmitter(compilation.GetEntrypointSemanticModel());

            // group assertion failures using AssertionScope, rather than reporting the first failure
            using (new AssertionScope())
            {
                foreach (var(syntaxTree, diagnostics) in compilation.GetAllDiagnosticsBySyntaxTree())
                {
                    var nonPermittedDiagnostics = diagnostics.Where(x => !IsPermittedMissingTypeDiagnostic(x));

                    nonPermittedDiagnostics.Should().BeEmpty($"\"{syntaxTree.FileUri.LocalPath}\" should not have warnings or errors");
                }

                var exampleExists = File.Exists(jsonFileName);
                exampleExists.Should().BeTrue($"Generated example \"{jsonFileName}\" should be checked in");

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

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

                if (result.Status == EmitStatus.Succeeded)
                {
                    stream.Position = 0;
                    var generated = new StreamReader(stream).ReadToEnd();

                    var actual = JToken.Parse(generated);
                    File.WriteAllText(jsonFileName + ".actual", generated);

                    actual.Should().EqualWithJsonDiffOutput(
                        TestContext,
                        exampleExists ? JToken.Parse(File.ReadAllText(jsonFileName)) : new JObject(),
                        example.JsonStreamName,
                        jsonFileName + ".actual");
                }
            }
        }
Exemple #18
0
        private static IEnumerable <string> GetAllDiagnostics(string bicepFilePath)
        {
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath));
            var compilation        = new Compilation(TestTypeHelper.CreateEmptyProvider(), syntaxTreeGrouping);

            var output = new List <string>();

            foreach (var(syntaxTree, diagnostics) in compilation.GetAllDiagnosticsBySyntaxTree())
            {
                foreach (var diagnostic in diagnostics)
                {
                    var(line, character) = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, diagnostic.Span.Position);
                    output.Add($"{syntaxTree.FileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}");
                }
            }

            return(output);
        }
        public void ValidBicep_TemplateEmiterShouldProduceExpectedTemplate(DataSet dataSet)
        {
            var outputDirectory  = dataSet.SaveFilesToTestDirectory(TestContext, dataSet.Name);
            var bicepFilePath    = Path.Combine(outputDirectory, DataSet.TestFileMain);
            var compiledFilePath = FileHelper.GetResultFilePath(this.TestContext, Path.Combine(dataSet.Name, DataSet.TestFileMainCompiled));

            // emitting the template should be successful
            var result = this.EmitTemplate(SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath)), compiledFilePath);

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

            var actual = JToken.Parse(File.ReadAllText(compiledFilePath));

            actual.Should().EqualWithJsonDiffOutput(
                JToken.Parse(dataSet.Compiled !),
                expectedLocation: OutputHelper.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainCompiled),
                actualLocation: compiledFilePath);
        }
Exemple #20
0
        private void BuildToStdout(IDiagnosticLogger logger, string bicepPath)
        {
            using var writer = new JsonTextWriter(this.outputWriter)
                  {
                      Formatting = Formatting.Indented
                  };

            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepPath));
            var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

            var success = LogDiagnosticsAndCheckSuccess(logger, compilation);

            if (success)
            {
                var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), this.assemblyFileVersion);

                emitter.Emit(writer);
            }
        }
Exemple #21
0
        public void Decompiler_generates_expected_bicep_files_with_diagnostics(ExampleData example)
        {
            // save all the files in the containing directory to disk so that we can test module resolution
            var parentStream    = Path.GetDirectoryName(example.BicepStreamName) !.Replace('\\', '/');
            var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix(TestContext, typeof(DecompilationTests).Assembly, parentStream);
            var bicepFileName   = Path.Combine(outputDirectory, Path.GetFileName(example.BicepStreamName));
            var jsonFileName    = Path.Combine(outputDirectory, Path.GetFileName(example.JsonStreamName));
            var typeProvider    = AzResourceTypeProvider.CreateWithAzTypes();

            var(bicepUri, filesToSave) = TemplateDecompiler.DecompileFileWithModules(typeProvider, new FileResolver(), PathHelper.FilePathToFileUrl(jsonFileName));

            var syntaxTrees = filesToSave.Select(kvp => SyntaxTree.Create(kvp.Key, kvp.Value));
            var workspace   = new Workspace();

            workspace.UpsertSyntaxTrees(syntaxTrees);

            var syntaxTreeGrouping      = SyntaxTreeGroupingBuilder.Build(new FileResolver(), workspace, bicepUri);
            var compilation             = new Compilation(typeProvider, syntaxTreeGrouping);
            var diagnosticsBySyntaxTree = compilation.GetAllDiagnosticsBySyntaxTree();

            using (new AssertionScope())
            {
                foreach (var syntaxTree in syntaxTreeGrouping.SyntaxTrees)
                {
                    var exampleExists = File.Exists(syntaxTree.FileUri.LocalPath);
                    exampleExists.Should().BeTrue($"Generated example \"{syntaxTree.FileUri.LocalPath}\" should be checked in");

                    var diagnostics = diagnosticsBySyntaxTree[syntaxTree];
                    var bicepOutput = filesToSave[syntaxTree.FileUri];

                    var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(bicepOutput, Environment.NewLine, diagnostics, diag => OutputHelper.GetDiagLoggingString(bicepOutput, outputDirectory, diag));
                    File.WriteAllText(syntaxTree.FileUri.LocalPath + ".actual", sourceTextWithDiags);

                    sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput(
                        TestContext,
                        exampleExists ? File.ReadAllText(syntaxTree.FileUri.LocalPath) : "",
                        expectedLocation: Path.Combine("src", "Bicep.Decompiler.IntegrationTests", parentStream, Path.GetRelativePath(outputDirectory, syntaxTree.FileUri.LocalPath)),
                        actualLocation: syntaxTree.FileUri.LocalPath + ".actual");
                }
            }
        }
Exemple #22
0
        private bool DecompileSingleFile(IDiagnosticLogger logger, string filePath)
        {
            try
            {
                var(bicepUri, filesToSave) = TemplateDecompiler.DecompileFileWithModules(resourceTypeProvider, new FileResolver(), PathHelper.FilePathToFileUrl(filePath));
                foreach (var(fileUri, bicepOutput) in filesToSave)
                {
                    File.WriteAllText(fileUri.LocalPath, bicepOutput);
                }

                var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), bicepUri);
                var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

                return(LogDiagnosticsAndCheckSuccess(logger, compilation));
            }
            catch (Exception exception)
            {
                this.errorWriter.WriteLine($"{filePath}: Decompilation failed with fatal error \"{exception.Message}\"");
                return(false);
            }
        }
Exemple #23
0
        private int DecompileToFile(IDiagnosticLogger logger, string jsonPath, string outputPath)
        {
            try
            {
                var(_, filesToSave) = TemplateDecompiler.DecompileFileWithModules(resourceTypeProvider, new FileResolver(), PathHelper.FilePathToFileUrl(jsonPath));
                foreach (var(_, bicepOutput) in filesToSave)
                {
                    File.WriteAllText(outputPath, bicepOutput);
                }

                var outputPathToCheck  = Path.GetFullPath(outputPath);
                var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(outputPathToCheck));
                var compilation        = new Compilation(resourceTypeProvider, syntaxTreeGrouping);

                return(LogDiagnosticsAndCheckSuccess(logger, compilation) ? 0 : 1);
            }
            catch (Exception exception)
            {
                this.errorWriter.WriteLine($"{jsonPath}: Decompilation failed with fatal error \"{exception.Message}\"");
                return(1);
            }
        }
        public void ValidBicepTextWriter_TemplateEmiterShouldProduceExpectedTemplate(DataSet dataSet)
        {
            var          outputDirectory = dataSet.SaveFilesToTestDirectory(TestContext, dataSet.Name);
            var          bicepFilePath   = Path.Combine(outputDirectory, DataSet.TestFileMain);
            MemoryStream memoryStream    = new MemoryStream();

            // emitting the template should be successful
            var result = this.EmitTemplate(SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath)), memoryStream);

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

            // normalizing the formatting in case there are differences in indentation
            // this way the diff between actual and expected will be clean
            var actual           = JToken.ReadFrom(new JsonTextReader(new StreamReader(new MemoryStream(memoryStream.ToArray()))));
            var compiledFilePath = FileHelper.SaveResultFile(this.TestContext, Path.Combine(dataSet.Name, DataSet.TestFileMainCompiled), actual.ToString(Formatting.Indented));

            actual.Should().EqualWithJsonDiffOutput(
                JToken.Parse(dataSet.Compiled !),
                expectedLocation: OutputHelper.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainCompiled),
                actualLocation: compiledFilePath);
        }
Exemple #25
0
        public async Task ValidateSnippetCompletionAfterPlaceholderReplacements(CompletionData completionData)
        {
            string pathPrefix = $"Completions/SnippetTemplates/{completionData.Prefix}";

            var outputDirectory = FileHelper.SaveEmbeddedResourcesWithPathPrefix(TestContext, typeof(CompletionTests).Assembly, pathPrefix);

            var bicepFileName       = Path.Combine(outputDirectory, "main.bicep");
            var bicepSourceFileName = Path.Combine("src", "Bicep.LangServer.IntegrationTests", pathPrefix, Path.GetRelativePath(outputDirectory, bicepFileName));

            File.Exists(bicepFileName).Should().BeTrue($"Snippet placeholder file \"{bicepSourceFileName}\" should be checked in");
            var bicepContents = await File.ReadAllTextAsync(bicepFileName);

            // Request the expected completion from the server, and ensure it is unique + valid
            var completionText = await RequestSnippetCompletion(bicepFileName, completionData, bicepContents);

            // Replace all the placeholders with values from the placeholder file
            var replacementContents = SnippetCompletionTestHelper.GetSnippetTextAfterPlaceholderReplacements(completionText, bicepContents);

            using (new AssertionScope())
            {
                var combinedFileName       = Path.Combine(outputDirectory, "main.combined.bicep");
                var combinedSourceFileName = Path.Combine("src", "Bicep.LangServer.IntegrationTests", pathPrefix, Path.GetRelativePath(outputDirectory, combinedFileName));
                File.Exists(combinedFileName).Should().BeTrue($"Combined snippet file \"{combinedSourceFileName}\" should be checked in");

                var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(combinedFileName));
                var compilation        = new Compilation(TypeProvider, syntaxTreeGrouping);
                var diagnostics        = compilation.GetEntrypointSemanticModel().GetAllDiagnostics();

                var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(replacementContents, Environment.NewLine, diagnostics, diag => OutputHelper.GetDiagLoggingString(replacementContents, outputDirectory, diag));
                File.WriteAllText(combinedFileName + ".actual", sourceTextWithDiags);

                sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput(
                    TestContext,
                    File.Exists(combinedFileName) ? (await File.ReadAllTextAsync(combinedFileName)) : string.Empty,
                    expectedLocation: combinedSourceFileName,
                    actualLocation: combinedFileName + ".actual");
            }
        }
Exemple #26
0
        private static Uri?GetFileUriWithDiagnostics(IBinder binder, IFileResolver fileResolver, IDiagnosticWriter diagnostics, string filePath, SyntaxBase filePathArgument)
        {
            if (!SyntaxTreeGroupingBuilder.ValidateFilePath(filePath, out var validateFilePathFailureBuilder))
            {
                diagnostics.Write(validateFilePathFailureBuilder.Invoke(DiagnosticBuilder.ForPosition(filePathArgument)));
                return(null);
            }

            var fileUri = fileResolver.TryResolveFilePath(binder.FileSymbol.FileUri, filePath);

            if (fileUri is null)
            {
                diagnostics.Write(DiagnosticBuilder.ForPosition(filePathArgument).FilePathCouldNotBeResolved(filePath, binder.FileSymbol.FileUri.LocalPath));
                return(null);
            }

            if (!fileUri.IsFile)
            {
                diagnostics.Write(DiagnosticBuilder.ForPosition(filePathArgument).UnableToLoadNonFileUri(fileUri));
                return(null);
            }
            return(fileUri);
        }
Exemple #27
0
        public static SyntaxTreeGrouping CreateForFiles(IReadOnlyDictionary <string, string> files, string entryFileName)
        {
            var fileResolver = new InMemoryFileResolver(files);

            return(SyntaxTreeGroupingBuilder.Build(fileResolver, entryFileName));
        }
Exemple #28
0
        public static (Uri entrypointUri, ImmutableDictionary <Uri, string> filesToSave) DecompileFileWithModules(IResourceTypeProvider resourceTypeProvider, IFileResolver fileResolver, Uri jsonUri)
        {
            var decompileQueue = new Queue <Uri>();

            var entryUri  = ChangeExtension(jsonUri, "bicep");
            var workspace = new Workspace();

            decompileQueue.Enqueue(entryUri);

            while (decompileQueue.Any())
            {
                var bicepUri = decompileQueue.Dequeue();
                if (!bicepUri.AbsolutePath.EndsWith(".bicep", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                var currentJsonUri = ChangeExtension(bicepUri, "json");

                if (workspace.TryGetSyntaxTree(bicepUri, out _))
                {
                    continue;
                }

                if (!fileResolver.TryRead(currentJsonUri, out var jsonInput, out _))
                {
                    throw new InvalidOperationException($"Failed to read {currentJsonUri}");
                }

                var program    = TemplateConverter.DecompileTemplate(workspace, fileResolver, currentJsonUri, jsonInput);
                var syntaxTree = new SyntaxTree(bicepUri, ImmutableArray <int> .Empty, program);
                workspace.UpsertSyntaxTrees(syntaxTree.AsEnumerable());

                foreach (var module in program.Children.OfType <ModuleDeclarationSyntax>())
                {
                    var moduleRelativePath = SyntaxHelper.TryGetModulePath(module, out _);
                    if (moduleRelativePath == null ||
                        !SyntaxTreeGroupingBuilder.ValidateModulePath(moduleRelativePath, out _) ||
                        !Uri.TryCreate(bicepUri, moduleRelativePath, out var moduleUri))
                    {
                        // Do our best, but keep going if we fail to resolve a module file
                        continue;
                    }

                    if (!workspace.TryGetSyntaxTree(moduleUri, out _))
                    {
                        decompileQueue.Enqueue(moduleUri);
                    }
                }
            }

            RewriteSyntax(resourceTypeProvider, workspace, entryUri, semanticModel => new ParentChildResourceNameRewriter(semanticModel));
            RewriteSyntax(resourceTypeProvider, workspace, entryUri, semanticModel => new DependsOnRemovalRewriter(semanticModel));
            RewriteSyntax(resourceTypeProvider, workspace, entryUri, semanticModel => new ForExpressionSimplifierRewriter(semanticModel));
            for (var i = 0; i < 5; i++)
            {
                // This is a little weird. If there are casing issues nested inside casing issues (e.g. in an object), then the inner casing issue will have no type information
                // available, as the compilation will not have associated a type with it (since there was no match on the outer object). So we need to correct the outer issue first,
                // and then move to the inner one. We need to recompute the entire compilation to do this. It feels simpler to just do this in passes over the file, rather than on demand.
                if (!RewriteSyntax(resourceTypeProvider, workspace, entryUri, semanticModel => new TypeCasingFixerRewriter(semanticModel)))
                {
                    break;
                }
            }

            return(entryUri, PrintFiles(workspace));
        }