|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.Suppression;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.UnitTests.Diagnostics;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
{
[UseExportProvider]
public abstract class AbstractSuppressionAllCodeTests : IEqualityComparer<Diagnostic>
{
protected abstract TestWorkspace CreateWorkspaceFromFile(string definition, ParseOptions parseOptions);
internal abstract Tuple<Analyzer, IConfigurationFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace);
protected Task TestPragmaAsync(string code, ParseOptions options, Func<string, bool> verifier)
{
var set = new HashSet<ValueTuple<SyntaxToken, SyntaxToken>>();
return TestPragmaOrAttributeAsync(code, options, pragma: true, digInto: n => true, verifier: verifier, fixChecker: c =>
{
var fix = (AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction)c;
var tuple = ValueTuple.Create(fix.StartToken_TestOnly, fix.EndToken_TestOnly);
if (set.Contains(tuple))
{
return true;
}
set.Add(tuple);
return false;
});
}
protected Task TestSuppressionWithAttributeAsync(string code, ParseOptions options, Func<SyntaxNode, bool> digInto, Func<string, bool> verifier)
{
var set = new HashSet<ISymbol>();
return TestPragmaOrAttributeAsync(code, options, pragma: false, digInto: digInto, verifier: verifier, fixChecker: c =>
{
var fix = (AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction)c;
if (set.Contains(fix.TargetSymbol_TestOnly))
{
return true;
}
set.Add(fix.TargetSymbol_TestOnly);
return false;
});
}
protected async Task TestPragmaOrAttributeAsync(
string code, ParseOptions options, bool pragma, Func<SyntaxNode, bool> digInto, Func<string, bool> verifier, Func<CodeAction, bool> fixChecker)
{
using (var workspace = CreateWorkspaceFromFile(code, options))
{
var (analyzer, fixer) = CreateDiagnosticProviderAndFixer(workspace);
var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference }));
var document = workspace.CurrentSolution.Projects.Single().Documents.Single();
var root = document.GetSyntaxRootAsync().GetAwaiter().GetResult();
var existingDiagnostics = root.GetDiagnostics().ToArray();
var descendants = root.DescendantNodesAndSelf(digInto).ToImmutableArray();
analyzer.AllNodes = descendants;
var diagnostics = await DiagnosticProviderTestUtilities.GetAllDiagnosticsAsync(workspace, document, root.FullSpan);
foreach (var diagnostic in diagnostics)
{
if (!fixer.IsFixableDiagnostic(diagnostic))
{
continue;
}
var fixes = fixer.GetFixesAsync(document, diagnostic.Location.SourceSpan, SpecializedCollections.SingletonEnumerable(diagnostic), CodeActionOptions.DefaultProvider, CancellationToken.None).GetAwaiter().GetResult();
if (fixes == null || fixes.Count() <= 0)
{
continue;
}
var fix = GetFix(fixes.Select(f => f.Action), pragma);
if (fix == null)
{
continue;
}
// already same fix has been tested
if (fixChecker(fix))
{
continue;
}
var operations = fix.GetOperationsAsync(
document.Project.Solution, new ProgressTracker(), CancellationToken.None).GetAwaiter().GetResult();
var applyChangesOperation = operations.OfType<ApplyChangesOperation>().Single();
var newDocument = applyChangesOperation.ChangedSolution.Projects.Single().Documents.Single();
var newTree = newDocument.GetSyntaxTreeAsync().GetAwaiter().GetResult();
var newText = newTree.GetText().ToString();
Assert.True(verifier(newText));
var newDiagnostics = newTree.GetDiagnostics();
Assert.Equal(0, existingDiagnostics.Except(newDiagnostics, this).Count());
}
}
}
private static CodeAction GetFix(IEnumerable<CodeAction> fixes, bool pragma)
{
if (pragma)
{
return fixes.FirstOrDefault(f => f is AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction);
}
return fixes.OfType<AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction>().FirstOrDefault();
}
public bool Equals(Diagnostic x, Diagnostic y)
=> x.Id == y.Id && x.Descriptor.Category == y.Descriptor.Category;
public int GetHashCode(Diagnostic obj)
=> Hash.Combine(obj.Id, obj.Descriptor.Category.GetHashCode());
internal class Analyzer : DiagnosticAnalyzer, IBuiltInAnalyzer
{
private readonly DiagnosticDescriptor _descriptor =
new DiagnosticDescriptor("TestId", "Test", "Test", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true);
public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal;
public bool OpenFileOnly(SimplifierOptions options) => false;
public ImmutableArray<SyntaxNode> AllNodes { get; set; }
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(_descriptor);
}
}
public DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis;
public override void Initialize(AnalysisContext analysisContext)
{
analysisContext.RegisterSyntaxTreeAction(
context =>
{
foreach (var node in AllNodes)
{
context.ReportDiagnostic(Diagnostic.Create(_descriptor, node.GetLocation()));
}
});
}
}
}
}
|