Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
343 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Testing; | ||
|
||
namespace Microsoft.CodeAnalysis.Tools.Tests.Analyzers | ||
{ | ||
public static class AnalyzerAssemblyGenerator | ||
{ | ||
public static SyntaxTree GenerateCodeFix(string typeName, string diagnosticId) | ||
{ | ||
var codefix = $@" | ||
using System; | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof({typeName})), Shared] | ||
public class {typeName} : CodeFixProvider | ||
{{ | ||
public const string DiagnosticId = ""{diagnosticId}""; | ||
public sealed override ImmutableArray<string> FixableDiagnosticIds | ||
=> ImmutableArray.Create(DiagnosticId); | ||
public sealed override FixAllProvider GetFixAllProvider() | ||
{{ | ||
return WellKnownFixAllProviders.BatchFixer; | ||
}} | ||
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{{ | ||
throw new NotImplementedException(); | ||
}} | ||
}}"; | ||
return CSharpSyntaxTree.ParseText(codefix); | ||
} | ||
|
||
public static SyntaxTree GenerateAnalyzerCode(string typeName, string diagnosticId) | ||
{ | ||
var analyzer = $@" | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public class {typeName} : DiagnosticAnalyzer | ||
{{ | ||
public const string DiagnosticId = ""{diagnosticId}""; | ||
internal static readonly LocalizableString Title = ""{typeName} Title""; | ||
internal static readonly LocalizableString MessageFormat = ""{typeName} '{{0}}'""; | ||
internal const string Category = ""{typeName} Category""; | ||
internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, true); | ||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); | ||
public override void Initialize(AnalysisContext context) | ||
{{ | ||
}} | ||
}}"; | ||
return CSharpSyntaxTree.ParseText(analyzer); | ||
} | ||
|
||
public static async Task<Assembly> GenerateAssemblyAsync(params SyntaxTree[] trees) | ||
{ | ||
var assemblyName = Guid.NewGuid().ToString(); | ||
var references = new List<MetadataReference>() | ||
{ | ||
MetadataReference.CreateFromFile(typeof(ImmutableArray).Assembly.Location), | ||
MetadataReference.CreateFromFile(typeof(SharedAttribute).Assembly.Location), | ||
MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location), | ||
MetadataReference.CreateFromFile(typeof(DiagnosticAnalyzer).Assembly.Location), | ||
MetadataReference.CreateFromFile(typeof(CodeFixProvider).Assembly.Location), | ||
}; | ||
|
||
var netstandardMetaDataReferences = await ReferenceAssemblies.NetStandard.NetStandard20.ResolveAsync(LanguageNames.CSharp, CancellationToken.None); | ||
references.AddRange(netstandardMetaDataReferences); | ||
var compilation = CSharpCompilation.Create(assemblyName, trees, references, | ||
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); | ||
using (var ms = new MemoryStream()) | ||
{ | ||
var result = compilation.Emit(ms); | ||
if (!result.Success) | ||
{ | ||
var failures = result.Diagnostics.Where(diagnostic => | ||
diagnostic.IsWarningAsError || | ||
diagnostic.Severity == DiagnosticSeverity.Error) | ||
.Select(diagnostic => $"{diagnostic.Id}: {diagnostic.GetMessage()}"); | ||
|
||
throw new Exception(string.Join(Environment.NewLine, failures)); | ||
} | ||
else | ||
{ | ||
ms.Seek(0, SeekOrigin.Begin); | ||
var assembly = Assembly.Load(ms.ToArray()); | ||
return assembly; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Testing; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Microsoft.CodeAnalysis.Tools.Analyzers; | ||
using Microsoft.CodeAnalysis.Tools.Formatters; | ||
using Microsoft.CodeAnalysis.Tools.Tests.Formatters; | ||
|
||
using Xunit; | ||
|
||
namespace Microsoft.CodeAnalysis.Tools.Tests.Analyzers | ||
{ | ||
using static AnalyzerAssemblyGenerator; | ||
|
||
public class FilterDiagnosticsTests : CSharpFormatterTests | ||
{ | ||
[Fact] | ||
public async Task TestFilterWarning() | ||
{ | ||
var projects = GetProjects(); | ||
var allAnalyzers = await GetAnalyzersAsync(); | ||
var formattablePaths = ImmutableHashSet.Create(projects.First().Documents.First().FilePath); | ||
var minimumSeverity = DiagnosticSeverity.Warning; | ||
var result = await AnalyzerFinderHelpers.FilterBySeverityAsync(projects, | ||
allAnalyzers, | ||
formattablePaths, | ||
minimumSeverity, | ||
CancellationToken.None); | ||
var (_, analyzers) = Assert.Single(result); | ||
Assert.Single(analyzers); | ||
} | ||
|
||
[Fact] | ||
public async Task TestFilterError() | ||
{ | ||
var projects = GetProjects(); | ||
var allAnalyzers = await GetAnalyzersAsync(); | ||
var formattablePaths = ImmutableHashSet.Create(projects.First().Documents.First().FilePath); | ||
var minimumSeverity = DiagnosticSeverity.Error; | ||
var result = await AnalyzerFinderHelpers.FilterBySeverityAsync(projects, | ||
allAnalyzers, | ||
formattablePaths, | ||
minimumSeverity, | ||
CancellationToken.None); | ||
var (_, analyzers) = Assert.Single(result); | ||
Assert.Empty(analyzers); | ||
} | ||
|
||
private async Task<ImmutableArray<DiagnosticAnalyzer>> GetAnalyzersAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId"), | ||
GenerateCodeFix("CodeFixProvider1", "DiagnosticAnalyzerId")) | ||
}; | ||
|
||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
return ImmutableArray.Create(analyzersAndFixers[0].Analyzer); | ||
} | ||
|
||
private IEnumerable<Project> GetProjects() | ||
{ | ||
var text = SourceText.From(""); | ||
TestState.Sources.Add(text); | ||
|
||
var solution = GetSolution(TestState.Sources.ToArray(), | ||
TestState.AdditionalFiles.ToArray(), | ||
TestState.AdditionalReferences.ToArray(), | ||
"root = true"); | ||
return solution.Projects; | ||
} | ||
|
||
private protected override ICodeFormatter Formatter { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Threading.Tasks; | ||
|
||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Tools.Analyzers; | ||
|
||
using Xunit; | ||
|
||
namespace Microsoft.CodeAnalysis.Tools.Tests.Analyzers | ||
{ | ||
using static AnalyzerAssemblyGenerator; | ||
|
||
public class LoadAnalyzersAndFixersTests | ||
{ | ||
[Fact] | ||
public static async Task TestSingleAnalyzerAndFixerAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId"), | ||
GenerateCodeFix("CodeFixProvider1", "DiagnosticAnalyzerId")) | ||
}; | ||
|
||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
var (analyzer, fixer) = Assert.Single(analyzersAndFixers); | ||
var analyzerDiagnosticDescriptor = Assert.Single(analyzer.SupportedDiagnostics); | ||
var fixerDiagnosticId = Assert.Single(fixer.FixableDiagnosticIds); | ||
Assert.Equal(analyzerDiagnosticDescriptor.Id, fixerDiagnosticId); | ||
} | ||
|
||
[Fact] | ||
public static async Task TestMultipleAnalyzersAndFixersAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId1"), | ||
GenerateAnalyzerCode("DiagnosticAnalyzer2", "DiagnosticAnalyzerId2"), | ||
GenerateCodeFix("CodeFixProvider1", "DiagnosticAnalyzerId1"), | ||
GenerateCodeFix("CodeFixProvider2", "DiagnosticAnalyzerId2")) | ||
}; | ||
|
||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
Assert.Equal(2, analyzersAndFixers.Length); | ||
Assert.Collection(analyzersAndFixers, VerifyAnalyzerCodeFixTuple, VerifyAnalyzerCodeFixTuple); | ||
} | ||
|
||
[Fact] | ||
public static async Task TestMultipleAnalyzersAndFixersFromTwoAssembliesAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId1"), | ||
GenerateCodeFix("CodeFixProvider1", "DiagnosticAnalyzerId1")), | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer2", "DiagnosticAnalyzerId2"), | ||
GenerateCodeFix("CodeFixProvider2", "DiagnosticAnalyzerId2")), | ||
}; | ||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
Assert.Equal(2, analyzersAndFixers.Length); | ||
Assert.Collection(analyzersAndFixers, VerifyAnalyzerCodeFixTuple, VerifyAnalyzerCodeFixTuple); | ||
} | ||
|
||
[Fact] | ||
public static async Task NonMatchingIdsAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId"), | ||
GenerateCodeFix("CodeFixProvider1", "CodeFixProviderId")) | ||
}; | ||
|
||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
Assert.Empty(analyzersAndFixers); | ||
} | ||
|
||
[Fact] | ||
public static async Task SomeMatchingIdsAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId1"), | ||
GenerateAnalyzerCode("DiagnosticAnalyzer2", "DiagnosticAnalyzerId2"), | ||
GenerateCodeFix("CodeFixProvider1", "DiagnosticAnalyzerId1"), | ||
GenerateCodeFix("CodeFixProvider2", "CodeFixProviderId")) | ||
}; | ||
|
||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
var (analyzer, fixer) = Assert.Single(analyzersAndFixers); | ||
var analyzerDiagnosticDescriptor = Assert.Single(analyzer.SupportedDiagnostics); | ||
var fixerDiagnosticId = Assert.Single(fixer.FixableDiagnosticIds); | ||
Assert.Equal(analyzerDiagnosticDescriptor.Id, fixerDiagnosticId); | ||
} | ||
|
||
[Fact] | ||
public static async Task SingleIdMapstoMultipleFixersAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId1"), | ||
GenerateAnalyzerCode("DiagnosticAnalyzer2", "DiagnosticAnalyzerId1"), | ||
GenerateCodeFix("CodeFixProvider1", "DiagnosticAnalyzerId1"), | ||
GenerateCodeFix("CodeFixProvider2", "CodeFixProviderId")) | ||
}; | ||
|
||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
Assert.Equal(2, analyzersAndFixers.Length); | ||
Assert.Collection(analyzersAndFixers, VerifyAnalyzerCodeFixTuple, VerifyAnalyzerCodeFixTuple); | ||
} | ||
|
||
[Fact] | ||
public static async Task MultipleIdsMaptoSingleFixerAsync() | ||
{ | ||
var assemblies = new[] | ||
{ | ||
await GenerateAssemblyAsync( | ||
GenerateAnalyzerCode("DiagnosticAnalyzer1", "DiagnosticAnalyzerId1"), | ||
GenerateAnalyzerCode("DiagnosticAnalyzer2", "DiagnosticAnalyzerId1"), | ||
GenerateCodeFix("CodeFixProvider1", "DiagnosticAnalyzerId1")) | ||
}; | ||
|
||
var analyzersAndFixers = AnalyzerFinderHelpers.LoadAnalyzersAndFixers(assemblies); | ||
Assert.Equal(2, analyzersAndFixers.Length); | ||
Assert.Collection(analyzersAndFixers, VerifyAnalyzerCodeFixTuple, VerifyAnalyzerCodeFixTuple); | ||
} | ||
|
||
private static void VerifyAnalyzerCodeFixTuple((DiagnosticAnalyzer Analyzer, CodeFixProvider Fixer) tuple) | ||
{ | ||
var analyzerDiagnosticDescriptor = Assert.Single(tuple.Analyzer.SupportedDiagnostics); | ||
var fixerDiagnosticId = Assert.Single(tuple.Fixer.FixableDiagnosticIds); | ||
Assert.Equal(analyzerDiagnosticDescriptor.Id, fixerDiagnosticId); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters