Esempio n. 1
0
        public static async Task <SyntaxTree> GenerateAsync(string source)
        {
            var document = CreateProject(source).Documents.Single();
            var tree     = await document.GetSyntaxTreeAsync();

            if (tree is null)
            {
                throw new InvalidOperationException("Could not get the syntax tree of the sources");
            }

            var compilation = (CSharpCompilation?)await document.Project.GetCompilationAsync();

            if (compilation is null)
            {
                throw new InvalidOperationException("Could not compile the sources");
            }

            var diagnostics = compilation.GetDiagnostics();

            Assert.Empty(diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning));
            var progress = new Progress <Diagnostic>();
            var result   = await DocumentTransform.TransformAsync(compilation, tree, null, Assembly.Load, progress, CancellationToken.None);

            return(result);
        }
        protected async Task <GenerationResult> GenerateAsync(SourceText inputSource)
        {
            var solution             = this.solution.WithDocumentText(this.inputDocumentId, inputSource);
            var inputDocument        = solution.GetDocument(this.inputDocumentId);
            var generatorDiagnostics = new List <Diagnostic>();
            var progress             = new SynchronousProgress <Diagnostic>(generatorDiagnostics.Add);
            var inputCompilation     = (CSharpCompilation)await inputDocument.Project.GetCompilationAsync();

            var inputSyntaxTree = await inputDocument.GetSyntaxTreeAsync();

            var outputSyntaxTree = await DocumentTransform.TransformAsync(inputCompilation, inputSyntaxTree, null, Assembly.Load, progress);

            var outputDocument = inputDocument.Project
                                 .AddDocument("output.cs", outputSyntaxTree.GetRoot());

            // Make sure the result compiles without errors or warnings.
            var compilation = await outputDocument.Project.GetCompilationAsync();

            var compilationDiagnostics = compilation.GetDiagnostics();

            SourceText outputDocumentText = await outputDocument.GetTextAsync();

            this.logger.WriteLine("{0}", outputDocumentText);

            // Verify all line endings are consistent (otherwise VS can bug the heck out of the user if they have the generated file open).
            string firstLineEnding = null;

            foreach (var line in outputDocumentText.Lines)
            {
                string actualNewLine = line.Text.GetSubText(TextSpan.FromBounds(line.End, line.EndIncludingLineBreak)).ToString();
                if (firstLineEnding == null)
                {
                    firstLineEnding = actualNewLine;
                }
                else if (actualNewLine != firstLineEnding && actualNewLine.Length > 0)
                {
                    string expected = EscapeLineEndingCharacters(firstLineEnding);
                    string actual   = EscapeLineEndingCharacters(actualNewLine);
                    Assert.True(false, $"Expected line ending characters '{expected}' but found '{actual}' on line {line.LineNumber + 1}.\nContent: {line}");
                }
            }

            var semanticModel = await outputDocument.GetSemanticModelAsync();

            var result = new GenerationResult(outputDocument, semanticModel, generatorDiagnostics, compilationDiagnostics);

            foreach (var diagnostic in generatorDiagnostics)
            {
                this.logger.WriteLine(diagnostic.ToString());
            }

            foreach (var diagnostic in result.CompilationDiagnostics)
            {
                this.logger.WriteLine(diagnostic.ToString());
            }

            return(result);
        }
        protected async Task <GenerationResult> GenerateAsync(SourceText inputSource)
        {
            var solution       = this.solution.WithDocumentText(this.inputDocumentId, inputSource);
            var inputDocument  = solution.GetDocument(this.inputDocumentId);
            var outputDocument = await DocumentTransform.TransformAsync(inputDocument, new MockProgress());

            // Make sure there are no compile errors.
            var compilation = await outputDocument.Project.GetCompilationAsync();

            var diagnostics = compilation.GetDiagnostics();
            var errors      = from diagnostic in diagnostics
                              where diagnostic.Severity >= DiagnosticSeverity.Error
                              select diagnostic;
            var warnings = from diagnostic in diagnostics
                           where diagnostic.Severity >= DiagnosticSeverity.Warning
                           select diagnostic;

            SourceText outputDocumentText = await outputDocument.GetTextAsync();

            this.logger.WriteLine("{0}", outputDocumentText);

            foreach (var error in errors)
            {
                this.logger.WriteLine("{0}", error);
            }

            foreach (var warning in warnings)
            {
                this.logger.WriteLine("{0}", warning);
            }

            Assert.Empty(errors);
            Assert.Empty(warnings);

            // Verify all line endings are consistent (otherwise VS can bug the heck out of the user if they have the generated file open).
            string firstLineEnding = null;

            foreach (var line in outputDocumentText.Lines)
            {
                string actualNewLine = line.Text.GetSubText(TextSpan.FromBounds(line.End, line.EndIncludingLineBreak)).ToString();
                if (firstLineEnding == null)
                {
                    firstLineEnding = actualNewLine;
                }
                else if (actualNewLine != firstLineEnding && actualNewLine.Length > 0)
                {
                    string expected = EscapeLineEndingCharacters(firstLineEnding);
                    string actual   = EscapeLineEndingCharacters(actualNewLine);
                    Assert.True(false, $"Expected line ending characters '{expected}' but found '{actual}' on line {line.LineNumber + 1}.\nContent: {line}");
                }
            }

            var semanticModel = await outputDocument.GetSemanticModelAsync();

            return(new GenerationResult(outputDocument, semanticModel));
        }
Esempio n. 4
0
    protected static SyntaxTree Generate(string source)
    {
        var document    = CreateProject(source).Documents.Single();
        var tree        = document.GetSyntaxTreeAsync().GetAwaiter().GetResult();
        var compilation = (CSharpCompilation)document.Project.GetCompilationAsync().GetAwaiter().GetResult();
        var diagnostics = compilation.GetDiagnostics();

        Assert.Empty(diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning));
        var progress = new Progress <Diagnostic>();
        var result   = DocumentTransform.TransformAsync(compilation, tree, null, Assembly.Load, Enumerable.Empty <IFreeCodeGenerator>(), progress).GetAwaiter().GetResult();

        return(result);
    }
Esempio n. 5
0
    protected static async Task <TransformResult> GenerateAsync(string source)
    {
        var document = CreateProject(source).Documents.Single();
        var tree     = await document.GetSyntaxTreeAsync();

        var compilation = (CSharpCompilation)(await document.Project.GetCompilationAsync());
        var diagnostics = compilation.GetDiagnostics();

        Assert.Empty(diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning));
        var progress = new Progress <Diagnostic>();
        var result   = await DocumentTransform.TransformAsync(compilation, tree, null, null, Assembly.Load, progress, CancellationToken.None);

        return(result);
    }
        public int Generate(string inputFilePath, string inputFileContents, string defaultNamespace, IntPtr[] outputFileContents, out uint outputLength, IVsGeneratorProgress generatorProgress)
        {
            if (outputFileContents != null && outputFileContents.Length > 0)
            {
                // Do this first, before input validation, so that the
                // catch block doesn't try to free memory with an uninitialized pointer.
                outputFileContents[0] = IntPtr.Zero;
            }

            try
            {
                Requires.NotNullOrEmpty(inputFilePath, "inputFilePath");
                Requires.NotNull(outputFileContents, "outputFileContents");
                Requires.Argument(outputFileContents.Length > 0, "outputFileContents", "Non-empty array expected.");

                string generated = null;
                ThreadHelper.JoinableTaskFactory.Run(async delegate
                {
                    VisualStudioWorkspace workspace = GetRoslynWorkspace();
                    var inputDocumentId             = workspace.CurrentSolution.GetDocumentIdsWithFilePath(inputFilePath).First();
                    var inputDocument  = workspace.CurrentSolution.GetDocument(inputDocumentId);
                    var outputDocument = await DocumentTransform.TransformAsync(inputDocument, new ProgressShim(generatorProgress));

                    // Now render as a complete string, as necessary by our single file generator.
                    var reducedDocumentText = await outputDocument.GetTextAsync();
                    generated = reducedDocumentText.ToString();
                });

                // Translate the string we've built up into the bytes of COM memory required.
                var    encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true);
                byte[] bytes    = encoding.GetBytes(generated);
                outputLength          = (uint)bytes.Length;
                outputFileContents[0] = Marshal.AllocCoTaskMem(bytes.Length);
                Marshal.Copy(bytes, 0, outputFileContents[0], bytes.Length);

                return(VSConstants.S_OK);
            }
            catch (Exception ex)
            {
                if (outputFileContents[0] != IntPtr.Zero)
                {
                    Marshal.FreeCoTaskMem(outputFileContents[0]);
                    outputFileContents[0] = IntPtr.Zero;
                }

                outputLength = 0;
                return(Marshal.GetHRForException(ex));
            }
        }
        protected async Task <GenerationResult> GenerateAsync(SourceText inputSource)
        {
            var solution       = this.solution.WithDocumentText(this.inputDocumentId, inputSource);
            var inputDocument  = solution.GetDocument(this.inputDocumentId);
            var outputDocument = await DocumentTransform.TransformAsync(inputDocument, new MockProgress());

            // Make sure there are no compile errors.
            var compilation = await outputDocument.Project.GetCompilationAsync();

            var diagnostics = compilation.GetDiagnostics();
            var errors      = from diagnostic in diagnostics
                              where diagnostic.Severity >= DiagnosticSeverity.Error
                              select diagnostic;
            var warnings = from diagnostic in diagnostics
                           where diagnostic.Severity >= DiagnosticSeverity.Warning
                           select diagnostic;

            SourceText outputDocumentText = await outputDocument.GetTextAsync();

            Console.WriteLine(outputDocumentText);

            foreach (var error in errors)
            {
                Console.WriteLine(error);
            }

            foreach (var warning in warnings)
            {
                Console.WriteLine(warning);
            }

            Assert.Empty(errors);
            Assert.Empty(warnings);

            // Verify all line endings are consistent (otherwise VS can bug the heck out of the user if they have the generated file open).
            foreach (var line in outputDocumentText.Lines)
            {
                string actualNewLine = line.Text.GetSubText(TextSpan.FromBounds(line.End, line.EndIncludingLineBreak)).ToString();
                if (actualNewLine != Environment.NewLine && actualNewLine.Length > 0)
                {
                    Assert.True(false, string.Format("Line {0} has unexpected line ending characters. Content: {1}", line.LineNumber, line));
                }
            }

            var semanticModel = await outputDocument.GetSemanticModelAsync();

            return(new GenerationResult(outputDocument, semanticModel));
        }
Esempio n. 8
0
        private DocumentTransform Transform(string filePath)
        {
            var result = new DocumentTransform();

            try {
                var document = _wordApplication.Open(filePath);
                result.Content = document.GetText();
                document.SaveAs(TempFilePath);
                var disposable = document as IDisposable;
                if (disposable != null)
                {
                    disposable.Dispose();
                }
                result.Payload = File.ReadAllBytes(TempFilePath);
                File.Delete(TempFilePath);
            } catch (Exception ex) { Log.Error($"CANNOT SAVE FILE: {filePath}. Error: {ex.Message}"); }
            return(result);
        }
            public void Execute()
            {
                Task.Run(async delegate
                {
                    var project     = this.CreateProject();
                    var outputFiles = new List <ITaskItem>();

                    // For incremental build, we want to consider the input->output files as well as the assemblies involved in code generation.
                    DateTime assembliesLastModified = GetLastModifiedAssemblyTime();

                    foreach (var inputDocument in project.Documents)
                    {
                        this.CancellationToken.ThrowIfCancellationRequested();

                        // Skip over documents that aren't on the prescribed list of files to scan.
                        if (!this.CompileToGenerateFromAttributes.Any(i => i.ItemSpec == inputDocument.Name))
                        {
                            continue;
                        }

                        string outputFilePath = Path.Combine(this.IntermediateOutputDirectory, Path.GetFileNameWithoutExtension(inputDocument.Name) + ".generated.cs");

                        // Code generation is relatively fast, but it's not free.
                        // And when we run the Simplifier.ReduceAsync it's dog slow.
                        // So skip files that haven't changed since we last generated them.
                        bool generated = false;
                        DateTime outputLastModified = File.GetLastWriteTime(outputFilePath);
                        if (File.GetLastWriteTime(inputDocument.Name) > outputLastModified || assembliesLastModified > outputLastModified)
                        {
                            this.Log.LogMessage(MessageImportance.Normal, "{0} -> {1}", inputDocument.Name, outputFilePath);

                            var outputDocument = await DocumentTransform.TransformAsync(inputDocument, new ProgressLogger(this.Log, inputDocument.Name));

                            // Only produce a new file if the generated document is not empty.
                            var semanticModel = await outputDocument.GetSemanticModelAsync(this.CancellationToken);
                            if (!semanticModel.GetDeclarationsInSpan(TextSpan.FromBounds(0, semanticModel.SyntaxTree.Length), false, this.CancellationToken).IsEmpty)
                            {
                                var outputText = await outputDocument.GetTextAsync(this.CancellationToken);
                                using (var outputFileStream = File.OpenWrite(outputFilePath))
                                    using (var outputWriter = new StreamWriter(outputFileStream))
                                    {
                                        outputText.Write(outputWriter);

                                        // Truncate any data that may be beyond this point if the file existed previously.
                                        outputWriter.Flush();
                                        outputFileStream.SetLength(outputFileStream.Position);
                                    }

                                generated = true;
                            }
                        }
                        else
                        {
                            generated = true;
                        }

                        if (generated)
                        {
                            var outputItem = new TaskItem(outputFilePath);
                            outputFiles.Add(outputItem);
                        }
                    }

                    this.GeneratedCompile = outputFiles.ToArray();
                }).GetAwaiter().GetResult();
            }
Esempio n. 10
0
        /// <summary>
        /// Adds a Write with an Update field to the list of writes, having first removed all sentinel values.
        /// If any sentinel values are server timestamps, they are transformed into a new write, which is also added.
        /// All methods creating an update should call this method rather than calling Writes.Add(write) themselves.
        /// </summary>
        private void AddUpdate(Write writeWithUpdate)
        {
            var transform = new DocumentTransform {
                Document = writeWithUpdate.Update.Name
            };

            ProcessSentinelValues(writeWithUpdate.Update.Fields, FieldPath.Empty);
            Writes.Add(writeWithUpdate);
            if (transform.FieldTransforms.Count != 0)
            {
                Writes.Add(new Write {
                    Transform = transform
                });
            }

            // Processes the sentinel values within a map of fields, recursively.
            // - Dictionary entries will be removed for sentinel fields
            // - The transform will be updated
            void ProcessSentinelValues(IDictionary <string, Value> fields, FieldPath parentPath)
            {
                List <string> removals = null;

                foreach (var pair in fields)
                {
                    Value  value = pair.Value;
                    string key   = pair.Key;
                    if (value.IsServerTimestampSentinel())
                    {
                        transform.FieldTransforms.Add(new FieldTransform
                        {
                            FieldPath        = parentPath.Append(pair.Key).EncodedPath,
                            SetToServerValue = ServerValue.RequestTime
                        });
                        removals = AddOrCreate(removals, key);
                    }
                    else if (value.IsDeleteSentinel())
                    {
                        removals = AddOrCreate(removals, key);
                    }
                    else if (value.MapValue != null)
                    {
                        ProcessSentinelValues(value.MapValue.Fields, parentPath.Append(pair.Key));
                    }
                    else if (value.ArrayValue != null)
                    {
                        ValidateNoSentinelValues(value.ArrayValue.Values);
                    }
                }
                removals?.ForEach(key => fields.Remove(key));
            }

            // Adds an item to an existing list, or creates a new list containing the item if there's currently no
            // list. This allows us to avoid creating lots of lists redundantly.
            List <string> AddOrCreate(List <string> list, string item)
            {
                if (list == null)
                {
                    list = new List <string>();
                }
                list.Add(item);
                return(list);
            }

            // Validates that the given sequence of values contains no sentinel values, recursing
            // as required to validate in a "deep" way.
            void ValidateNoSentinelValues(IEnumerable <Value> values)
            {
                foreach (var value in values)
                {
                    if (value.IsServerTimestampSentinel() || value.IsDeleteSentinel())
                    {
                        // We don't know what parameter name to use here
                        throw new ArgumentException("Sentinel values must not appear directly or indirectly within array values");
                    }
                    else if (value.MapValue != null)
                    {
                        ValidateNoSentinelValues(value.MapValue.Fields.Values);
                    }
                    else if (value.ArrayValue != null)
                    {
                        ValidateNoSentinelValues(value.ArrayValue.Values);
                    }
                }
            }
        }
Esempio n. 11
0
        public void Execute()
        {
#if NET46
            AppDomain.CurrentDomain.AssemblyResolve += (s, e) =>
            {
                return(this.TryLoadAssembly(new AssemblyName(e.Name)));
            };
#else
            this.loadContext.Resolving += (lc, an) =>
            {
                Assumes.True(ReferenceEquals(lc, this.loadContext));
                return(this.TryLoadAssembly(an));
            };
#endif

            Task.Run(async delegate
            {
                var compilation  = this.CreateCompilation();
                var outputFiles  = new List <ITaskItem>();
                var writtenFiles = new List <ITaskItem>();

                string generatorAssemblyInputsFile = Path.Combine(this.IntermediateOutputDirectory, "CodeGeneration.Roslyn.InputAssemblies.txt");

                // For incremental build, we want to consider the input->output files as well as the assemblies involved in code generation.
                DateTime assembliesLastModified = GetLastModifiedAssemblyTime(generatorAssemblyInputsFile);

                var explicitIncludeList = new HashSet <string>(
                    from item in this.Compile
                    where string.Equals(item.GetMetadata("Generator"), $"MSBuild:{this.TargetName}", StringComparison.OrdinalIgnoreCase)
                    select item.ItemSpec,
                    StringComparer.OrdinalIgnoreCase);

                foreach (var inputSyntaxTree in compilation.SyntaxTrees)
                {
                    this.CancellationToken.ThrowIfCancellationRequested();

                    // Skip over documents that aren't on the prescribed list of files to scan.
                    if (!explicitIncludeList.Contains(inputSyntaxTree.FilePath))
                    {
                        continue;
                    }

                    string sourceHash     = inputSyntaxTree.FilePath.GetHashCode().ToString("x", CultureInfo.InvariantCulture);
                    string outputFilePath = Path.Combine(this.IntermediateOutputDirectory, Path.GetFileNameWithoutExtension(inputSyntaxTree.FilePath) + $".{sourceHash}.generated.cs");

                    // Code generation is relatively fast, but it's not free.
                    // So skip files that haven't changed since we last generated them.
                    DateTime outputLastModified = File.Exists(outputFilePath) ? File.GetLastWriteTime(outputFilePath) : DateTime.MinValue;
                    if (File.GetLastWriteTime(inputSyntaxTree.FilePath) > outputLastModified || assembliesLastModified > outputLastModified)
                    {
                        var generatedSyntaxTree = await DocumentTransform.TransformAsync(
                            compilation,
                            inputSyntaxTree,
                            new ProgressLogger(this.Log, inputSyntaxTree.FilePath));

                        var outputText = generatedSyntaxTree.GetText(this.CancellationToken);
                        using (var outputFileStream = File.OpenWrite(outputFilePath))
                            using (var outputWriter = new StreamWriter(outputFileStream))
                            {
                                outputText.Write(outputWriter);

                                // Truncate any data that may be beyond this point if the file existed previously.
                                outputWriter.Flush();
                                outputFileStream.SetLength(outputFileStream.Position);
                            }

                        bool anyTypesGenerated = generatedSyntaxTree?.GetRoot(this.CancellationToken).DescendantNodes().OfType <TypeDeclarationSyntax>().Any() ?? false;
                        if (anyTypesGenerated)
                        {
                            this.Log.LogMessage(MessageImportance.Normal, "{0} -> {1}", inputSyntaxTree.FilePath, outputFilePath);
                        }
                        else
                        {
                            this.Log.LogMessage(MessageImportance.Low, "{0} used no code generation attributes.", inputSyntaxTree.FilePath);
                        }
                    }

                    var outputItem = new TaskItem(outputFilePath);
                    outputFiles.Add(outputItem);
                }

                this.SaveGeneratorAssemblyList(generatorAssemblyInputsFile);
                writtenFiles.Add(new TaskItem(generatorAssemblyInputsFile));

                this.GeneratedCompile       = outputFiles.ToArray();
                this.AdditionalWrittenFiles = writtenFiles.ToArray();
            }).GetAwaiter().GetResult();
        }
            public void Execute()
            {
                Task.Run(async delegate
                {
                    AppDomain.CurrentDomain.AssemblyResolve += (s, e) =>
                    {
                        return(this.TryLoadAssembly(new AssemblyName(e.Name)));
                    };

                    var project      = this.CreateProject();
                    var outputFiles  = new List <ITaskItem>();
                    var writtenFiles = new List <ITaskItem>();

                    string generatorAssemblyInputsFile = Path.Combine(this.IntermediateOutputDirectory, "CodeGeneration.Roslyn.InputAssemblies.txt");

                    // For incremental build, we want to consider the input->output files as well as the assemblies involved in code generation.
                    DateTime assembliesLastModified = GetLastModifiedAssemblyTime(generatorAssemblyInputsFile);

                    var explicitIncludeList = new HashSet <string>(
                        from item in this.Compile
                        where string.Equals(item.GetMetadata("Generator"), $"MSBuild:{this.TargetName}", StringComparison.OrdinalIgnoreCase)
                        select item.ItemSpec,
                        StringComparer.OrdinalIgnoreCase);

                    foreach (var inputDocument in project.Documents)
                    {
                        this.CancellationToken.ThrowIfCancellationRequested();

                        // Skip over documents that aren't on the prescribed list of files to scan.
                        if (!explicitIncludeList.Contains(inputDocument.Name))
                        {
                            continue;
                        }

                        string sourceHash     = inputDocument.Name.GetHashCode().ToString("x", CultureInfo.InvariantCulture);
                        string outputFilePath = Path.Combine(this.IntermediateOutputDirectory, Path.GetFileNameWithoutExtension(inputDocument.Name) + $".{sourceHash}.generated.cs");

                        // Code generation is relatively fast, but it's not free.
                        // And when we run the Simplifier.ReduceAsync it's dog slow.
                        // So skip files that haven't changed since we last generated them.
                        bool generated = false;
                        DateTime outputLastModified = File.GetLastWriteTime(outputFilePath);
                        if (File.GetLastWriteTime(inputDocument.Name) > outputLastModified || assembliesLastModified > outputLastModified)
                        {
                            this.Log.LogMessage(MessageImportance.Normal, "{0} -> {1}", inputDocument.Name, outputFilePath);

                            var outputDocument = await DocumentTransform.TransformAsync(
                                inputDocument,
                                new ProgressLogger(this.Log, inputDocument.Name));

                            // Only produce a new file if the generated document is not empty.
                            var semanticModel = await outputDocument.GetSemanticModelAsync(this.CancellationToken);
                            if (!CSharpDeclarationComputer.GetDeclarationsInSpan(semanticModel, TextSpan.FromBounds(0, semanticModel.SyntaxTree.Length), false, this.CancellationToken).IsEmpty)
                            {
                                var outputText = await outputDocument.GetTextAsync(this.CancellationToken);
                                using (var outputFileStream = File.OpenWrite(outputFilePath))
                                    using (var outputWriter = new StreamWriter(outputFileStream))
                                    {
                                        outputText.Write(outputWriter);

                                        // Truncate any data that may be beyond this point if the file existed previously.
                                        outputWriter.Flush();
                                        outputFileStream.SetLength(outputFileStream.Position);
                                    }

                                generated = true;
                            }
                        }
                        else
                        {
                            generated = true;
                        }

                        if (generated)
                        {
                            var outputItem = new TaskItem(outputFilePath);
                            outputFiles.Add(outputItem);
                        }
                    }

                    SaveGeneratorAssemblyList(generatorAssemblyInputsFile);
                    writtenFiles.Add(new TaskItem(generatorAssemblyInputsFile));

                    this.GeneratedCompile       = outputFiles.ToArray();
                    this.AdditionalWrittenFiles = writtenFiles.ToArray();
                }).GetAwaiter().GetResult();
            }