Example #1
0
        private static async Task AreEqualAsync(IReadOnlyList <string> expected, Solution actual, string messageHeader)
        {
            var index = 0;

            foreach (var project in actual.Projects)
            {
                for (var i = 0; i < project.DocumentIds.Count; i++)
                {
                    var fixedSource = await CodeReader.GetStringFromDocumentAsync(project.GetDocument(project.DocumentIds[i]), Formatter.Annotation, CancellationToken.None).ConfigureAwait(false);

                    CodeAssert.AreEqual(expected[index], fixedSource, messageHeader);
                    index++;
                }
            }
        }
Example #2
0
        /// <summary>
        /// Verifies that
        /// 1. <paramref name="diagnosticsAndSources"/> produces the expected diagnostics when analyzed.
        /// 2. The code fix fixes the code.
        /// </summary>
        /// <param name="analyzer">The analyzer to run on the code..</param>
        /// <param name="codeFix">The code fix to apply.</param>
        /// <param name="diagnosticsAndSources">The code and expected diagnostics.</param>
        /// <param name="fixedCode">The expected code produced by the code fix.</param>
        /// <param name="fixTitle">The title of the fix to apply if more than one.</param>
        /// <param name="compilationOptions">The <see cref="CSharpCompilationOptions"/> to use.</param>
        /// <param name="metadataReferences">The meta data metadataReferences to add to the compilation.</param>
        /// <param name="allowCompilationErrors">If compilation errors are accepted in the fixed code.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static async Task CodeFixAsync(DiagnosticAnalyzer analyzer, CodeFixProvider codeFix, DiagnosticsAndSources diagnosticsAndSources, string fixedCode, string fixTitle, CSharpCompilationOptions compilationOptions, IReadOnlyList <MetadataReference> metadataReferences, AllowCompilationErrors allowCompilationErrors)
        {
            AssertCodeFixCanFixDiagnosticsFromAnalyzer(analyzer, codeFix);
            var data = await DiagnosticsWithMetadataAsync(analyzer, diagnosticsAndSources, compilationOptions, metadataReferences).ConfigureAwait(false);

            var fixableDiagnostics = data.ActualDiagnostics.SelectMany(x => x)
                                     .Where(x => codeFix.FixableDiagnosticIds.Contains(x.Id))
                                     .ToArray();

            if (fixableDiagnostics.Length == 0)
            {
                var message = $"Code analyzed with {analyzer} did not generate any diagnostics fixable by {codeFix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", data.ExpectedDiagnostics.Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", codeFix.FixableDiagnosticIds)}}}";
                throw AssertException.Create(message);
            }

            if (fixableDiagnostics.Length > 1)
            {
                var message = $"Code analyzed with {analyzer} generated more than one diagnostic fixable by {codeFix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", data.ExpectedDiagnostics.Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", codeFix.FixableDiagnosticIds)}}}{Environment.NewLine}" +
                              $"Maybe you meant to call AnalyzerAssert.FixAll?";
                throw AssertException.Create(message);
            }

            var diagnostic    = fixableDiagnostics.Single();
            var fixedSolution = await Fix.ApplyAsync(data.Solution, codeFix, diagnostic, fixTitle, CancellationToken.None).ConfigureAwait(false);

            if (ReferenceEquals(data.Solution, fixedSolution))
            {
                throw AssertException.Create($"{codeFix} did not change any document.");
            }

            var fixedSource = await CodeReader.GetStringFromDocumentAsync(
                fixedSolution.GetDocument(data.Solution.GetDocument(diagnostic.Location.SourceTree).Id),
                Formatter.Annotation,
                CancellationToken.None)
                              .ConfigureAwait(false);

            CodeAssert.AreEqual(fixedCode, fixedSource);

            if (allowCompilationErrors == AllowCompilationErrors.No)
            {
                await AssertNoCompilerErrorsAsync(codeFix, fixedSolution).ConfigureAwait(false);
            }
        }
        private static async Task VerifyFixAsync(Solution sln, IReadOnlyList <ImmutableArray <Diagnostic> > diagnostics, DiagnosticAnalyzer analyzer, CodeFixProvider fix, string fixedCode, string fixTitle = null, AllowCompilationErrors allowCompilationErrors = AllowCompilationErrors.No)
        {
            var fixableDiagnostics = diagnostics.SelectMany(x => x)
                                     .Where(x => fix.FixableDiagnosticIds.Contains(x.Id))
                                     .ToArray();

            if (fixableDiagnostics.Length == 0)
            {
                var message = $"Code analyzed with {analyzer} did not generate any diagnostics fixable by {fix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", diagnostics.SelectMany(x => x).Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", fix.FixableDiagnosticIds)}}}";
                throw new AssertException(message);
            }

            if (fixableDiagnostics.Length > 1)
            {
                var message = $"Code analyzed with {analyzer} generated more than one diagnostic fixable by {fix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", diagnostics.SelectMany(x => x).Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", fix.FixableDiagnosticIds)}}}{Environment.NewLine}" +
                              $"Maybe you meant to call AnalyzerAssert.FixAll?";
                throw new AssertException(message);
            }

            var diagnostic    = fixableDiagnostics.Single();
            var fixedSolution = await Fix.ApplyAsync(sln, fix, diagnostic, fixTitle).ConfigureAwait(false);

            if (ReferenceEquals(sln, fixedSolution))
            {
                throw new AssertException($"{fix} did not change any document.");
            }

            var fixedSource = await CodeReader.GetStringFromDocumentAsync(
                fixedSolution.GetDocument(sln.GetDocument(diagnostic.Location.SourceTree).Id),
                Formatter.Annotation,
                CancellationToken.None)
                              .ConfigureAwait(false);

            CodeAssert.AreEqual(fixedCode, fixedSource);

            if (allowCompilationErrors == AllowCompilationErrors.No)
            {
                await VerifyNoCompilerErrorsAsync(fix, fixedSolution).ConfigureAwait(false);
            }
        }
Example #4
0
        private static async Task AreEqualAsync(IReadOnlyList <string> expected, Solution actual, string?messageHeader)
        {
            var actualCount = actual.Projects.SelectMany(x => x.Documents).Count();

            if (expected.Count != actualCount)
            {
                throw new AssertException($"Expected {expected.Count} documents the fixed solution has {actualCount} documents.");
            }

            foreach (var project in actual.Projects)
            {
                foreach (var document in project.Documents)
                {
                    var fixedSource = await CodeReader.GetStringFromDocumentAsync(document, CancellationToken.None).ConfigureAwait(false);

                    CodeAssert.AreEqual(FindExpected(fixedSource), fixedSource, messageHeader);
                }
            }

            string FindExpected(string fixedSource)
            {
                var fixedNamespace = CodeReader.Namespace(fixedSource);
                var fixedFileName  = CodeReader.FileName(fixedSource);
                var match          = expected.FirstOrDefault(x => x == fixedSource);

                if (match != null)
                {
                    return(match);
                }

                foreach (var candidate in expected)
                {
                    if (CodeReader.Namespace(candidate) == fixedNamespace &&
                        CodeReader.FileName(candidate) == fixedFileName)
                    {
                        return(candidate);
                    }
                }

                throw new AssertException($"The fixed solution contains a document {fixedFileName} in namespace {fixedNamespace} that is not in the expected documents.");
            }
        }
Example #5
0
        private static async Task AssertNoCompilerErrorsAsync(CodeFixProvider codeFix, Solution fixedSolution)
        {
            var diagnostics = await Analyze.GetDiagnosticsAsync(fixedSolution).ConfigureAwait(false);

            var introducedDiagnostics = diagnostics
                                        .SelectMany(x => x)
                                        .Where(IsIncluded)
                                        .ToArray();

            if (introducedDiagnostics.Select(x => x.Id)
                .Except(DiagnosticSettings.AllowedErrorIds())
                .Any())
            {
                var errorBuilder = StringBuilderPool.Borrow();
                errorBuilder.AppendLine($"{codeFix} introduced syntax error{(introducedDiagnostics.Length > 1 ? "s" : string.Empty)}.");
                foreach (var introducedDiagnostic in introducedDiagnostics)
                {
                    var errorInfo = await introducedDiagnostic.ToStringAsync(fixedSolution).ConfigureAwait(false);

                    errorBuilder.AppendLine($"{errorInfo}");
                }

                errorBuilder.AppendLine("First source file with error is:");
                var sources = await Task.WhenAll(fixedSolution.Projects.SelectMany(p => p.Documents).Select(d => CodeReader.GetStringFromDocumentAsync(d, Formatter.Annotation, CancellationToken.None)));

                var lineSpan = introducedDiagnostics.First().Location.GetMappedLineSpan();
                var match    = sources.SingleOrDefault(x => CodeReader.FileName(x) == lineSpan.Path);
                errorBuilder.Append(match);
                errorBuilder.AppendLine();
                throw AssertException.Create(errorBuilder.Return());
            }
        }
Example #6
0
        private static async Task VerifyNoCompilerErrorsAsync(CodeFixProvider fix, Solution fixedSolution)
        {
            var diagnostics = await Analyze.GetDiagnosticsAsync(fixedSolution).ConfigureAwait(false);

            var introducedDiagnostics = diagnostics
                                        .SelectMany(x => x)
                                        .Where(IsIncluded)
                                        .ToArray();

            if (introducedDiagnostics.Select(x => x.Id)
#pragma warning disable CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
                .Except(SuppressedDiagnostics)
#pragma warning restore CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
                .Any())
            {
                var errorBuilder = StringBuilderPool.Borrow();
                errorBuilder.AppendLine($"{fix.GetType().Name} introduced syntax error{(introducedDiagnostics.Length > 1 ? "s" : string.Empty)}.");
                foreach (var introducedDiagnostic in introducedDiagnostics)
                {
                    errorBuilder.AppendLine($"{introducedDiagnostic.ToErrorString()}");
                }

                var sources = await Task.WhenAll(fixedSolution.Projects.SelectMany(p => p.Documents).Select(d => CodeReader.GetStringFromDocumentAsync(d, CancellationToken.None)));

                errorBuilder.AppendLine("First source file with error is:");
                var lineSpan = introducedDiagnostics.First().Location.GetMappedLineSpan();
                if (sources.TrySingle(x => CodeReader.FileName(x) == lineSpan.Path, out var match))
                {
                    errorBuilder.AppendLine(match);
                }
                else if (sources.TryFirst(x => CodeReader.FileName(x) == lineSpan.Path, out _))
                {
                    errorBuilder.AppendLine($"Found more than one document for {lineSpan.Path}.");
                    foreach (string source in sources.Where(x => CodeReader.FileName(x) == lineSpan.Path))
                    {
                        errorBuilder.AppendLine(source);
                    }
                }
                else
                {
                    errorBuilder.AppendLine($"Did not find a single document for {lineSpan.Path}.");
                }

                throw new AssertException(errorBuilder.Return());
            }
        }