diff --git a/RoslynSDK.ruleset b/RoslynSDK.ruleset index 2c248b26a..fc337d722 100644 --- a/RoslynSDK.ruleset +++ b/RoslynSDK.ruleset @@ -18,6 +18,6 @@ - + diff --git a/eng/Versions.props b/eng/Versions.props index b76517d71..216fe8140 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -61,7 +61,7 @@ 1.4.4 2.6.1 - 3.3.1 + 3.8.0 1.0.1-beta1.20374.2 1.2.7 0.1.49-beta diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs index 3f5e72095..35819de23 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/AnalyzerTest`1.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Testing.Model; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Composition; using IComparer = System.Collections.IComparer; @@ -68,7 +69,7 @@ static AnalyzerTest() protected AnalyzerTest() { - TestState = new SolutionState(DefaultFilePathPrefix, DefaultFileExt); + TestState = new SolutionState(DefaultTestProjectName, Language, DefaultFilePathPrefix, DefaultFileExt); } /// @@ -134,8 +135,9 @@ public string TestCode public List DisabledDiagnostics { get; } = new List(); /// - /// Gets or sets the reference assemblies to use. + /// Gets or sets the default reference assemblies to use. /// + /// public ReferenceAssemblies ReferenceAssemblies { get; set; } = ReferenceAssemblies.Default; /// @@ -178,7 +180,7 @@ public virtual async Task RunAsync(CancellationToken cancellationToken = default var fixableDiagnostics = ImmutableArray.Empty; var testState = TestState.WithInheritedValuesApplied(null, fixableDiagnostics).WithProcessedMarkup(MarkupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, DefaultFilePath); - await VerifyDiagnosticsAsync(testState.Sources.ToArray(), testState.AdditionalFiles.ToArray(), testState.AdditionalProjects.ToArray(), testState.AdditionalReferences.ToArray(), testState.ExpectedDiagnostics.ToArray(), Verify, cancellationToken).ConfigureAwait(false); + await VerifyDiagnosticsAsync(new EvaluatedProjectState(testState, ReferenceAssemblies), testState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), testState.ExpectedDiagnostics.ToArray(), Verify, cancellationToken).ConfigureAwait(false); } /// @@ -232,24 +234,24 @@ protected string FormatVerifierMessage(ImmutableArray analyz /// General method that gets a collection of actual s found in the source after the /// analyzer is run, then verifies each of them. /// - /// An array of strings to create source documents from to run the analyzers on. - /// Additional documents to include in the project. + /// The primary project. /// Additional projects to include in the solution. - /// Additional metadata references to include in the project. /// A collection of s that should appear after the analyzer /// is run on the sources. /// The verifier to use for test assertions. /// The that the task will observe. /// A representing the asynchronous operation. - protected async Task VerifyDiagnosticsAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, ProjectState[] additionalProjects, MetadataReference[] additionalMetadataReferences, DiagnosticResult[] expected, IVerifier verifier, CancellationToken cancellationToken) + protected async Task VerifyDiagnosticsAsync(EvaluatedProjectState primaryProject, ImmutableArray additionalProjects, DiagnosticResult[] expected, IVerifier verifier, CancellationToken cancellationToken) { + (string filename, SourceText content)[] sources = primaryProject.Sources.ToArray(); + var analyzers = GetDiagnosticAnalyzers().ToImmutableArray(); - VerifyDiagnosticResults(await GetSortedDiagnosticsAsync(sources, additionalFiles, additionalProjects, additionalMetadataReferences, analyzers, verifier, cancellationToken).ConfigureAwait(false), analyzers, expected, verifier); - await VerifyGeneratedCodeDiagnosticsAsync(analyzers, sources, additionalFiles, additionalProjects, additionalMetadataReferences, expected, verifier, cancellationToken).ConfigureAwait(false); - await VerifySuppressionDiagnosticsAsync(analyzers, sources, additionalFiles, additionalProjects, additionalMetadataReferences, expected, verifier, cancellationToken).ConfigureAwait(false); + VerifyDiagnosticResults(await GetSortedDiagnosticsAsync(primaryProject, additionalProjects, analyzers, verifier, cancellationToken).ConfigureAwait(false), analyzers, expected, verifier); + await VerifyGeneratedCodeDiagnosticsAsync(analyzers, sources, primaryProject, additionalProjects, expected, verifier, cancellationToken).ConfigureAwait(false); + await VerifySuppressionDiagnosticsAsync(analyzers, sources, primaryProject, additionalProjects, expected, verifier, cancellationToken).ConfigureAwait(false); } - private async Task VerifyGeneratedCodeDiagnosticsAsync(ImmutableArray analyzers, (string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, ProjectState[] additionalProjects, MetadataReference[] additionalMetadataReferences, DiagnosticResult[] expected, IVerifier verifier, CancellationToken cancellationToken) + private async Task VerifyGeneratedCodeDiagnosticsAsync(ImmutableArray analyzers, (string filename, SourceText content)[] sources, EvaluatedProjectState primaryProject, ImmutableArray additionalProjects, DiagnosticResult[] expected, IVerifier verifier, CancellationToken cancellationToken) { if (TestBehaviors.HasFlag(TestBehaviors.SkipGeneratedCodeCheck) || analyzers.All(analyzer => AnalyzerInfo.HasConfiguredGeneratedCodeAnalysis(analyzer))) @@ -272,10 +274,11 @@ private async Task VerifyGeneratedCodeDiagnosticsAsync(ImmutableArray code"); var commentPrefix = Language == LanguageNames.CSharp ? "//" : "'"; - VerifyDiagnosticResults(await GetSortedDiagnosticsAsync(sources.Select(x => (x.filename, x.content.Replace(new TextSpan(0, 0), $" {commentPrefix} \r\n"))).ToArray(), additionalFiles, additionalProjects, additionalMetadataReferences, analyzers, generatedCodeVerifier, cancellationToken).ConfigureAwait(false), analyzers, expectedResults, generatedCodeVerifier); + var transformedProject = primaryProject.WithSources(primaryProject.Sources.Select(x => (x.filename, x.content.Replace(new TextSpan(0, 0), $" {commentPrefix} \r\n"))).ToImmutableArray()); + VerifyDiagnosticResults(await GetSortedDiagnosticsAsync(transformedProject, additionalProjects, analyzers, generatedCodeVerifier, cancellationToken).ConfigureAwait(false), analyzers, expectedResults, generatedCodeVerifier); } - private async Task VerifySuppressionDiagnosticsAsync(ImmutableArray analyzers, (string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, ProjectState[] additionalProjects, MetadataReference[] additionalMetadataReferences, DiagnosticResult[] expected, IVerifier verifier, CancellationToken cancellationToken) + private async Task VerifySuppressionDiagnosticsAsync(ImmutableArray analyzers, (string filename, SourceText content)[] sources, EvaluatedProjectState primaryProject, ImmutableArray additionalProjects, DiagnosticResult[] expected, IVerifier verifier, CancellationToken cancellationToken) { if (TestBehaviors.HasFlag(TestBehaviors.SkipSuppressionCheck)) { @@ -299,7 +302,8 @@ private async Task VerifySuppressionDiagnosticsAsync(ImmutableArray IsSubjectToExclusion(x, sources)).Select(x => x.Id).Distinct(); var suppression = prefix + " " + string.Join(", ", suppressedDiagnostics); - VerifyDiagnosticResults(await GetSortedDiagnosticsAsync(sources.Select(x => (x.filename, x.content.Replace(new TextSpan(0, 0), $"{suppression}\r\n"))).ToArray(), additionalFiles, additionalProjects, additionalMetadataReferences, analyzers, suppressionVerifier, cancellationToken).ConfigureAwait(false), analyzers, expectedResults, suppressionVerifier); + var transformedProject = primaryProject.WithSources(primaryProject.Sources.Select(x => (x.filename, x.content.Replace(new TextSpan(0, 0), $"{suppression}\r\n"))).ToImmutableArray()); + VerifyDiagnosticResults(await GetSortedDiagnosticsAsync(transformedProject, additionalProjects, analyzers, suppressionVerifier, cancellationToken).ConfigureAwait(false), analyzers, expectedResults, suppressionVerifier); } /// @@ -952,18 +956,16 @@ private static bool IsInSourceFile(DiagnosticResult result, (string filename, So /// it, return the s found in the string after converting it to a /// . /// - /// Classes in the form of strings. - /// Additional documents to include in the project. + /// The primary project. /// Additional projects to include in the solution. - /// Additional metadata references to include in the project. /// The analyzers to be run on the sources. /// The verifier to use for test assertions. /// The that the task will observe. /// A collection of s that surfaced in the source code, sorted by /// . - private async Task> GetSortedDiagnosticsAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, ProjectState[] additionalProjects, MetadataReference[] additionalMetadataReferences, ImmutableArray analyzers, IVerifier verifier, CancellationToken cancellationToken) + private async Task> GetSortedDiagnosticsAsync(EvaluatedProjectState primaryProject, ImmutableArray additionalProjects, ImmutableArray analyzers, IVerifier verifier, CancellationToken cancellationToken) { - var solution = await GetSolutionAsync(sources, additionalFiles, additionalProjects, additionalMetadataReferences, verifier, cancellationToken); + var solution = await GetSolutionAsync(primaryProject, additionalProjects, verifier, cancellationToken); return await GetSortedDiagnosticsAsync(solution, analyzers, CompilerDiagnostics, cancellationToken); } @@ -1039,21 +1041,19 @@ protected virtual AnalyzerOptions GetAnalyzerOptions(Project project) /// Given an array of strings as sources and a language, turn them into a and return the /// solution. /// - /// Classes in the form of strings. - /// Additional documents to include in the project. + /// The primary project. /// Additional projects to include in the solution. - /// Additional metadata references to include in the project. /// The verifier to use for test assertions. /// The that the task will observe. /// A solution containing a project with the specified sources and additional files. - private async Task GetSolutionAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, ProjectState[] additionalProjects, MetadataReference[] additionalMetadataReferences, IVerifier verifier, CancellationToken cancellationToken) + private async Task GetSolutionAsync(EvaluatedProjectState primaryProject, ImmutableArray additionalProjects, IVerifier verifier, CancellationToken cancellationToken) { verifier.LanguageIsSupported(Language); - var project = await CreateProjectAsync(sources, additionalFiles, additionalProjects, additionalMetadataReferences, Language, cancellationToken); + var project = await CreateProjectAsync(primaryProject, additionalProjects, cancellationToken); var documents = project.Documents.ToArray(); - verifier.Equal(sources.Length, documents.Length, "Amount of sources did not match amount of Documents created"); + verifier.Equal(primaryProject.Sources.Length, documents.Length, "Amount of sources did not match amount of Documents created"); return project.Solution; } @@ -1065,90 +1065,127 @@ private async Task GetSolutionAsync((string filename, SourceText conte /// This method first creates a by calling , and then /// applies compilation options to the project by calling . /// - /// Classes in the form of strings. - /// Additional documents to include in the project. + /// The primary project. /// Additional projects to include in the solution. - /// Additional metadata references to include in the project. - /// The language the source classes are in. Values may be taken from the - /// class. /// The that the task will observe. /// A created out of the s created from the source /// strings. - protected async Task CreateProjectAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, ProjectState[] additionalProjects, MetadataReference[] additionalMetadataReferences, string language, CancellationToken cancellationToken) + protected async Task CreateProjectAsync(EvaluatedProjectState primaryProject, ImmutableArray additionalProjects, CancellationToken cancellationToken) { - var project = await CreateProjectImplAsync(sources, additionalFiles, additionalProjects, additionalMetadataReferences, language, cancellationToken); + var project = await CreateProjectImplAsync(primaryProject, additionalProjects, cancellationToken); return ApplyCompilationOptions(project); } /// /// Create a project using the input strings as sources. /// - /// Classes in the form of strings. - /// Additional documents to include in the project. + /// The primary project. /// Additional projects to include in the solution. - /// Additional metadata references to include in the project. - /// The language the source classes are in. Values may be taken from the - /// class. /// The that the task will observe. /// A created out of the s created from the source /// strings. - protected virtual async Task CreateProjectImplAsync((string filename, SourceText content)[] sources, (string filename, SourceText content)[] additionalFiles, ProjectState[] additionalProjects, MetadataReference[] additionalMetadataReferences, string language, CancellationToken cancellationToken) + protected virtual async Task CreateProjectImplAsync(EvaluatedProjectState primaryProject, ImmutableArray additionalProjects, CancellationToken cancellationToken) { var fileNamePrefix = DefaultFilePathPrefix; var fileExt = DefaultFileExt; - var projectId = ProjectId.CreateNewId(debugName: DefaultTestProjectName); - var solution = await CreateSolutionAsync(projectId, language, cancellationToken); + var projectIdMap = new Dictionary(); + + var projectId = ProjectId.CreateNewId(debugName: primaryProject.Name); + projectIdMap.Add(primaryProject.Name, projectId); + var solution = await CreateSolutionAsync(projectId, primaryProject, cancellationToken); foreach (var projectState in additionalProjects) { var additionalProjectId = ProjectId.CreateNewId(debugName: projectState.Name); + projectIdMap.Add(projectState.Name, additionalProjectId); + solution = solution.AddProject(additionalProjectId, projectState.Name, projectState.AssemblyName, projectState.Language); - for (var i = 0; i < projectState.Sources.Count; i++) + var referenceAssemblies = projectState.ReferenceAssemblies ?? ReferenceAssemblies; + + var xmlReferenceResolver = new TestXmlReferenceResolver(); + foreach (var xmlReference in XmlReferences) + { + xmlReferenceResolver.XmlReferences.Add(xmlReference.Key, xmlReference.Value); + } + + solution = solution.WithProjectCompilationOptions( + additionalProjectId, + solution.GetProject(additionalProjectId).CompilationOptions + .WithOutputKind(projectState.OutputKind) + .WithXmlReferenceResolver(xmlReferenceResolver) + .WithAssemblyIdentityComparer(referenceAssemblies.AssemblyIdentityComparer)); + + solution = solution.WithProjectParseOptions( + additionalProjectId, + solution.GetProject(additionalProjectId).ParseOptions + .WithDocumentationMode(projectState.DocumentationMode)); + + var metadataReferences = await referenceAssemblies.ResolveAsync(projectState.Language, cancellationToken); + solution = solution.AddMetadataReferences(additionalProjectId, metadataReferences) + .AddMetadataReferences(additionalProjectId, projectState.AdditionalReferences); + + foreach (var (newFileName, source) in projectState.Sources) { - (var newFileName, var source) = projectState.Sources[i]; var documentId = DocumentId.CreateNewId(additionalProjectId, debugName: newFileName); solution = solution.AddDocument(documentId, newFileName, source, filePath: newFileName); } - solution = solution.AddProjectReference(projectId, new ProjectReference(additionalProjectId)); + foreach (var (newFileName, source) in projectState.AdditionalFiles) + { + var documentId = DocumentId.CreateNewId(additionalProjectId, debugName: newFileName); + solution = solution.AddAdditionalDocument(documentId, newFileName, source, filePath: newFileName); + } } - solution = solution.AddMetadataReferences(projectId, additionalMetadataReferences); + solution = solution.AddMetadataReferences(projectId, primaryProject.AdditionalReferences); - for (var i = 0; i < sources.Length; i++) + foreach (var (newFileName, source) in primaryProject.Sources) { - (var newFileName, var source) = sources[i]; var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); solution = solution.AddDocument(documentId, newFileName, source, filePath: newFileName); } - for (var i = 0; i < additionalFiles.Length; i++) + foreach (var (newFileName, source) in primaryProject.AdditionalFiles) { - (var newFileName, var source) = additionalFiles[i]; var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); solution = solution.AddAdditionalDocument(documentId, newFileName, source, filePath: newFileName); } + solution = AddProjectReferences(solution, projectId, primaryProject.AdditionalProjectReferences.Select(name => projectIdMap[name])); + foreach (var projectState in additionalProjects) + { + solution = AddProjectReferences(solution, projectIdMap[projectState.Name], projectState.AdditionalProjectReferences.Select(name => projectIdMap[name])); + } + foreach (var transform in SolutionTransforms) { solution = transform(solution, projectId); } return solution.GetProject(projectId); + + // Local functions + static Solution AddProjectReferences(Solution solution, ProjectId sourceProject, IEnumerable targetProjects) + { + return solution.AddProjectReferences(sourceProject, targetProjects.Select(id => new ProjectReference(id))); + } } /// /// Creates a solution that will be used as parent for the sources that need to be checked. /// /// The project identifier to use. - /// The language for which the solution is being created. + /// The primary project. /// The that the task will observe. /// The created solution. - protected virtual async Task CreateSolutionAsync(ProjectId projectId, string language, CancellationToken cancellationToken) + protected virtual async Task CreateSolutionAsync(ProjectId projectId, EvaluatedProjectState projectState, CancellationToken cancellationToken) { - var compilationOptions = CreateCompilationOptions(); + var referenceAssemblies = projectState.ReferenceAssemblies ?? ReferenceAssemblies; + + var compilationOptions = CreateCompilationOptions() + .WithOutputKind(projectState.OutputKind); var xmlReferenceResolver = new TestXmlReferenceResolver(); foreach (var xmlReference in XmlReferences) @@ -1158,9 +1195,10 @@ protected virtual async Task CreateSolutionAsync(ProjectId projectId, compilationOptions = compilationOptions .WithXmlReferenceResolver(xmlReferenceResolver) - .WithAssemblyIdentityComparer(ReferenceAssemblies.AssemblyIdentityComparer); + .WithAssemblyIdentityComparer(referenceAssemblies.AssemblyIdentityComparer); - var parseOptions = CreateParseOptions(); + var parseOptions = CreateParseOptions() + .WithDocumentationMode(projectState.DocumentationMode); var workspace = CreateWorkspace(); foreach (var transform in OptionsTransforms) @@ -1170,11 +1208,11 @@ protected virtual async Task CreateSolutionAsync(ProjectId projectId, var solution = workspace .CurrentSolution - .AddProject(projectId, DefaultTestProjectName, DefaultTestProjectName, language) + .AddProject(projectId, projectState.Name, projectState.Name, projectState.Language) .WithProjectCompilationOptions(projectId, compilationOptions) .WithProjectParseOptions(projectId, parseOptions); - var metadataReferences = await ReferenceAssemblies.ResolveAsync(language, cancellationToken); + var metadataReferences = await referenceAssemblies.ResolveAsync(projectState.Language, cancellationToken); solution = solution.AddMetadataReferences(projectId, metadataReferences); return solution; diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/DictionaryExtensions.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/DictionaryExtensions.cs index 2a9494dfa..a2b88f208 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/DictionaryExtensions.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Extensions/DictionaryExtensions.cs @@ -9,6 +9,15 @@ namespace Microsoft.CodeAnalysis.Testing { internal static class DictionaryExtensions { + public static void AddRange(this IDictionary dictionary, IEnumerable> items) + where TKey : notnull + { + foreach (var (key, value) in items) + { + dictionary.Add(key, value); + } + } + // Copied from ConcurrentDictionary since IDictionary doesn't have this useful method public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func function) where TKey : notnull diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Model/EvaluatedProjectState.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Model/EvaluatedProjectState.cs new file mode 100644 index 000000000..138dcb23e --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/Model/EvaluatedProjectState.cs @@ -0,0 +1,114 @@ +// 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. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Testing.Model +{ + /// + /// Represents an evaluated . + /// + public sealed class EvaluatedProjectState + { + public EvaluatedProjectState(ProjectState state, ReferenceAssemblies defaultReferenceAssemblies) + : this( + state.Name, + state.AssemblyName, + state.Language, + state.ReferenceAssemblies ?? defaultReferenceAssemblies, + state.OutputKind ?? OutputKind.DynamicallyLinkedLibrary, + state.DocumentationMode ?? DocumentationMode.Diagnose, + state.Sources.ToImmutableArray(), + state.AdditionalFiles.ToImmutableArray(), + state.AdditionalProjectReferences.ToImmutableArray(), + state.AdditionalReferences.ToImmutableArray()) + { + } + + private EvaluatedProjectState( + string name, + string assemblyName, + string language, + ReferenceAssemblies referenceAssemblies, + OutputKind outputKind, + DocumentationMode documentationMode, + ImmutableArray<(string filename, SourceText content)> sources, + ImmutableArray<(string filename, SourceText content)> additionalFiles, + ImmutableArray additionalProjectReferences, + ImmutableArray additionalReferences) + { + Name = name; + AssemblyName = assemblyName; + Language = language; + ReferenceAssemblies = referenceAssemblies; + OutputKind = outputKind; + DocumentationMode = documentationMode; + Sources = sources; + AdditionalFiles = additionalFiles; + AdditionalProjectReferences = additionalProjectReferences; + AdditionalReferences = additionalReferences; + } + + public string Name { get; } + + public string AssemblyName { get; } + + public string Language { get; } + + public ReferenceAssemblies ReferenceAssemblies { get; } + + public OutputKind OutputKind { get; } + + public DocumentationMode DocumentationMode { get; } + + public ImmutableArray<(string filename, SourceText content)> Sources { get; } + + public ImmutableArray<(string filename, SourceText content)> AdditionalFiles { get; } + + public ImmutableArray AdditionalProjectReferences { get; } + + public ImmutableArray AdditionalReferences { get; } + + public EvaluatedProjectState WithSources(ImmutableArray<(string filename, SourceText content)> sources) + { + if (sources == Sources) + { + return this; + } + + return With(sources: sources); + } + + private EvaluatedProjectState With( + Optional name = default, + Optional assemblyName = default, + Optional language = default, + Optional referenceAssemblies = default, + Optional outputKind = default, + Optional documentationMode = default, + Optional> sources = default, + Optional> additionalFiles = default, + Optional> additionalProjectReferences = default, + Optional> additionalReferences = default) + { + return new EvaluatedProjectState( + GetValueOrDefault(name, Name), + GetValueOrDefault(assemblyName, AssemblyName), + GetValueOrDefault(language, Language), + GetValueOrDefault(referenceAssemblies, ReferenceAssemblies), + GetValueOrDefault(outputKind, OutputKind), + GetValueOrDefault(documentationMode, DocumentationMode), + GetValueOrDefault(sources, Sources), + GetValueOrDefault(additionalFiles, AdditionalFiles), + GetValueOrDefault(additionalProjectReferences, AdditionalProjectReferences), + GetValueOrDefault(additionalReferences, AdditionalReferences)); + } + + private static T GetValueOrDefault(Optional optionalValue, T defaultValue) + { + return optionalValue.HasValue ? optionalValue.Value : defaultValue; + } + } +} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ProjectCollection.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ProjectCollection.cs new file mode 100644 index 000000000..07ee2603d --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ProjectCollection.cs @@ -0,0 +1,48 @@ +// 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. + +using System; +using System.Collections.Generic; + +namespace Microsoft.CodeAnalysis.Testing +{ + public class ProjectCollection : Dictionary + { + private readonly string _defaultLanguage; + private readonly string _defaultExtension; + + public ProjectCollection(string defaultLanguage, string defaultExtension) + { + _defaultLanguage = defaultLanguage; + _defaultExtension = defaultExtension; + } + + public new ProjectState this[string projectName] + { + get + { + if (TryGetValue(projectName, out var project)) + { + return project; + } + + return this[projectName, _defaultLanguage]; + } + } + + public ProjectState this[string projectName, string language] + { + get + { + var project = this.GetOrAdd(projectName, () => new ProjectState(projectName, _defaultLanguage, $"/{projectName}/Test", _defaultExtension)); + if (project.Language != language) + { + throw new InvalidOperationException(); + } + + return project; + } + } + } +} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ProjectState.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ProjectState.cs index 49686c8b6..8131415cc 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ProjectState.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/ProjectState.cs @@ -2,27 +2,60 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Text; + namespace Microsoft.CodeAnalysis.Testing { - public abstract class ProjectState + public class ProjectState { - private readonly string _defaultPrefix; - private readonly string _defaultExtension; - - protected ProjectState(string name, string defaultPrefix, string defaultExtension) + public ProjectState(string name, string language, string defaultPrefix, string defaultExtension) { Name = name; - _defaultPrefix = defaultPrefix; - _defaultExtension = defaultExtension; + Language = language; + DefaultPrefix = defaultPrefix; + DefaultExtension = defaultExtension; Sources = new SourceFileList(defaultPrefix, defaultExtension); } + internal ProjectState(ProjectState sourceState) + { + Name = sourceState.Name; + Language = sourceState.Language; + ReferenceAssemblies = sourceState.ReferenceAssemblies; + OutputKind = sourceState.OutputKind; + DocumentationMode = sourceState.DocumentationMode; + DefaultPrefix = sourceState.DefaultPrefix; + DefaultExtension = sourceState.DefaultExtension; + Sources = new SourceFileList(DefaultPrefix, DefaultExtension); + + Sources.AddRange(sourceState.Sources); + AdditionalFiles.AddRange(sourceState.AdditionalFiles); + AdditionalFilesFactories.AddRange(sourceState.AdditionalFilesFactories); + AdditionalProjectReferences.AddRange(sourceState.AdditionalProjectReferences); + } + public string Name { get; } public string AssemblyName => Name; - public abstract string Language { get; } + public string Language { get; } + + /// + /// Gets or sets the reference assemblies to use for the project. + /// + /// + /// A instance to use specific reference assemblies; otherwise, + /// to inherit the reference assemblies from + /// . + /// + public ReferenceAssemblies? ReferenceAssemblies { get; set; } + + public OutputKind? OutputKind { get; set; } + + public DocumentationMode? DocumentationMode { get; set; } /// /// Gets the set of source files for analyzer or code fix testing. Files may be added to this list using one of @@ -30,6 +63,16 @@ protected ProjectState(string name, string defaultPrefix, string defaultExtensio /// public SourceFileList Sources { get; } + public SourceFileCollection AdditionalFiles { get; } = new SourceFileCollection(); + + public List>> AdditionalFilesFactories { get; } = new List>>(); + + public List AdditionalProjectReferences { get; } = new List(); + public MetadataReferenceCollection AdditionalReferences { get; } = new MetadataReferenceCollection(); + + private protected string DefaultPrefix { get; } + + private protected string DefaultExtension { get; } } } diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt index 2364f2925..842920f86 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/PublicAPI.Unshipped.txt @@ -2,7 +2,7 @@ Microsoft.CodeAnalysis.Testing.AnalyzerTest Microsoft.CodeAnalysis.Testing.AnalyzerTest.AnalyzerTest() -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.CompilerDiagnostics.get -> Microsoft.CodeAnalysis.Testing.CompilerDiagnostics Microsoft.CodeAnalysis.Testing.AnalyzerTest.CompilerDiagnostics.set -> void -Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectAsync((string filename, Microsoft.CodeAnalysis.Text.SourceText content)[] sources, (string filename, Microsoft.CodeAnalysis.Text.SourceText content)[] additionalFiles, Microsoft.CodeAnalysis.Testing.ProjectState[] additionalProjects, Microsoft.CodeAnalysis.MetadataReference[] additionalMetadataReferences, string language, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectAsync(Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState primaryProject, System.Collections.Immutable.ImmutableArray additionalProjects, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Testing.AnalyzerTest.DiagnosticVerifier.get -> System.Action Microsoft.CodeAnalysis.Testing.AnalyzerTest.DiagnosticVerifier.set -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.DisabledDiagnostics.get -> System.Collections.Generic.List @@ -21,7 +21,7 @@ Microsoft.CodeAnalysis.Testing.AnalyzerTest.TestBehaviors.get -> Micr Microsoft.CodeAnalysis.Testing.AnalyzerTest.TestBehaviors.set -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.TestCode.set -> void Microsoft.CodeAnalysis.Testing.AnalyzerTest.TestState.get -> Microsoft.CodeAnalysis.Testing.SolutionState -Microsoft.CodeAnalysis.Testing.AnalyzerTest.VerifyDiagnosticsAsync((string filename, Microsoft.CodeAnalysis.Text.SourceText content)[] sources, (string filename, Microsoft.CodeAnalysis.Text.SourceText content)[] additionalFiles, Microsoft.CodeAnalysis.Testing.ProjectState[] additionalProjects, Microsoft.CodeAnalysis.MetadataReference[] additionalMetadataReferences, Microsoft.CodeAnalysis.Testing.DiagnosticResult[] expected, Microsoft.CodeAnalysis.Testing.IVerifier verifier, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +Microsoft.CodeAnalysis.Testing.AnalyzerTest.VerifyDiagnosticsAsync(Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState primaryProject, System.Collections.Immutable.ImmutableArray additionalProjects, Microsoft.CodeAnalysis.Testing.DiagnosticResult[] expected, Microsoft.CodeAnalysis.Testing.IVerifier verifier, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Microsoft.CodeAnalysis.Testing.AnalyzerTest.XmlReferences.get -> System.Collections.Generic.Dictionary Microsoft.CodeAnalysis.Testing.AnalyzerVerifier Microsoft.CodeAnalysis.Testing.AnalyzerVerifier.AnalyzerVerifier() -> void @@ -118,15 +118,42 @@ Microsoft.CodeAnalysis.Testing.MetadataReferenceCollection.Add(System.Reflection Microsoft.CodeAnalysis.Testing.MetadataReferenceCollection.Add(string path) -> void Microsoft.CodeAnalysis.Testing.MetadataReferenceCollection.MetadataReferenceCollection() -> void Microsoft.CodeAnalysis.Testing.MetadataReferences +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AdditionalFiles.get -> System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)> +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AdditionalProjectReferences.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AdditionalReferences.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AssemblyName.get -> string +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.DocumentationMode.get -> Microsoft.CodeAnalysis.DocumentationMode +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.EvaluatedProjectState(Microsoft.CodeAnalysis.Testing.ProjectState state, Microsoft.CodeAnalysis.Testing.ReferenceAssemblies defaultReferenceAssemblies) -> void +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.Language.get -> string +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.Name.get -> string +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.OutputKind.get -> Microsoft.CodeAnalysis.OutputKind +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.ReferenceAssemblies.get -> Microsoft.CodeAnalysis.Testing.ReferenceAssemblies +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.Sources.get -> System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)> +Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.WithSources(System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)> sources) -> Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState Microsoft.CodeAnalysis.Testing.PackageIdentity Microsoft.CodeAnalysis.Testing.PackageIdentity.Id.get -> string Microsoft.CodeAnalysis.Testing.PackageIdentity.PackageIdentity(string id, string version) -> void Microsoft.CodeAnalysis.Testing.PackageIdentity.Version.get -> string +Microsoft.CodeAnalysis.Testing.ProjectCollection +Microsoft.CodeAnalysis.Testing.ProjectCollection.ProjectCollection(string defaultLanguage, string defaultExtension) -> void +Microsoft.CodeAnalysis.Testing.ProjectCollection.this[string projectName, string language].get -> Microsoft.CodeAnalysis.Testing.ProjectState +Microsoft.CodeAnalysis.Testing.ProjectCollection.this[string projectName].get -> Microsoft.CodeAnalysis.Testing.ProjectState Microsoft.CodeAnalysis.Testing.ProjectState +Microsoft.CodeAnalysis.Testing.ProjectState.AdditionalFiles.get -> Microsoft.CodeAnalysis.Testing.SourceFileCollection +Microsoft.CodeAnalysis.Testing.ProjectState.AdditionalFilesFactories.get -> System.Collections.Generic.List>> +Microsoft.CodeAnalysis.Testing.ProjectState.AdditionalProjectReferences.get -> System.Collections.Generic.List Microsoft.CodeAnalysis.Testing.ProjectState.AdditionalReferences.get -> Microsoft.CodeAnalysis.Testing.MetadataReferenceCollection Microsoft.CodeAnalysis.Testing.ProjectState.AssemblyName.get -> string +Microsoft.CodeAnalysis.Testing.ProjectState.DocumentationMode.get -> Microsoft.CodeAnalysis.DocumentationMode? +Microsoft.CodeAnalysis.Testing.ProjectState.DocumentationMode.set -> void +Microsoft.CodeAnalysis.Testing.ProjectState.Language.get -> string Microsoft.CodeAnalysis.Testing.ProjectState.Name.get -> string -Microsoft.CodeAnalysis.Testing.ProjectState.ProjectState(string name, string defaultPrefix, string defaultExtension) -> void +Microsoft.CodeAnalysis.Testing.ProjectState.OutputKind.get -> Microsoft.CodeAnalysis.OutputKind? +Microsoft.CodeAnalysis.Testing.ProjectState.OutputKind.set -> void +Microsoft.CodeAnalysis.Testing.ProjectState.ProjectState(string name, string language, string defaultPrefix, string defaultExtension) -> void +Microsoft.CodeAnalysis.Testing.ProjectState.ReferenceAssemblies.get -> Microsoft.CodeAnalysis.Testing.ReferenceAssemblies +Microsoft.CodeAnalysis.Testing.ProjectState.ReferenceAssemblies.set -> void Microsoft.CodeAnalysis.Testing.ProjectState.Sources.get -> Microsoft.CodeAnalysis.Testing.SourceFileList Microsoft.CodeAnalysis.Testing.ReferenceAssemblies Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.AddAssemblies(System.Collections.Immutable.ImmutableArray assemblies) -> Microsoft.CodeAnalysis.Testing.ReferenceAssemblies @@ -167,17 +194,13 @@ Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.WithLanguageSpecificAssemblie Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.WithLanguageSpecificAssemblies(string language, System.Collections.Immutable.ImmutableArray assemblies) -> Microsoft.CodeAnalysis.Testing.ReferenceAssemblies Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.WithPackages(System.Collections.Immutable.ImmutableArray packages) -> Microsoft.CodeAnalysis.Testing.ReferenceAssemblies Microsoft.CodeAnalysis.Testing.SolutionState -Microsoft.CodeAnalysis.Testing.SolutionState.AdditionalFiles.get -> Microsoft.CodeAnalysis.Testing.SourceFileCollection -Microsoft.CodeAnalysis.Testing.SolutionState.AdditionalFilesFactories.get -> System.Collections.Generic.List>> -Microsoft.CodeAnalysis.Testing.SolutionState.AdditionalProjects.get -> System.Collections.Generic.List -Microsoft.CodeAnalysis.Testing.SolutionState.AdditionalReferences.get -> Microsoft.CodeAnalysis.Testing.MetadataReferenceCollection +Microsoft.CodeAnalysis.Testing.SolutionState.AdditionalProjects.get -> Microsoft.CodeAnalysis.Testing.ProjectCollection Microsoft.CodeAnalysis.Testing.SolutionState.ExpectedDiagnostics.get -> System.Collections.Generic.List Microsoft.CodeAnalysis.Testing.SolutionState.InheritanceMode.get -> Microsoft.CodeAnalysis.Testing.StateInheritanceMode? Microsoft.CodeAnalysis.Testing.SolutionState.InheritanceMode.set -> void Microsoft.CodeAnalysis.Testing.SolutionState.MarkupHandling.get -> Microsoft.CodeAnalysis.Testing.MarkupMode? Microsoft.CodeAnalysis.Testing.SolutionState.MarkupHandling.set -> void -Microsoft.CodeAnalysis.Testing.SolutionState.SolutionState(string defaultPrefix, string defaultExtension) -> void -Microsoft.CodeAnalysis.Testing.SolutionState.Sources.get -> Microsoft.CodeAnalysis.Testing.SourceFileList +Microsoft.CodeAnalysis.Testing.SolutionState.SolutionState(string name, string language, string defaultPrefix, string defaultExtension) -> void Microsoft.CodeAnalysis.Testing.SolutionState.WithInheritedValuesApplied(Microsoft.CodeAnalysis.Testing.SolutionState baseState, System.Collections.Immutable.ImmutableArray fixableDiagnostics) -> Microsoft.CodeAnalysis.Testing.SolutionState Microsoft.CodeAnalysis.Testing.SolutionState.WithProcessedMarkup(Microsoft.CodeAnalysis.Testing.MarkupOptions markupOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor defaultDiagnostic, System.Collections.Immutable.ImmutableArray supportedDiagnostics, System.Collections.Immutable.ImmutableArray fixableDiagnostics, string defaultPath) -> Microsoft.CodeAnalysis.Testing.SolutionState Microsoft.CodeAnalysis.Testing.SourceFileCollection @@ -202,7 +225,6 @@ abstract Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultFileExt.g abstract Microsoft.CodeAnalysis.Testing.AnalyzerTest.GetDiagnosticAnalyzers() -> System.Collections.Generic.IEnumerable abstract Microsoft.CodeAnalysis.Testing.AnalyzerTest.Language.get -> string abstract Microsoft.CodeAnalysis.Testing.CodeActionTest.SyntaxKindType.get -> System.Type -abstract Microsoft.CodeAnalysis.Testing.ProjectState.Language.get -> string override Microsoft.CodeAnalysis.Testing.DiagnosticResult.ToString() -> string override Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext context) -> void override Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray @@ -287,15 +309,15 @@ static Microsoft.CodeAnalysis.Testing.TestFileMarkupParser.GetSpans(string input static Microsoft.CodeAnalysis.Testing.TestFileMarkupParser.GetSpans(string input, out string output, out System.Collections.Immutable.ImmutableDictionary> spans) -> void static readonly Microsoft.CodeAnalysis.Testing.DiagnosticResult.EmptyDiagnosticResults -> Microsoft.CodeAnalysis.Testing.DiagnosticResult[] virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.ApplyCompilationOptions(Microsoft.CodeAnalysis.Project project) -> Microsoft.CodeAnalysis.Project -virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectImplAsync((string filename, Microsoft.CodeAnalysis.Text.SourceText content)[] sources, (string filename, Microsoft.CodeAnalysis.Text.SourceText content)[] additionalFiles, Microsoft.CodeAnalysis.Testing.ProjectState[] additionalProjects, Microsoft.CodeAnalysis.MetadataReference[] additionalMetadataReferences, string language, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateSolutionAsync(Microsoft.CodeAnalysis.ProjectId projectId, string language, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateProjectImplAsync(Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState primaryProject, System.Collections.Immutable.ImmutableArray additionalProjects, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateSolutionAsync(Microsoft.CodeAnalysis.ProjectId projectId, Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState projectState, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.CreateWorkspace() -> Microsoft.CodeAnalysis.AdhocWorkspace virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultFilePath.get -> string virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultFilePathPrefix.get -> string virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.DefaultTestProjectName.get -> string -virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.IsCompilerDiagnosticIncluded(Microsoft.CodeAnalysis.Diagnostic diagnostic, Microsoft.CodeAnalysis.Testing.CompilerDiagnostics compilerDiagnostics) -> bool virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.GetAnalyzerOptions(Microsoft.CodeAnalysis.Project project) -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.GetDefaultDiagnostic(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer[] analyzers) -> Microsoft.CodeAnalysis.DiagnosticDescriptor +virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.IsCompilerDiagnosticIncluded(Microsoft.CodeAnalysis.Diagnostic diagnostic, Microsoft.CodeAnalysis.Testing.CompilerDiagnostics compilerDiagnostics) -> bool virtual Microsoft.CodeAnalysis.Testing.AnalyzerTest.RunAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task virtual Microsoft.CodeAnalysis.Testing.CodeActionTest.FilterCodeActions(System.Collections.Immutable.ImmutableArray actions) -> System.Collections.Immutable.ImmutableArray virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.CreateMessage(string message) -> string @@ -307,4 +329,4 @@ virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.LanguageIsSupported(strin virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.NotEmpty(string collectionName, System.Collections.Generic.IEnumerable collection) -> void virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.PushContext(string context) -> Microsoft.CodeAnalysis.Testing.IVerifier virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.SequenceEqual(System.Collections.Generic.IEnumerable expected, System.Collections.Generic.IEnumerable actual, System.Collections.Generic.IEqualityComparer equalityComparer = null, string message = null) -> void -virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.True(bool assert, string message = null) -> void \ No newline at end of file +virtual Microsoft.CodeAnalysis.Testing.DefaultVerifier.True(bool assert, string message = null) -> void diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/SolutionState.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/SolutionState.cs index 6d660a7be..21be8da19 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/SolutionState.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing/SolutionState.cs @@ -5,24 +5,17 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; -using System.Text; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Testing { - public class SolutionState + public class SolutionState : ProjectState { - private readonly string _defaultPrefix; - private readonly string _defaultExtension; - - public SolutionState(string defaultPrefix, string defaultExtension) + public SolutionState(string name, string language, string defaultPrefix, string defaultExtension) + : base(name, language, defaultPrefix, defaultExtension) { - _defaultPrefix = defaultPrefix; - _defaultExtension = defaultExtension; - - Sources = new SourceFileList(defaultPrefix, defaultExtension); + AdditionalProjects = new ProjectCollection(language, defaultExtension); } /// @@ -37,23 +30,9 @@ public SolutionState(string defaultPrefix, string defaultExtension) public StateInheritanceMode? InheritanceMode { get; set; } /// - /// Gets the set of source files for analyzer or code fix testing. Files may be added to this list using one of - /// the methods. - /// - public SourceFileList Sources { get; } - - public SourceFileCollection AdditionalFiles { get; } = new SourceFileCollection(); - - public List>> AdditionalFilesFactories { get; } = new List>>(); - - /// - /// Gets a list of additional projects to include in the solution. A will be - /// added from the primary project to each of the additional projects provided here. Markup, additional files, - /// and diagnostics in additional projects are not yet supported. + /// Gets a collection of additional projects to include in the solution. /// - public List AdditionalProjects { get; } = new List(); - - public MetadataReferenceCollection AdditionalReferences { get; } = new MetadataReferenceCollection(); + public ProjectCollection AdditionalProjects { get; } /// /// Gets the list of diagnostics expected in the source(s) and/or additonal files. @@ -92,8 +71,9 @@ public SolutionState(string defaultPrefix, string defaultExtension) /// Applies the using a specified base state. /// /// - /// This method evaluates , and places the resulting additional - /// files in the collection of the result before returning. + /// This method evaluates , and places the resulting + /// additional files in the collection of the result before + /// returning. /// /// The base state to inherit from, or if the current state is /// the root state. @@ -138,9 +118,18 @@ public SolutionState WithInheritedValuesApplied(SolutionState? baseState, Immuta throw new InvalidOperationException("The base state should already have its inheritance state evaluated prior to its use as a base state."); } - var result = new SolutionState(_defaultPrefix, _defaultExtension); + var result = new SolutionState(Name, Language, DefaultPrefix, DefaultExtension); + + result.ReferenceAssemblies = ReferenceAssemblies; + result.OutputKind = OutputKind; + result.DocumentationMode = DocumentationMode; + if (inheritanceMode != StateInheritanceMode.Explicit && baseState != null) { + result.ReferenceAssemblies ??= baseState.ReferenceAssemblies; + result.OutputKind ??= baseState.OutputKind; + result.DocumentationMode ??= baseState.DocumentationMode; + if (Sources.Count == 0) { result.Sources.AddRange(baseState.Sources); @@ -156,6 +145,11 @@ public SolutionState WithInheritedValuesApplied(SolutionState? baseState, Immuta result.AdditionalProjects.AddRange(baseState.AdditionalProjects); } + if (AdditionalProjectReferences.Count == 0) + { + result.AdditionalProjectReferences.AddRange(baseState.AdditionalProjectReferences); + } + if (AdditionalReferences.Count == 0) { result.AdditionalReferences.AddRange(baseState.AdditionalReferences); @@ -179,6 +173,7 @@ public SolutionState WithInheritedValuesApplied(SolutionState? baseState, Immuta result.Sources.AddRange(Sources); result.AdditionalFiles.AddRange(AdditionalFiles); result.AdditionalProjects.AddRange(AdditionalProjects); + result.AdditionalProjectReferences.AddRange(AdditionalProjectReferences); result.AdditionalReferences.AddRange(AdditionalReferences); result.ExpectedDiagnostics.AddRange(ExpectedDiagnostics); result.AdditionalFiles.AddRange(AdditionalFilesFactories.SelectMany(factory => factory())); @@ -245,8 +240,9 @@ private static bool ContentEqual(SourceFileCollection x, SourceFileCollection y) /// /// Processes the markup syntax for this according to the current - /// , and returns a new with the , - /// , and updated accordingly. + /// , and returns a new with the + /// , , and + /// updated accordingly. /// /// Additional options to apply during markup processing. /// The diagnostic descriptor to use for markup spans without an explicit name, @@ -271,17 +267,37 @@ public SolutionState WithProcessedMarkup(MarkupOptions markupOptions, Diagnostic (var expected, var testSources) = ProcessMarkupSources(Sources, ExpectedDiagnostics, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath); var (additionalExpected, additionalFiles) = ProcessMarkupSources(AdditionalFiles.Concat(AdditionalFilesFactories.SelectMany(factory => factory())), expected, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath); + var result = new SolutionState(Name, Language, DefaultPrefix, DefaultExtension); + result.MarkupHandling = MarkupMode.None; + result.InheritanceMode = StateInheritanceMode.Explicit; + result.ReferenceAssemblies = ReferenceAssemblies; + result.OutputKind = OutputKind; + result.DocumentationMode = DocumentationMode; + result.Sources.AddRange(testSources); + result.AdditionalFiles.AddRange(additionalFiles); + + foreach (var (projectName, projectState) in AdditionalProjects) + { + var (correctedIntermediateDiagnostics, additionalProjectSources) = ProcessMarkupSources(projectState.Sources, additionalExpected, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath); + var (correctedDiagnostics, additionalProjectAdditionalFiles) = ProcessMarkupSources(projectState.AdditionalFiles.Concat(projectState.AdditionalFilesFactories.SelectMany(factory => factory())), correctedIntermediateDiagnostics, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath); + + var processedProjectState = new ProjectState(projectState); + processedProjectState.Sources.Clear(); + processedProjectState.Sources.AddRange(additionalProjectSources); + processedProjectState.AdditionalFiles.Clear(); + processedProjectState.AdditionalFilesFactories.Clear(); + processedProjectState.AdditionalFiles.AddRange(additionalProjectAdditionalFiles); + + result.AdditionalProjects.Add(projectName, processedProjectState); + additionalExpected = correctedDiagnostics; + } + for (var i = 0; i < additionalExpected.Length; i++) { additionalExpected[i] = additionalExpected[i].WithAppliedMarkupLocations(markupLocations); } - var result = new SolutionState(_defaultPrefix, _defaultExtension); - result.MarkupHandling = MarkupMode.None; - result.InheritanceMode = StateInheritanceMode.Explicit; - result.Sources.AddRange(testSources); - result.AdditionalFiles.AddRange(additionalFiles); - result.AdditionalProjects.AddRange(AdditionalProjects); + result.AdditionalProjectReferences.AddRange(AdditionalProjectReferences); result.AdditionalReferences.AddRange(AdditionalReferences); result.ExpectedDiagnostics.AddRange(additionalExpected); return result; diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CSharp.Analyzer.Testing/CSharpProjectState.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CSharp.Analyzer.Testing/CSharpProjectState.cs deleted file mode 100644 index 085d43e42..000000000 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CSharp.Analyzer.Testing/CSharpProjectState.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -using Microsoft.CodeAnalysis.Testing; - -namespace Microsoft.CodeAnalysis.CSharp.Testing -{ - public class CSharpProjectState : ProjectState - { - public CSharpProjectState(string name) - : base(name, defaultPrefix: "Test", defaultExtension: "cs") - { - } - - public override string Language => LanguageNames.CSharp; - } -} diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CSharp.Analyzer.Testing/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CSharp.Analyzer.Testing/PublicAPI.Unshipped.txt index 8e0aee21e..63f1d4430 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CSharp.Analyzer.Testing/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CSharp.Analyzer.Testing/PublicAPI.Unshipped.txt @@ -2,11 +2,8 @@ Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest.CSharpAnalyzerTest() -> void Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier.CSharpAnalyzerVerifier() -> void -Microsoft.CodeAnalysis.CSharp.Testing.CSharpProjectState -Microsoft.CodeAnalysis.CSharp.Testing.CSharpProjectState.CSharpProjectState(string name) -> void override Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest.CreateCompilationOptions() -> Microsoft.CodeAnalysis.CompilationOptions override Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest.CreateParseOptions() -> Microsoft.CodeAnalysis.ParseOptions override Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest.DefaultFileExt.get -> string override Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest.GetDiagnosticAnalyzers() -> System.Collections.Generic.IEnumerable override Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerTest.Language.get -> string -override Microsoft.CodeAnalysis.CSharp.Testing.CSharpProjectState.Language.get -> string diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs index c78ce6f64..adc7a363d 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing/CodeFixTest`1.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; +using Microsoft.CodeAnalysis.Testing.Model; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Testing @@ -187,8 +188,8 @@ public CodeFixValidationMode CodeFixValidationMode protected CodeFixTest() { - FixedState = new SolutionState(DefaultFilePathPrefix, DefaultFileExt); - BatchFixedState = new SolutionState(DefaultFilePathPrefix, DefaultFileExt); + FixedState = new SolutionState(DefaultTestProjectName, Language, DefaultFilePathPrefix, DefaultFileExt); + BatchFixedState = new SolutionState(DefaultTestProjectName, Language, DefaultFilePathPrefix, DefaultFileExt); } /// @@ -234,14 +235,14 @@ public override async Task RunAsync(CancellationToken cancellationToken = defaul var allowFixAll = (CodeFixTestBehaviors & CodeFixTestBehaviors.SkipFixAllCheck) != CodeFixTestBehaviors.SkipFixAllCheck; - await VerifyDiagnosticsAsync(testState.Sources.ToArray(), testState.AdditionalFiles.ToArray(), testState.AdditionalProjects.ToArray(), testState.AdditionalReferences.ToArray(), testState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of test state"), cancellationToken).ConfigureAwait(false); + await VerifyDiagnosticsAsync(new EvaluatedProjectState(testState, ReferenceAssemblies), testState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), testState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of test state"), cancellationToken).ConfigureAwait(false); if (CodeFixExpected()) { - await VerifyDiagnosticsAsync(fixedState.Sources.ToArray(), fixedState.AdditionalFiles.ToArray(), fixedState.AdditionalProjects.ToArray(), fixedState.AdditionalReferences.ToArray(), fixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false); + await VerifyDiagnosticsAsync(new EvaluatedProjectState(fixedState, ReferenceAssemblies), fixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), fixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false); if (allowFixAll && CodeActionExpected(BatchFixedState)) { - await VerifyDiagnosticsAsync(batchFixedState.Sources.ToArray(), batchFixedState.AdditionalFiles.ToArray(), batchFixedState.AdditionalProjects.ToArray(), batchFixedState.AdditionalReferences.ToArray(), batchFixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of batch fixed state"), cancellationToken).ConfigureAwait(false); + await VerifyDiagnosticsAsync(new EvaluatedProjectState(batchFixedState, ReferenceAssemblies), batchFixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), batchFixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of batch fixed state"), cancellationToken).ConfigureAwait(false); } await VerifyFixAsync(testState, fixedState, batchFixedState, Verify, cancellationToken).ConfigureAwait(false); @@ -397,7 +398,7 @@ private async Task VerifyFixAsync( IVerifier verifier, CancellationToken cancellationToken) { - var project = await CreateProjectAsync(oldState.Sources.ToArray(), oldState.AdditionalFiles.ToArray(), oldState.AdditionalProjects.ToArray(), oldState.AdditionalReferences.ToArray(), language, cancellationToken); + var project = await CreateProjectAsync(new EvaluatedProjectState(oldState, ReferenceAssemblies), oldState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), cancellationToken); var compilerDiagnostics = await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); ExceptionDispatchInfo? iterationCountFailure; diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing/CodeRefactoringTest`1.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing/CodeRefactoringTest`1.cs index 414ddbfed..734a461ad 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing/CodeRefactoringTest`1.cs +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing/CodeRefactoringTest`1.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; +using Microsoft.CodeAnalysis.Testing.Model; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Testing @@ -51,7 +52,7 @@ public string FixedCode protected CodeRefactoringTest() { - FixedState = new SolutionState(DefaultFilePathPrefix, DefaultFileExt); + FixedState = new SolutionState(DefaultTestProjectName, Language, DefaultFilePathPrefix, DefaultFileExt); } protected override IEnumerable GetDiagnosticAnalyzers() @@ -78,11 +79,11 @@ public override async Task RunAsync(CancellationToken cancellationToken = defaul var testState = rawTestState.WithProcessedMarkup(MarkupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, DefaultFilePath); var fixedState = rawFixedState.WithProcessedMarkup(MarkupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, DefaultFilePath); - await VerifyDiagnosticsAsync(testState.Sources.ToArray(), testState.AdditionalFiles.ToArray(), testState.AdditionalProjects.ToArray(), testState.AdditionalReferences.ToArray(), FilterTriggerSpanResults(testState.ExpectedDiagnostics).ToArray(), Verify.PushContext("Diagnostics of test state"), cancellationToken).ConfigureAwait(false); + await VerifyDiagnosticsAsync(new EvaluatedProjectState(testState, ReferenceAssemblies), testState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), FilterTriggerSpanResults(testState.ExpectedDiagnostics).ToArray(), Verify.PushContext("Diagnostics of test state"), cancellationToken).ConfigureAwait(false); if (CodeActionExpected()) { - await VerifyDiagnosticsAsync(fixedState.Sources.ToArray(), fixedState.AdditionalFiles.ToArray(), fixedState.AdditionalProjects.ToArray(), fixedState.AdditionalReferences.ToArray(), FilterTriggerSpanResults(fixedState.ExpectedDiagnostics).ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false); + await VerifyDiagnosticsAsync(new EvaluatedProjectState(fixedState, ReferenceAssemblies), fixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), FilterTriggerSpanResults(fixedState.ExpectedDiagnostics).ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false); await VerifyRefactoringAsync(testState, fixedState, GetTriggerSpanResult(testState.ExpectedDiagnostics), Verify, cancellationToken).ConfigureAwait(false); } @@ -149,7 +150,7 @@ private async Task VerifyRefactoringAsync( IVerifier verifier, CancellationToken cancellationToken) { - var project = await CreateProjectAsync(oldState.Sources.ToArray(), oldState.AdditionalFiles.ToArray(), oldState.AdditionalProjects.ToArray(), oldState.AdditionalReferences.ToArray(), language, cancellationToken); + var project = await CreateProjectAsync(new EvaluatedProjectState(oldState, ReferenceAssemblies), oldState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), cancellationToken); _ = await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); ExceptionDispatchInfo? iterationCountFailure; diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing/PublicAPI.Unshipped.txt index 78a1f8892..dee8d5b39 100644 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing/PublicAPI.Unshipped.txt +++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing/PublicAPI.Unshipped.txt @@ -2,11 +2,8 @@ Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerTest(Of TAnalyzer, Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerTest(Of TAnalyzer, TVerifier).New() -> Void Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerVerifier(Of TAnalyzer, TVerifier) Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerVerifier(Of TAnalyzer, TVerifier).New() -> Void -Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicProjectState -Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicProjectState.New(name As String) -> Void Overrides Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerTest(Of TAnalyzer, TVerifier).CreateCompilationOptions() -> Microsoft.CodeAnalysis.CompilationOptions Overrides Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerTest(Of TAnalyzer, TVerifier).CreateParseOptions() -> Microsoft.CodeAnalysis.ParseOptions Overrides Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerTest(Of TAnalyzer, TVerifier).DefaultFileExt() -> String Overrides Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerTest(Of TAnalyzer, TVerifier).GetDiagnosticAnalyzers() -> System.Collections.Generic.IEnumerable(Of Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer) Overrides Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicAnalyzerTest(Of TAnalyzer, TVerifier).Language() -> String -Overrides Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicProjectState.Language() -> String diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing/VisualBasicProjectState.vb b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing/VisualBasicProjectState.vb deleted file mode 100644 index 4bc69cf3f..000000000 --- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing/VisualBasicProjectState.vb +++ /dev/null @@ -1,17 +0,0 @@ -' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -Imports Microsoft.CodeAnalysis.Testing - -Public Class VisualBasicProjectState - Inherits ProjectState - - Public Sub New(name As String) - MyBase.New(name, defaultPrefix:="Test", defaultExtension:="vb") - End Sub - - Public Overrides ReadOnly Property Language As String - Get - Return LanguageNames.VisualBasic - End Get - End Property -End Class diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AdditionalFilesTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AdditionalFilesTests.cs index 75b470b84..6c7478078 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AdditionalFilesTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AdditionalFilesTests.cs @@ -3,20 +3,19 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; using Xunit; +using CSharpTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpAnalyzerTest< + Microsoft.CodeAnalysis.Testing.TestAnalyzers.HighlightBracesAnalyzer>; namespace Microsoft.CodeAnalysis.Testing { public class AdditionalFilesTests { + private static DiagnosticResult Diagnostic() + => AnalyzerVerifier.Diagnostic(); + [Fact] public async Task TestDiagnosticInNormalFile() { @@ -25,7 +24,7 @@ public async Task TestDiagnosticInNormalFile() TestState = { Sources = { "namespace MyNamespace { }" }, - ExpectedDiagnostics = { new DiagnosticResult(HighlightBracesAnalyzer.Descriptor).WithLocation(1, 23) }, + ExpectedDiagnostics = { Diagnostic().WithLocation(1, 23) }, AdditionalFiles = { ("File1.txt", "Content without braces"), @@ -70,7 +69,7 @@ public async Task TestDiagnosticInAdditionalFile() TestState = { Sources = { "[assembly: System.Reflection.AssemblyVersion(\"1.0.0.0\")]" }, - ExpectedDiagnostics = { new DiagnosticResult(HighlightBracesAnalyzer.Descriptor).WithSpan("File1.txt", 1, 14, 1, 15) }, + ExpectedDiagnostics = { Diagnostic().WithSpan("File1.txt", 1, 14, 1, 15) }, AdditionalFiles = { ("File1.txt", "Content with { braces }"), @@ -87,7 +86,7 @@ public async Task TestDiagnosticInAdditionalFileWithCombinedSyntax() TestState = { Sources = { "[assembly: System.Reflection.AssemblyVersion(\"1.0.0.0\")]" }, - ExpectedDiagnostics = { new DiagnosticResult(HighlightBracesAnalyzer.Descriptor).WithLocation(0) }, + ExpectedDiagnostics = { Diagnostic().WithLocation(0) }, AdditionalFiles = { ("File1.txt", "Content with {|#0:{|} braces }"), @@ -106,7 +105,7 @@ public async Task TestDiagnosticInAdditionalFileWithCombinedSyntaxDuplicate() TestState = { Sources = { "[assembly: System.Reflection.AssemblyVersion{|#0:(|}\"1.0.0.0\")]" }, - ExpectedDiagnostics = { new DiagnosticResult(HighlightBracesAnalyzer.Descriptor).WithLocation(0) }, + ExpectedDiagnostics = { Diagnostic().WithLocation(0) }, AdditionalFiles = { ("File1.txt", "Content with {|#0:{|} braces }"), @@ -129,7 +128,7 @@ public async Task TestDiagnosticInAdditionalFileWithCombinedSyntaxMismatch() TestState = { Sources = { "[assembly: System.Reflection.AssemblyVersion(\"1.0.0.0\")]" }, - ExpectedDiagnostics = { new DiagnosticResult(HighlightBracesAnalyzer.Descriptor).WithLocation(0) }, + ExpectedDiagnostics = { Diagnostic().WithLocation(0) }, AdditionalFiles = { ("File1.txt", "Content with {|#1:{|} braces }"), @@ -194,7 +193,7 @@ public async Task TestDiagnosticInAdditionalFileBraceNotTreatedAsMarkup() TestState = { Sources = { "[assembly: System.Reflection.AssemblyVersion(\"1.0.0.0\")]" }, - ExpectedDiagnostics = { new DiagnosticResult(HighlightBracesAnalyzer.Descriptor).WithSpan("File1.txt", 1, 14, 1, 15) }, + ExpectedDiagnostics = { Diagnostic().WithSpan("File1.txt", 1, 14, 1, 15) }, AdditionalFiles = { ("File1.txt", "Content with {|Literal:text|}"), @@ -203,69 +202,5 @@ public async Task TestDiagnosticInAdditionalFileBraceNotTreatedAsMarkup() }, }.RunAsync(); } - - [DiagnosticAnalyzer(LanguageNames.CSharp)] - private class HighlightBracesAnalyzer : DiagnosticAnalyzer - { - internal static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("Brace", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - - context.RegisterSyntaxTreeAction(HandleSyntaxTree); - context.RegisterCompilationAction(HandleCompilation); - } - - private void HandleCompilation(CompilationAnalysisContext context) - { - foreach (var file in context.Options.AdditionalFiles) - { - var sourceText = file.GetText(context.CancellationToken); - var text = sourceText.ToString(); - for (var i = text.IndexOf('{'); i >= 0; i = text.IndexOf('{', i + 1)) - { - var textSpan = new TextSpan(i, 1); - var lineSpan = sourceText.Lines.GetLinePositionSpan(textSpan); - context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(file.Path, textSpan, lineSpan))); - } - } - } - - private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) - { - foreach (var token in context.Tree.GetRoot(context.CancellationToken).DescendantTokens()) - { - if (!token.IsKind(SyntaxKind.OpenBraceToken)) - { - continue; - } - - context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation())); - } - } - } - - private class CSharpTest : AnalyzerTest - { - public override string Language => LanguageNames.CSharp; - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - => new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - protected override ParseOptions CreateParseOptions() - => new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - - protected override IEnumerable GetDiagnosticAnalyzers() - { - yield return new HighlightBracesAnalyzer(); - } - } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AnalyzerTestTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AnalyzerTestTests.cs index 2b4908769..da1a0426d 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AnalyzerTestTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AnalyzerTestTests.cs @@ -2,11 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Xunit; +using CSharpTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpAnalyzerTest< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer>; namespace Microsoft.CodeAnalysis.Testing { @@ -52,23 +51,5 @@ public void TestSetTestCodeTwice() Assert.Null(test.TestState.Sources[1].content.Encoding); Assert.Equal(SourceHashAlgorithm.Sha1, test.TestState.Sources[1].content.ChecksumAlgorithm); } - - private class CSharpTest : AnalyzerTest - { - public override string Language => LanguageNames.CSharp; - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - => new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - protected override ParseOptions CreateParseOptions() - => new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - - protected override IEnumerable GetDiagnosticAnalyzers() - { - yield return new NoActionAnalyzer(); - } - } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AutoExclusionTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AutoExclusionTests.cs index fd05a9f8c..570eef6f8 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AutoExclusionTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/AutoExclusionTests.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -313,7 +314,7 @@ private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) } } - private class CSharpReplaceThisWithBaseTest : AnalyzerTest + private class CSharpReplaceThisWithBaseTest : CSharpAnalyzerTest { private readonly GeneratedCodeAnalysisFlags? _generatedCodeAnalysisFlags; @@ -322,27 +323,13 @@ public CSharpReplaceThisWithBaseTest(GeneratedCodeAnalysisFlags? generatedCodeAn _generatedCodeAnalysisFlags = generatedCodeAnalysisFlags; } - public override string Language => LanguageNames.CSharp; - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - { - return new CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - } - - protected override ParseOptions CreateParseOptions() - { - return new CSharp.CSharpParseOptions(CSharp.LanguageVersion.Default, DocumentationMode.Diagnose); - } - protected override IEnumerable GetDiagnosticAnalyzers() { yield return new ReplaceThisWithBaseAnalyzer(_generatedCodeAnalysisFlags); } } - private class VisualBasicReplaceThisWithBaseTest : AnalyzerTest + private class VisualBasicReplaceThisWithBaseTest : VisualBasicAnalyzerTest { private readonly GeneratedCodeAnalysisFlags? _generatedCodeAnalysisFlags; @@ -351,20 +338,6 @@ public VisualBasicReplaceThisWithBaseTest(GeneratedCodeAnalysisFlags? generatedC _generatedCodeAnalysisFlags = generatedCodeAnalysisFlags; } - public override string Language => LanguageNames.VisualBasic; - - protected override string DefaultFileExt => "vb"; - - protected override CompilationOptions CreateCompilationOptions() - { - return new VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - } - - protected override ParseOptions CreateParseOptions() - { - return new VisualBasic.VisualBasicParseOptions(VisualBasic.LanguageVersion.Default, DocumentationMode.Diagnose); - } - protected override IEnumerable GetDiagnosticAnalyzers() { yield return new ReplaceThisWithBaseAnalyzer(_generatedCodeAnalysisFlags); diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs index 7868f36f9..3337e36b6 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CompilerErrorTests.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; using Xunit; +using CSharpTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpAnalyzerTest< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer>; +using VisualBasicTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.VisualBasicAnalyzerTest< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer>; namespace Microsoft.CodeAnalysis.Testing { @@ -456,40 +458,22 @@ class TestClass { }.RunAsync(); } - private class CSharpTest : AnalyzerTest - { - public override string Language => LanguageNames.CSharp; - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - => new CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - protected override ParseOptions CreateParseOptions() - => new CSharp.CSharpParseOptions(CSharp.LanguageVersion.Default, DocumentationMode.Diagnose); - - protected override IEnumerable GetDiagnosticAnalyzers() - { - yield return new NoActionAnalyzer(); - } - } - - private class VisualBasicTest : AnalyzerTest +#if !(NETCOREAPP1_1 || NET46) + [Fact] + public async Task TestTopLevelStatements() { - public override string Language => LanguageNames.VisualBasic; - - protected override string DefaultFileExt => "vb"; - - protected override CompilationOptions CreateCompilationOptions() - => new VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - protected override ParseOptions CreateParseOptions() - => new VisualBasic.VisualBasicParseOptions(VisualBasic.LanguageVersion.Default, DocumentationMode.Diagnose); - - protected override IEnumerable GetDiagnosticAnalyzers() + await new CSharpTest { - yield return new NoActionAnalyzer(); - } + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + Sources = + { + @"return 0;", + }, + }, + }.RunAsync(); } +#endif } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs index acc1d42dd..bee70d675 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticLocationTests.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Immutable; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -14,6 +14,10 @@ namespace Microsoft.CodeAnalysis.Testing { public class DiagnosticLocationTests { + private static DiagnosticResult Diagnostic() + where TAnalyzer : DiagnosticAnalyzer, new() + => AnalyzerVerifier, DefaultVerifier>.Diagnostic(); + [Fact] public async Task TestDiagnosticMatchesCorrectSpan() { @@ -111,7 +115,7 @@ public async Task TestDiagnosticDoesNotMatchMissingLocation() await new CSharpAnalyzerTest { TestCode = @"class TestClass { }", - ExpectedDiagnostics = { new DiagnosticResult(HighlightBraceAnalyzer.Descriptor) }, + ExpectedDiagnostics = { Diagnostic() }, }.RunAsync(); }); @@ -137,7 +141,7 @@ public async Task TestDiagnosticDoesNotMatchNoLocation() await new CSharpAnalyzerTest { TestCode = @"class TestClass { }", - ExpectedDiagnostics = { new DiagnosticResult(HighlightBraceAnalyzer.Descriptor).WithNoLocation() }, + ExpectedDiagnostics = { Diagnostic().WithNoLocation() }, }.RunAsync(); }); @@ -223,53 +227,22 @@ public async Task TestZeroWidthDiagnosticDoesNotMatchIncorrectLocation() new DefaultVerifier().EqualOrDiff(expected, exception.Message); } - private abstract class HighlightBraceAnalyzer : DiagnosticAnalyzer - { - internal static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("Brace", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - - context.RegisterSyntaxTreeAction(HandleSyntaxTree); - } - - private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) - { - foreach (var token in context.Tree.GetRoot(context.CancellationToken).DescendantTokens()) - { - if (!token.IsKind(SyntaxKind.OpenBraceToken)) - { - continue; - } - - context.ReportDiagnostic(CreateDiagnostic(token)); - } - } - - protected abstract Diagnostic CreateDiagnostic(SyntaxToken token); - } - [DiagnosticAnalyzer(LanguageNames.CSharp)] - private class HighlightBracePositionAnalyzer : HighlightBraceAnalyzer + private class HighlightBracePositionAnalyzer : AbstractHighlightBracesAnalyzer { protected override Diagnostic CreateDiagnostic(SyntaxToken token) { var location = token.GetLocation(); - return Diagnostic.Create(Descriptor, Location.Create(location.SourceTree, new TextSpan(location.SourceSpan.Start, 0))); + return CodeAnalysis.Diagnostic.Create(Descriptor, Location.Create(location.SourceTree!, new TextSpan(location.SourceSpan.Start, 0))); } } [DiagnosticAnalyzer(LanguageNames.CSharp)] - private class HighlightBraceSpanAnalyzer : HighlightBraceAnalyzer + private class HighlightBraceSpanAnalyzer : AbstractHighlightBracesAnalyzer { protected override Diagnostic CreateDiagnostic(SyntaxToken token) { - return Diagnostic.Create(Descriptor, token.GetLocation()); + return CodeAnalysis.Diagnostic.Create(Descriptor, token.GetLocation()); } } @@ -291,7 +264,7 @@ public override void Initialize(AnalysisContext context) private void HandleCompilation(CompilationAnalysisContext context) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, location: null)); + context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(Descriptor, location: null)); } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticResultTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticResultTests.cs index 57176e3ae..48dda99b7 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticResultTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/DiagnosticResultTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; using Microsoft.CodeAnalysis.Text; using Xunit; diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MarkupTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MarkupTests.cs index 14c83976d..1951a6c05 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MarkupTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MarkupTests.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; using Xunit; namespace Microsoft.CodeAnalysis.Testing @@ -270,12 +271,12 @@ private IEnumerable GetAdditionalLocations(SyntaxToken token) { if (_reportAdditionalLocations) { - yield return token.Parent.ChildTokens().Single(t => t.IsKind(SyntaxKind.CloseBraceToken)).GetLocation(); + yield return token.Parent!.ChildTokens().Single(t => t.IsKind(SyntaxKind.CloseBraceToken)).GetLocation(); } } } - private class CSharpTest : AnalyzerTest + private class CSharpTest : CSharpAnalyzerTest { private readonly bool _nestedDiagnostics; private readonly bool _hiddenDescriptors; @@ -288,16 +289,6 @@ public CSharpTest(bool nestedDiagnostics, bool hiddenDescriptors, bool reportAdd _reportAdditionalLocations = reportAdditionalLocations; } - public override string Language => LanguageNames.CSharp; - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - => new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - protected override ParseOptions CreateParseOptions() - => new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - protected override IEnumerable GetDiagnosticAnalyzers() { yield return new HighlightBracesAnalyzer(_nestedDiagnostics, _hiddenDescriptors, _reportAdditionalLocations); diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MetadataReferenceTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MetadataReferenceTests.cs index d71968ec2..70f0a47d5 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MetadataReferenceTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MetadataReferenceTests.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; using Xunit; +using CSharpTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpAnalyzerTest< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer>; namespace Microsoft.CodeAnalysis.Testing { @@ -514,23 +513,5 @@ internal static ReferenceAssemblies ReferenceAssembliesForTargetFramework(string _ => throw new NotSupportedException($"Target framework '{targetFramework}' is not currently supported."), }; } - - private class CSharpTest : AnalyzerTest - { - public override string Language => LanguageNames.CSharp; - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - => new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - protected override ParseOptions CreateParseOptions() - => new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - - protected override IEnumerable GetDiagnosticAnalyzers() - { - yield return new NoActionAnalyzer(); - } - } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MultipleProjectsTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MultipleProjectsTests.cs new file mode 100644 index 000000000..0ecfcd1e9 --- /dev/null +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/MultipleProjectsTests.cs @@ -0,0 +1,192 @@ +// 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. + +using System; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; +using Xunit; +using CSharpTest = Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpAnalyzerTest< + Microsoft.CodeAnalysis.Testing.TestAnalyzers.HighlightBracesAnalyzer>; +using VerifyCS = Microsoft.CodeAnalysis.Testing.AnalyzerVerifier< + Microsoft.CodeAnalysis.Testing.TestAnalyzers.HighlightBracesAnalyzer, + Microsoft.CodeAnalysis.Testing.TestAnalyzers.CSharpAnalyzerTest, + Microsoft.CodeAnalysis.Testing.DefaultVerifier>; + +namespace Microsoft.CodeAnalysis.Testing +{ + public class MultipleProjectsTests + { + [Fact] + public async Task TwoCSharpProjects_Independent() + { + await new CSharpTest + { + TestState = + { + Sources = + { + @"public class Derived1 : {|CS0246:Base2|} [|{|] }", + @"public class Base1 [|{|] }", + }, + AdditionalProjects = + { + ["Secondary"] = + { + Sources = + { + @"public class Derived2 : {|CS0246:Base1|} [|{|] }", + @"public class Base2 [|{|] }", + }, + }, + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task TwoCSharpProjects_IndependentWithMarkupLocations() + { + await new CSharpTest + { + TestState = + { + Sources = + { + @"public class Derived1 : {|#0:Base2|} {|#1:{|} }", + @"public class Base1 {|#2:{|} }", + }, + AdditionalProjects = + { + ["Secondary"] = + { + Sources = + { + @"public class Derived2 : {|#3:Base1|} {|#4:{|} }", + @"public class Base2 {|#5:{|} }", + }, + }, + }, + ExpectedDiagnostics = + { + // /0/Test0.cs(1,25): error CS0246: The type or namespace name 'Base2' could not be found (are you missing a using directive or an assembly reference?) + DiagnosticResult.CompilerError("CS0246").WithLocation(0).WithArguments("Base2"), + + // /0/Test0.cs(1,31): warning Brace: message + VerifyCS.Diagnostic().WithLocation(1), + + // /0/Test1.cs(1,20): warning Brace: message + VerifyCS.Diagnostic().WithLocation(2), + + // /Secondary/Test0.cs(1,25): error CS0246: The type or namespace name 'Base1' could not be found (are you missing a using directive or an assembly reference?) + DiagnosticResult.CompilerError("CS0246").WithLocation(3).WithArguments("Base1"), + + // /Secondary/Test0.cs(1,31): warning Brace: message + VerifyCS.Diagnostic().WithLocation(4), + + // /Secondary/Test1.cs(1,20): warning Brace: message + VerifyCS.Diagnostic().WithLocation(5), + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task TwoCSharpProjects_PrimaryReferencesSecondary() + { + // TestProject references Secondary + await new CSharpTest + { + TestState = + { + Sources = + { + @"public class Derived1 : Base2 [|{|] }", + @"public class Base1 [|{|] }", + }, + AdditionalProjectReferences = { "Secondary", }, + AdditionalProjects = + { + ["Secondary"] = + { + Sources = + { + @"public class Derived2 : {|CS0246:Base1|} [|{|] }", + @"public class Base2 [|{|] }", + }, + }, + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task TwoCSharpProjects_SecondaryReferencesPrimary() + { + // Secondary references TestProject + await new CSharpTest + { + TestState = + { + Sources = + { + @"public class Derived1 : {|CS0246:Base2|} [|{|] }", + @"public class Base1 [|{|] }", + }, + AdditionalProjects = + { + ["Secondary"] = + { + Sources = + { + @"public class Derived2 : Base1 [|{|] }", + @"public class Base2 [|{|] }", + }, + AdditionalProjectReferences = { "TestProject" }, + }, + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task TwoCSharpProjects_DefaultPaths() + { + var exception = await Assert.ThrowsAsync(async () => + { + await new CSharpAnalyzerTest + { + TestState = + { + Sources = + { + @"public class Derived1 : Base1 { }", + }, + AdditionalProjects = + { + ["Secondary"] = + { + Sources = + { + @"public class Derived2 : Base2 { }", + }, + }, + }, + }, + }.RunAsync(); + }); + + var expected = + "Mismatch between number of diagnostics returned, expected \"0\" actual \"2\"" + Environment.NewLine + + Environment.NewLine + + "Diagnostics:" + Environment.NewLine + + "// /0/Test0.cs(1,25): error CS0246: The type or namespace name 'Base1' could not be found (are you missing a using directive or an assembly reference?)" + Environment.NewLine + + "DiagnosticResult.CompilerError(\"CS0246\").WithSpan(1, 25, 1, 30).WithArguments(\"Base1\")," + Environment.NewLine + + "// /Secondary/Test0.cs(1,25): error CS0246: The type or namespace name 'Base2' could not be found (are you missing a using directive or an assembly reference?)" + Environment.NewLine + + "DiagnosticResult.CompilerError(\"CS0246\").WithSpan(\"/Secondary/Test0.cs\", 1, 25, 1, 30).WithArguments(\"Base2\")," + Environment.NewLine + + Environment.NewLine; + + new DefaultVerifier().EqualOrDiff(expected, exception.Message); + } + } +} diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/NoActionAnalyzer.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/NoActionAnalyzer.cs deleted file mode 100644 index 8527e311f..000000000 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/NoActionAnalyzer.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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. - -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.Testing -{ - [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] - internal sealed class NoActionAnalyzer : DiagnosticAnalyzer - { - public override ImmutableArray SupportedDiagnostics - => ImmutableArray.Empty; - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - } - } -} diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CSharpAnalyzerTest`1.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/TestAnalyzers/CSharpAnalyzerTest`1.cs similarity index 76% rename from tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CSharpAnalyzerTest`1.cs rename to tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/TestAnalyzers/CSharpAnalyzerTest`1.cs index 6cb3d43e2..822116e8b 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/CSharpAnalyzerTest`1.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/TestAnalyzers/CSharpAnalyzerTest`1.cs @@ -6,20 +6,20 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.Testing +namespace Microsoft.CodeAnalysis.Testing.TestAnalyzers { internal class CSharpAnalyzerTest : AnalyzerTest where TAnalyzer : DiagnosticAnalyzer, new() { - public override string Language => LanguageNames.CSharp; + public sealed override string Language => LanguageNames.CSharp; - protected override string DefaultFileExt => "cs"; + protected sealed override string DefaultFileExt => "cs"; protected override CompilationOptions CreateCompilationOptions() => new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); protected override ParseOptions CreateParseOptions() - => new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); + => new CSharpParseOptions(LanguageVersion.Default); protected override IEnumerable GetDiagnosticAnalyzers() { diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/VisualBasicAnalyzerTest`1.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/TestAnalyzers/VisualBasicAnalyzerTest`1.cs similarity index 81% rename from tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/VisualBasicAnalyzerTest`1.cs rename to tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/TestAnalyzers/VisualBasicAnalyzerTest`1.cs index 5f733dc98..9aa7129ff 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/VisualBasicAnalyzerTest`1.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Analyzer.Testing.UnitTests/TestAnalyzers/VisualBasicAnalyzerTest`1.cs @@ -6,20 +6,20 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.VisualBasic; -namespace Microsoft.CodeAnalysis.Testing +namespace Microsoft.CodeAnalysis.Testing.TestAnalyzers { internal class VisualBasicAnalyzerTest : AnalyzerTest where TAnalyzer : DiagnosticAnalyzer, new() { - public override string Language => LanguageNames.VisualBasic; + public sealed override string Language => LanguageNames.VisualBasic; - protected override string DefaultFileExt => "vb"; + protected sealed override string DefaultFileExt => "vb"; protected override CompilationOptions CreateCompilationOptions() => new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary); protected override ParseOptions CreateParseOptions() - => new VisualBasicParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); + => new VisualBasicParseOptions(LanguageVersion.Default); protected override IEnumerable GetDiagnosticAnalyzers() { diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/AdditionalFilesFixTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/AdditionalFilesFixTests.cs index 398f41b49..06ed976e2 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/AdditionalFilesFixTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/AdditionalFilesFixTests.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestFixes; using Xunit; namespace Microsoft.CodeAnalysis.Testing @@ -353,10 +354,10 @@ private static Task CreateChangedSolution(Document document, bool remo private class FixAll : FixAllProvider { - public override Task GetFixAsync(FixAllContext fixAllContext) + public override Task GetFixAsync(FixAllContext fixAllContext) { var hasAdditionalFiles = fixAllContext.Solution.Projects.Single().AdditionalDocumentIds.Count > 0; - return Task.FromResult(CodeAction.Create( + return Task.FromResult(CodeAction.Create( "ToggleFile", ct => CreateChangedSolution(fixAllContext.Solution.Projects.Single().Documents.First(), remove: hasAdditionalFiles, ct), nameof(ToggleAdditionalFileFix))); @@ -364,7 +365,7 @@ public override Task GetFixAsync(FixAllContext fixAllContext) } } - private class CSharpTest : CodeFixTest + private class CSharpTest : CSharpCodeFixTest { private readonly SuppressDiagnosticIf _suppressDiagnosticIf; @@ -373,27 +374,10 @@ public CSharpTest(SuppressDiagnosticIf suppressDiagnosticIf) _suppressDiagnosticIf = suppressDiagnosticIf; } - public override string Language => LanguageNames.CSharp; - - public override Type SyntaxKindType => typeof(SyntaxKind); - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - => new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - - protected override ParseOptions CreateParseOptions() - => new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - protected override IEnumerable GetDiagnosticAnalyzers() { yield return new HighlightBracesAnalyzer(_suppressDiagnosticIf); } - - protected override IEnumerable GetCodeFixProviders() - { - yield return new ToggleAdditionalFileFix(); - } } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixIterationTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixIterationTests.cs index 62404ace4..43704cacf 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixIterationTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixIterationTests.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestFixes; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -456,7 +457,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; var root = await tree.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); var replacement = int.Parse(token.ValueText) + 1; @@ -465,14 +466,8 @@ private async Task CreateChangedDocument(Document document, TextSpan s } } - private class CSharpTest : CodeFixTest + private class CSharpTest : CSharpCodeFixTest { - public override string Language => LanguageNames.CSharp; - - public override Type SyntaxKindType => typeof(SyntaxKind); - - protected override string DefaultFileExt => "cs"; - public int DiagnosticIndexToFix { get; set; } public CSharpTest() @@ -484,26 +479,6 @@ public CSharpTest() { return fixableDiagnostics[DiagnosticIndexToFix]; } - - protected override CompilationOptions CreateCompilationOptions() - { - return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - } - - protected override ParseOptions CreateParseOptions() - { - return new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - } - - protected override IEnumerable GetCodeFixProviders() - { - yield return new IncrementFix(); - } - - protected override IEnumerable GetDiagnosticAnalyzers() - { - yield return new LiteralUnderFiveAnalyzer(); - } } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixValidationTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixValidationTests.cs index c3ae0c68e..b1a9597eb 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixValidationTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CodeFixValidationTests.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestFixes; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -342,7 +343,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken); - var root = await tree.GetRootAsync(cancellationToken); + var root = await tree!.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); var newToken = SyntaxFactory.Token(token.LeadingTrivia, token.Kind(), "base", "base", token.TrailingTrivia); return document.WithSyntaxRoot(root.ReplaceToken(token, newToken)); @@ -374,10 +375,10 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; var root = await tree.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); - var node = token.Parent; + var node = token.Parent!; var newToken = SyntaxFactory.Token(token.LeadingTrivia, SyntaxKind.BaseKeyword, "base", "base", token.TrailingTrivia); // Intentionally relocate a whitespace trivia node so the text is the same but the tree shape changes @@ -421,44 +422,19 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; var root = await tree.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); - var node = token.Parent; + var node = token.Parent!; var newToken = SyntaxFactory.Token(token.LeadingTrivia, SyntaxKind.BaseKeyword, "base", "base", token.TrailingTrivia); var newNode = SyntaxFactory.BaseExpression(newToken); return document.WithSyntaxRoot(root.ReplaceNode(node, newNode)); } } - private class ReplaceThisWithBaseTest : CodeFixTest + private class ReplaceThisWithBaseTest : CSharpCodeFixTest where TCodeFix : CodeFixProvider, new() { - public override string Language => LanguageNames.CSharp; - - public override Type SyntaxKindType => typeof(SyntaxKind); - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - { - return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - } - - protected override ParseOptions CreateParseOptions() - { - return new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - } - - protected override IEnumerable GetCodeFixProviders() - { - yield return new TCodeFix(); - } - - protected override IEnumerable GetDiagnosticAnalyzers() - { - yield return new ReplaceThisWithBaseAnalyzer(); - } } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/IncludeDiagnosticsMentionedByCodeFixTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/IncludeDiagnosticsMentionedByCodeFixTests.cs index 870642e16..0ecf7a130 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/IncludeDiagnosticsMentionedByCodeFixTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/IncludeDiagnosticsMentionedByCodeFixTests.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Testing.TestFixes; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -52,11 +53,11 @@ private async Task CreateChangedDocument( TextSpan sourceSpan, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); - var root = await tree.GetRootAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; + var root = (await tree.GetRootAsync(cancellationToken))!; var node = root.FindNode(sourceSpan); - var targetNode = node.AncestorsAndSelf().OfType().FirstOrDefault(); - return document.WithSyntaxRoot(root.RemoveNode(targetNode, SyntaxRemoveOptions.AddElasticMarker)); + var targetNode = node.AncestorsAndSelf().OfType().First(); + return document.WithSyntaxRoot(root.RemoveNode(targetNode, SyntaxRemoveOptions.AddElasticMarker)!); } public override ImmutableArray FixableDiagnosticIds => new[] { "CS0169" }.ToImmutableArray(); diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixMarkupTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixMarkupTests.cs index a732c6ca4..329843b5f 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixMarkupTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixMarkupTests.cs @@ -8,8 +8,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; +using Microsoft.CodeAnalysis.Testing.TestFixes; using Xunit; namespace Microsoft.CodeAnalysis.Testing @@ -35,7 +35,7 @@ namespace MyNamespace {|Brace:{|} "; // Test through the helper - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, FixedState = { MarkupHandling = MarkupMode.Allow }, @@ -62,7 +62,7 @@ namespace MyNamespace {|Brace:{|} // Test through the helper var exception = await Assert.ThrowsAsync(async () => { - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, FixedState = { MarkupHandling = MarkupMode.Allow }, @@ -94,7 +94,7 @@ namespace MyNamespace {|Brace:{|} "; // Test through the helper - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, FixedState = { MarkupHandling = MarkupMode.Allow }, @@ -123,7 +123,7 @@ namespace MyNamespace {|Brace:{|} // Test through the helper (this scenario cannot be described via the verifier) var exception = await Assert.ThrowsAsync(async () => { - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, FixedState = { MarkupHandling = MarkupMode.Allow }, @@ -135,42 +135,12 @@ namespace MyNamespace {|Brace:{|} Assert.Equal($"Context: Iterative code fix application{Environment.NewLine}Expected '1' iterations but found '0' iterations.", exception.Message); } - [DiagnosticAnalyzer(LanguageNames.CSharp)] - private class HighlightBraceAnalyzer : DiagnosticAnalyzer - { - internal static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("Brace", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - - context.RegisterSyntaxTreeAction(HandleSyntaxTree); - } - - private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) - { - foreach (var token in context.Tree.GetRoot(context.CancellationToken).DescendantTokens()) - { - if (!token.IsKind(SyntaxKind.OpenBraceToken)) - { - continue; - } - - context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation())); - } - } - } - [ExportCodeFixProvider(LanguageNames.CSharp)] [PartNotDiscoverable] private class CodeFixNotOfferedProvider : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds - => ImmutableArray.Create(HighlightBraceAnalyzer.Descriptor.Id); + => ImmutableArray.Create(new HighlightBracesAnalyzer().Descriptor.Id); public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; @@ -184,7 +154,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private class CodeFixOfferedProvider : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds - => ImmutableArray.Create(HighlightBraceAnalyzer.Descriptor.Id); + => ImmutableArray.Create(new HighlightBracesAnalyzer().Descriptor.Id); public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; @@ -205,7 +175,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) } } - private class Verify : CodeFixVerifier, DefaultVerifier> + private class Verify : CodeFixVerifier, DefaultVerifier> where TCodeFix : CodeFixProvider, new() { } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixTests.cs index 070d2b939..3efd07063 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MissingCodeFixTests.cs @@ -8,8 +8,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestAnalyzers; +using Microsoft.CodeAnalysis.Testing.TestFixes; using Xunit; namespace Microsoft.CodeAnalysis.Testing @@ -33,10 +33,10 @@ public async Task TestCodeFixNotProvided() namespace MyNamespace { } "; - var expected = new DiagnosticResult(HighlightBraceAnalyzer.Descriptor).WithSpan(2, 23, 2, 24); + var expected = new DiagnosticResult(new HighlightBracesAnalyzer().Descriptor).WithSpan(2, 23, 2, 24); // Test through the helper - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, ExpectedDiagnostics = { expected }, @@ -60,12 +60,12 @@ public async Task TestCodeFixProvidedWhenNotExpected() namespace MyNamespace { } "; - var expected = new DiagnosticResult(HighlightBraceAnalyzer.Descriptor).WithSpan(2, 23, 2, 24); + var expected = new DiagnosticResult(new HighlightBracesAnalyzer().Descriptor).WithSpan(2, 23, 2, 24); // Test through the helper var exception = await Assert.ThrowsAsync(async () => { - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, ExpectedDiagnostics = { expected }, @@ -96,10 +96,10 @@ public async Task TestCodeFixProvidedButTakesNoAction() namespace MyNamespace { } "; - var expected = new DiagnosticResult(HighlightBraceAnalyzer.Descriptor).WithSpan(2, 23, 2, 24); + var expected = new DiagnosticResult(new HighlightBracesAnalyzer().Descriptor).WithSpan(2, 23, 2, 24); // Test through the helper - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, ExpectedDiagnostics = { expected }, @@ -125,12 +125,12 @@ public async Task TestCodeFixNotProvidedWhenNoActionFixIsExpected() namespace MyNamespace { } "; - var expected = new DiagnosticResult(HighlightBraceAnalyzer.Descriptor).WithSpan(2, 23, 2, 24); + var expected = new DiagnosticResult(new HighlightBracesAnalyzer().Descriptor).WithSpan(2, 23, 2, 24); // Test through the helper (this scenario cannot be described via the verifier) var exception = await Assert.ThrowsAsync(async () => { - await new CSharpCodeFixTest + await new CSharpCodeFixTest { TestCode = testCode, ExpectedDiagnostics = { expected }, @@ -143,42 +143,12 @@ namespace MyNamespace { Assert.Equal($"Context: Iterative code fix application{Environment.NewLine}Expected '1' iterations but found '0' iterations.", exception.Message); } - [DiagnosticAnalyzer(LanguageNames.CSharp)] - private class HighlightBraceAnalyzer : DiagnosticAnalyzer - { - internal static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("Brace", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - - context.RegisterSyntaxTreeAction(HandleSyntaxTree); - } - - private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) - { - foreach (var token in context.Tree.GetRoot(context.CancellationToken).DescendantTokens()) - { - if (!token.IsKind(SyntaxKind.OpenBraceToken)) - { - continue; - } - - context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation())); - } - } - } - [ExportCodeFixProvider(LanguageNames.CSharp)] [PartNotDiscoverable] private class CodeFixNotOfferedProvider : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds - => ImmutableArray.Create(HighlightBraceAnalyzer.Descriptor.Id); + => ImmutableArray.Create(new HighlightBracesAnalyzer().Descriptor.Id); public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; @@ -192,7 +162,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private class CodeFixOfferedProvider : CodeFixProvider { public override ImmutableArray FixableDiagnosticIds - => ImmutableArray.Create(HighlightBraceAnalyzer.Descriptor.Id); + => ImmutableArray.Create(new HighlightBracesAnalyzer().Descriptor.Id); public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; @@ -213,7 +183,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) } } - private class Verify : CodeFixVerifier, DefaultVerifier> + private class Verify : CodeFixVerifier, DefaultVerifier> where TCodeFix : CodeFixProvider, new() { } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MultipleCodeFixesTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MultipleCodeFixesTests.cs index 82812cd8e..13e55322b 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MultipleCodeFixesTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/MultipleCodeFixesTests.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.TestFixes; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -90,10 +91,6 @@ class TestClass {{ }} "; - // The batch code fix provider does not support nested code actions. - // https://github.com/dotnet/roslyn/issues/43044 - var batchFixedCode = testCode; - // Three CodeFixProviders provide three actions var codeFixes = ImmutableArray.Create( ImmutableArray.Create(1), @@ -103,11 +100,6 @@ class TestClass {{ { TestCode = testCode, FixedCode = fixedCode, - BatchFixedState = - { - Sources = { batchFixedCode }, - MarkupHandling = MarkupMode.Allow, - }, }.RunAsync(); } #endif @@ -395,7 +387,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, int replacement, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; var root = await tree.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); var newToken = SyntaxFactory.Literal(token.LeadingTrivia, replacement.ToString(), replacement, token.TrailingTrivia); @@ -403,7 +395,7 @@ private async Task CreateChangedDocument(Document document, TextSpan s } } - private class CSharpTest : CodeFixTest + private class CSharpTest : CSharpCodeFixTest { private readonly ImmutableArray> _replacementGroups; private readonly bool _nested; @@ -414,22 +406,6 @@ public CSharpTest(ImmutableArray> replacementGroups, bool ne _nested = nested; } - public override string Language => LanguageNames.CSharp; - - public override Type SyntaxKindType => typeof(SyntaxKind); - - protected override string DefaultFileExt => "cs"; - - protected override CompilationOptions CreateCompilationOptions() - { - return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); - } - - protected override ParseOptions CreateParseOptions() - { - return new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); - } - protected override IEnumerable GetCodeFixProviders() { foreach (var replacementGroup in _replacementGroups) @@ -437,11 +413,6 @@ protected override IEnumerable GetCodeFixProviders() yield return new ReplaceZeroFix(replacementGroup, _nested); } } - - protected override IEnumerable GetDiagnosticAnalyzers() - { - yield return new LiteralZeroAnalyzer(); - } } } } diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CSharpCodeFixTest`2.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/TestFixes/CSharpCodeFixTest`2.cs similarity index 81% rename from tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CSharpCodeFixTest`2.cs rename to tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/TestFixes/CSharpCodeFixTest`2.cs index 2894c1a6d..926e8b0d7 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/CSharpCodeFixTest`2.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/TestFixes/CSharpCodeFixTest`2.cs @@ -8,17 +8,17 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.Testing +namespace Microsoft.CodeAnalysis.Testing.TestFixes { internal class CSharpCodeFixTest : CodeFixTest where TAnalyzer : DiagnosticAnalyzer, new() where TCodeFix : CodeFixProvider, new() { - public override string Language => LanguageNames.CSharp; + public sealed override string Language => LanguageNames.CSharp; - public override Type SyntaxKindType => typeof(SyntaxKind); + public sealed override Type SyntaxKindType => typeof(SyntaxKind); - protected override string DefaultFileExt => "cs"; + protected sealed override string DefaultFileExt => "cs"; protected override CompilationOptions CreateCompilationOptions() => new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/TestFixes/VisualBasicCodeFixTest`2.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/TestFixes/VisualBasicCodeFixTest`2.cs new file mode 100644 index 000000000..d764fdfc9 --- /dev/null +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeFix.Testing.UnitTests/TestFixes/VisualBasicCodeFixTest`2.cs @@ -0,0 +1,39 @@ +// 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. + +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.VisualBasic; + +namespace Microsoft.CodeAnalysis.Testing.TestFixes +{ + internal class VisualBasicCodeFixTest : CodeFixTest + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public override string Language => LanguageNames.VisualBasic; + + public override Type SyntaxKindType => typeof(SyntaxKind); + + protected override string DefaultFileExt => "vb"; + + protected override CompilationOptions CreateCompilationOptions() + => new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + + protected override ParseOptions CreateParseOptions() + => new VisualBasicParseOptions(LanguageVersion.Default, DocumentationMode.Diagnose); + + protected override IEnumerable GetDiagnosticAnalyzers() + { + yield return new TAnalyzer(); + } + + protected override IEnumerable GetCodeFixProviders() + { + yield return new TCodeFix(); + } + } +} diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing.UnitTests/RefactoringValidationTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing.UnitTests/RefactoringValidationTests.cs index 70ca3ad9f..b47c232dc 100644 --- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing.UnitTests/RefactoringValidationTests.cs +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.CodeRefactoring.Testing.UnitTests/RefactoringValidationTests.cs @@ -307,7 +307,7 @@ public override Task ComputeRefactoringsAsync(CodeRefactoringContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; var root = await tree.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); var newToken = SyntaxFactory.Token(token.LeadingTrivia, token.Kind(), "base", "base", token.TrailingTrivia); @@ -332,10 +332,10 @@ public override Task ComputeRefactoringsAsync(CodeRefactoringContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; var root = await tree.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); - var node = token.Parent; + var node = token.Parent!; var newToken = SyntaxFactory.Token(token.LeadingTrivia, SyntaxKind.BaseKeyword, "base", "base", token.TrailingTrivia); // Intentionally relocate a whitespace trivia node so the text is the same but the tree shape changes @@ -371,10 +371,10 @@ public override Task ComputeRefactoringsAsync(CodeRefactoringContext context) private async Task CreateChangedDocument(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken); + var tree = (await document.GetSyntaxTreeAsync(cancellationToken))!; var root = await tree.GetRootAsync(cancellationToken); var token = root.FindToken(sourceSpan.Start); - var node = token.Parent; + var node = token.Parent!; var newToken = SyntaxFactory.Token(token.LeadingTrivia, SyntaxKind.BaseKeyword, "base", "base", token.TrailingTrivia); var newNode = SyntaxFactory.BaseExpression(newToken); return document.WithSyntaxRoot(root.ReplaceNode(node, newNode)); diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/AbstractHighlightBracesAnalyzer.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/AbstractHighlightBracesAnalyzer.cs new file mode 100644 index 000000000..e2aa204ea --- /dev/null +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/AbstractHighlightBracesAnalyzer.cs @@ -0,0 +1,17 @@ +// 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. + +using CSharpSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; +using VisualBasicSyntaxKind = Microsoft.CodeAnalysis.VisualBasic.SyntaxKind; + +namespace Microsoft.CodeAnalysis.Testing.TestAnalyzers +{ + public abstract class AbstractHighlightBracesAnalyzer : AbstractHighlightTokensAnalyzer + { + protected AbstractHighlightBracesAnalyzer(string id = "Brace") + : base(id, (int)CSharpSyntaxKind.OpenBraceToken, (int)VisualBasicSyntaxKind.OpenBraceToken) + { + } + } +} diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/AbstractHighlightTokensAnalyzer.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/AbstractHighlightTokensAnalyzer.cs new file mode 100644 index 000000000..e89a46272 --- /dev/null +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/AbstractHighlightTokensAnalyzer.cs @@ -0,0 +1,52 @@ +// 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. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Testing.TestAnalyzers +{ + public abstract class AbstractHighlightTokensAnalyzer : DiagnosticAnalyzer + { + protected AbstractHighlightTokensAnalyzer(string id, params int[] tokenKinds) + { + Descriptor = new DiagnosticDescriptor(id, "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + Tokens = ImmutableHashSet.CreateRange(tokenKinds); + } + + public DiagnosticDescriptor Descriptor { get; } + + public ImmutableHashSet Tokens { get; } + + protected virtual GeneratedCodeAnalysisFlags GeneratedCodeAnalysisFlags => GeneratedCodeAnalysisFlags.None; + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags); + + context.RegisterSyntaxTreeAction(HandleSyntaxTree); + } + + protected virtual Diagnostic CreateDiagnostic(SyntaxToken token) + { + return Diagnostic.Create(Descriptor, token.GetLocation()); + } + + private void HandleSyntaxTree(SyntaxTreeAnalysisContext context) + { + foreach (var token in context.Tree.GetRoot(context.CancellationToken).DescendantTokens()) + { + if (!Tokens.Contains(token.RawKind)) + { + continue; + } + + context.ReportDiagnostic(CreateDiagnostic(token)); + } + } + } +} diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/HighlightBracesAnalyzer.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/HighlightBracesAnalyzer.cs new file mode 100644 index 000000000..b022af9d5 --- /dev/null +++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Utilities/TestAnalyzers/HighlightBracesAnalyzer.cs @@ -0,0 +1,48 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Testing.TestAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class HighlightBracesAnalyzer : AbstractHighlightBracesAnalyzer + { + public HighlightBracesAnalyzer() + { + } + + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1025:Configure generated code analysis", Justification = "False positive: https://github.com/dotnet/roslyn-analyzers/issues/4624")] + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1026:Enable concurrent execution", Justification = "False positive: https://github.com/dotnet/roslyn-analyzers/issues/4625")] + public override void Initialize(AnalysisContext context) + { + base.Initialize(context); + + // Also register a callback to handle braces in additional files + context.RegisterCompilationAction(HandleCompilation); + } + + private void HandleCompilation(CompilationAnalysisContext context) + { + foreach (var file in context.Options.AdditionalFiles) + { + var sourceText = file.GetText(context.CancellationToken); + if (sourceText is null) + { + continue; + } + + var text = sourceText.ToString(); + for (var i = text.IndexOf('{'); i >= 0; i = text.IndexOf('{', i + 1)) + { + var textSpan = new TextSpan(i, 1); + var lineSpan = sourceText.Lines.GetLinePositionSpan(textSpan); + context.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.Create(file.Path, textSpan, lineSpan))); + } + } + } + } +}