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); }
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)); }
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(); } }
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)); }
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); }
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); } }
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); } } }
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)); }
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)); }
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)); }
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)); }
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)); }
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); }
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(); }
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); } }
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"); } } }
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); }
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); } }
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); }
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"); } } }
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); } }
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); }
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 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"); } }
public static SyntaxTreeGrouping CreateForFiles(IReadOnlyDictionary <string, string> files, string entryFileName) { var fileResolver = new InMemoryFileResolver(files); return(SyntaxTreeGroupingBuilder.Build(fileResolver, entryFileName)); }