Skip to content

Commit

Permalink
Merge pull request #759 from sharwell/generated-sources
Browse files Browse the repository at this point in the history
Add ProjectState.GeneratedSources API
  • Loading branch information
sharwell authored Mar 11, 2021
2 parents ef87003 + e0c5578 commit ea51db4
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ private async Task VerifyGeneratedCodeDiagnosticsAsync(ImmutableArray<Diagnostic
return;
}

if (!expected.Any(x => IsSubjectToExclusion(x, sources)))
if (!expected.Any(x => IsSubjectToExclusion(x, analyzers, sources)))
{
return;
}
Expand All @@ -268,7 +268,7 @@ private async Task VerifyGeneratedCodeDiagnosticsAsync(ImmutableArray<Diagnostic
// still be reported. We also insert a new line at the beginning so we have to move all diagnostic
// locations which have a specific position down by one line.
var expectedResults = expected
.Where(x => !IsSubjectToExclusion(x, sources))
.Where(x => !IsSubjectToExclusion(x, analyzers, sources))
.Select(x => IsInSourceFile(x, sources) ? x.WithLineOffset(1) : x)
.ToArray();

Expand All @@ -285,7 +285,7 @@ private async Task VerifySuppressionDiagnosticsAsync(ImmutableArray<DiagnosticAn
return;
}

if (!expected.Any(x => IsSubjectToExclusion(x, sources)))
if (!expected.Any(x => IsSubjectToExclusion(x, analyzers, sources)))
{
return;
}
Expand All @@ -300,7 +300,7 @@ private async Task VerifySuppressionDiagnosticsAsync(ImmutableArray<DiagnosticAn

var prefix = Language == LanguageNames.CSharp ? "#pragma warning disable" : "#Disable Warning";
var suppressionVerifier = verifier.PushContext($"Verifying exclusions in '{prefix}' code");
var suppressedDiagnostics = expected.Where(x => IsSubjectToExclusion(x, sources)).Select(x => x.Id).Distinct();
var suppressedDiagnostics = expected.Where(x => IsSubjectToExclusion(x, analyzers, sources)).Select(x => x.Id).Distinct();
var suppression = prefix + " " + string.Join(", ", suppressedDiagnostics);
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);
Expand Down Expand Up @@ -886,7 +886,7 @@ void AppendLocation(DiagnosticLocation location)
}
}

private static bool IsSubjectToExclusion(DiagnosticResult result, (string filename, SourceText content)[] sources)
private static bool IsSubjectToExclusion(DiagnosticResult result, ImmutableArray<DiagnosticAnalyzer> analyzers, (string filename, SourceText content)[] sources)
{
if (result.Id.StartsWith("CS", StringComparison.Ordinal)
|| result.Id.StartsWith("BC", StringComparison.Ordinal))
Expand All @@ -912,12 +912,18 @@ private static bool IsSubjectToExclusion(DiagnosticResult result, (string filena
return false;
}

if (!analyzers.Any(analyzer => analyzer.SupportedDiagnostics.Any(supported => supported.Id == result.Id)))
{
// This diagnostic is not reported by an active analyzer
return false;
}

return true;
}

private static bool IsSuppressible(ImmutableArray<DiagnosticAnalyzer> analyzers, DiagnosticResult result, (string filename, SourceText content)[] sources)
{
if (!IsSubjectToExclusion(result, sources))
if (!IsSubjectToExclusion(result, analyzers, sources))
{
return false;
}
Expand Down Expand Up @@ -988,6 +994,11 @@ private async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsAsync(Evaluat
/// <see cref="Diagnostic.Location"/>.</returns>
protected async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsAsync(Solution solution, ImmutableArray<DiagnosticAnalyzer> analyzers, ImmutableArray<Diagnostic> additionalDiagnostics, CompilerDiagnostics compilerDiagnostics, CancellationToken cancellationToken)
{
if (analyzers.IsEmpty)
{
analyzers = ImmutableArray.Create<DiagnosticAnalyzer>(new EmptyDiagnosticAnalyzer());
}

var diagnostics = ImmutableArray.CreateBuilder<Diagnostic>();
foreach (var project in solution.Projects)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ protected static bool CodeActionExpected(SolutionState state)
return state.InheritanceMode != null
|| state.MarkupHandling != null
|| state.Sources.Any()
|| state.GeneratedSources.Any()
|| state.AdditionalFiles.Any()
|| state.AnalyzerConfigFiles.Any()
|| state.AdditionalFilesFactories.Any();
Expand All @@ -72,6 +73,7 @@ protected static bool CodeActionExpected(SolutionState state)
protected static bool HasAnyChange(SolutionState oldState, SolutionState newState)
{
return !oldState.Sources.SequenceEqual(newState.Sources, SourceFileEqualityComparer.Instance)
|| !oldState.GeneratedSources.SequenceEqual(newState.GeneratedSources, SourceFileEqualityComparer.Instance)
|| !oldState.AdditionalFiles.SequenceEqual(newState.AdditionalFiles, SourceFileEqualityComparer.Instance)
|| !oldState.AnalyzerConfigFiles.SequenceEqual(newState.AnalyzerConfigFiles, SourceFileEqualityComparer.Instance);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public EvaluatedProjectState(ProjectState state, ReferenceAssemblies defaultRefe
state.OutputKind ?? OutputKind.DynamicallyLinkedLibrary,
state.DocumentationMode ?? DocumentationMode.Diagnose,
state.Sources.ToImmutableArray(),
state.GeneratedSources.ToImmutableArray(),
state.AdditionalFiles.ToImmutableArray(),
state.AnalyzerConfigFiles.ToImmutableArray(),
state.AdditionalProjectReferences.ToImmutableArray(),
Expand All @@ -37,6 +38,7 @@ private EvaluatedProjectState(
OutputKind outputKind,
DocumentationMode documentationMode,
ImmutableArray<(string filename, SourceText content)> sources,
ImmutableArray<(string filename, SourceText content)> generatedSources,
ImmutableArray<(string filename, SourceText content)> additionalFiles,
ImmutableArray<(string filename, SourceText content)> analyzerConfigFiles,
ImmutableArray<string> additionalProjectReferences,
Expand All @@ -50,6 +52,7 @@ private EvaluatedProjectState(
OutputKind = outputKind;
DocumentationMode = documentationMode;
Sources = sources;
GeneratedSources = generatedSources;
AdditionalFiles = additionalFiles;
AnalyzerConfigFiles = analyzerConfigFiles;
AdditionalProjectReferences = additionalProjectReferences;
Expand All @@ -71,6 +74,8 @@ private EvaluatedProjectState(

public ImmutableArray<(string filename, SourceText content)> Sources { get; }

public ImmutableArray<(string filename, SourceText content)> GeneratedSources { get; }

public ImmutableArray<(string filename, SourceText content)> AdditionalFiles { get; }

public ImmutableArray<(string filename, SourceText content)> AnalyzerConfigFiles { get; }
Expand Down Expand Up @@ -109,6 +114,7 @@ private EvaluatedProjectState With(
Optional<OutputKind> outputKind = default,
Optional<DocumentationMode> documentationMode = default,
Optional<ImmutableArray<(string filename, SourceText content)>> sources = default,
Optional<ImmutableArray<(string filename, SourceText content)>> generatedSources = default,
Optional<ImmutableArray<(string filename, SourceText content)>> additionalFiles = default,
Optional<ImmutableArray<(string filename, SourceText content)>> analyzerConfigFiles = default,
Optional<ImmutableArray<string>> additionalProjectReferences = default,
Expand All @@ -123,6 +129,7 @@ private EvaluatedProjectState With(
GetValueOrDefault(outputKind, OutputKind),
GetValueOrDefault(documentationMode, DocumentationMode),
GetValueOrDefault(sources, Sources),
GetValueOrDefault(generatedSources, GeneratedSources),
GetValueOrDefault(additionalFiles, AdditionalFiles),
GetValueOrDefault(analyzerConfigFiles, AnalyzerConfigFiles),
GetValueOrDefault(additionalProjectReferences, AdditionalProjectReferences),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ internal ProjectState(ProjectState sourceState)
Sources = new SourceFileList(DefaultPrefix, DefaultExtension);

Sources.AddRange(sourceState.Sources);
GeneratedSources.AddRange(sourceState.GeneratedSources);
AdditionalFiles.AddRange(sourceState.AdditionalFiles);
AnalyzerConfigFiles.AddRange(sourceState.AnalyzerConfigFiles);
AdditionalFilesFactories.AddRange(sourceState.AdditionalFilesFactories);
Expand Down Expand Up @@ -64,6 +65,8 @@ internal ProjectState(ProjectState sourceState)
/// </summary>
public SourceFileList Sources { get; }

public SourceFileCollection GeneratedSources { get; } = new SourceFileCollection();

public SourceFileCollection AdditionalFiles { get; } = new SourceFileCollection();

public SourceFileCollection AnalyzerConfigFiles { get; } = new SourceFileCollection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AnalyzerConfigFiles.g
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.GeneratedSources.get -> System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)>
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
Expand All @@ -151,6 +152,7 @@ Microsoft.CodeAnalysis.Testing.ProjectState.AnalyzerConfigFiles.get -> Microsoft
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.GeneratedSources.get -> Microsoft.CodeAnalysis.Testing.SourceFileCollection
Microsoft.CodeAnalysis.Testing.ProjectState.Language.get -> string
Microsoft.CodeAnalysis.Testing.ProjectState.Name.get -> string
Microsoft.CodeAnalysis.Testing.ProjectState.OutputKind.get -> Microsoft.CodeAnalysis.OutputKind?
Expand Down Expand Up @@ -223,6 +225,7 @@ Microsoft.CodeAnalysis.Testing.StateInheritanceMode.Explicit = 1 -> Microsoft.Co
Microsoft.CodeAnalysis.Testing.TestBehaviors
Microsoft.CodeAnalysis.Testing.TestBehaviors.None = 0 -> Microsoft.CodeAnalysis.Testing.TestBehaviors
Microsoft.CodeAnalysis.Testing.TestBehaviors.SkipGeneratedCodeCheck = 1 -> Microsoft.CodeAnalysis.Testing.TestBehaviors
Microsoft.CodeAnalysis.Testing.TestBehaviors.SkipGeneratedSourcesCheck = 4 -> Microsoft.CodeAnalysis.Testing.TestBehaviors
Microsoft.CodeAnalysis.Testing.TestBehaviors.SkipSuppressionCheck = 2 -> Microsoft.CodeAnalysis.Testing.TestBehaviors
Microsoft.CodeAnalysis.Testing.TestFileMarkupParser
abstract Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.CreateCompilationOptions() -> Microsoft.CodeAnalysis.CompilationOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ public SolutionState WithInheritedValuesApplied(SolutionState? baseState, Immuta
result.Sources.AddRange(baseState.Sources);
}

if (GeneratedSources.Count == 0)
{
result.GeneratedSources.AddRange(baseState.GeneratedSources);
}

if (AdditionalFiles.Count == 0)
{
result.AdditionalFiles.AddRange(baseState.AdditionalFiles);
Expand Down Expand Up @@ -176,6 +181,7 @@ public SolutionState WithInheritedValuesApplied(SolutionState? baseState, Immuta
result.MarkupHandling = markupHandling;
result.InheritanceMode = StateInheritanceMode.Explicit;
result.Sources.AddRange(Sources);
result.GeneratedSources.AddRange(GeneratedSources);
result.AdditionalFiles.AddRange(AdditionalFiles);
result.AnalyzerConfigFiles.AddRange(AnalyzerConfigFiles);
result.AdditionalProjects.AddRange(AdditionalProjects);
Expand Down Expand Up @@ -203,6 +209,11 @@ private static bool HasAnyContentChanges(bool willInherit, SolutionState state,
return true;
}

if ((!willInherit || state.GeneratedSources.Any()) && !ContentEqual(state.GeneratedSources, baseState.GeneratedSources))
{
return true;
}

if ((!willInherit || state.AdditionalFiles.Any()) && !ContentEqual(state.AdditionalFiles, baseState.AdditionalFiles))
{
return true;
Expand Down Expand Up @@ -252,8 +263,9 @@ private static bool ContentEqual(SourceFileCollection x, SourceFileCollection y)
/// <summary>
/// Processes the markup syntax for this <see cref="SolutionState"/> according to the current
/// <see cref="MarkupHandling"/>, and returns a new <see cref="SolutionState"/> with the
/// <see cref="ProjectState.Sources"/>, <see cref="ProjectState.AdditionalFiles"/>,
/// <see cref="ProjectState.AnalyzerConfigFiles"/>, and <see cref="ExpectedDiagnostics"/> updated accordingly.
/// <see cref="ProjectState.Sources"/>, <see cref="ProjectState.GeneratedSources"/>,
/// <see cref="ProjectState.AdditionalFiles"/>, <see cref="ProjectState.AnalyzerConfigFiles"/>, and
/// <see cref="ExpectedDiagnostics"/> updated accordingly.
/// </summary>
/// <param name="markupOptions">Additional options to apply during markup processing.</param>
/// <param name="defaultDiagnostic">The diagnostic descriptor to use for markup spans without an explicit name,
Expand All @@ -276,7 +288,8 @@ public SolutionState WithProcessedMarkup(MarkupOptions markupOptions, Diagnostic

var markupLocations = ImmutableDictionary<string, FileLinePositionSpan>.Empty;
(var expected, var testSources) = ProcessMarkupSources(Sources, ExpectedDiagnostics, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (additionalExpected1, additionalFiles) = ProcessMarkupSources(AdditionalFiles.Concat(AdditionalFilesFactories.SelectMany(factory => factory())), expected, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (additionalExpected2, testGeneratedSources) = ProcessMarkupSources(GeneratedSources, expected, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (additionalExpected1, additionalFiles) = ProcessMarkupSources(AdditionalFiles.Concat(AdditionalFilesFactories.SelectMany(factory => factory())), additionalExpected2, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (additionalExpected, analyzerConfigFiles) = ProcessMarkupSources(AnalyzerConfigFiles, additionalExpected1, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);

var result = new SolutionState(Name, Language, DefaultPrefix, DefaultExtension);
Expand All @@ -286,18 +299,22 @@ public SolutionState WithProcessedMarkup(MarkupOptions markupOptions, Diagnostic
result.OutputKind = OutputKind;
result.DocumentationMode = DocumentationMode;
result.Sources.AddRange(testSources);
result.GeneratedSources.AddRange(testGeneratedSources);
result.AdditionalFiles.AddRange(additionalFiles);
result.AnalyzerConfigFiles.AddRange(analyzerConfigFiles);

foreach (var (projectName, projectState) in AdditionalProjects)
{
var (correctedIntermediateDiagnostics, additionalProjectSources) = ProcessMarkupSources(projectState.Sources, additionalExpected, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (correctedDiagnostics1, additionalProjectAdditionalFiles) = ProcessMarkupSources(projectState.AdditionalFiles.Concat(projectState.AdditionalFilesFactories.SelectMany(factory => factory())), correctedIntermediateDiagnostics, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (correctedDiagnostics2, additionalProjectGeneratedSources) = ProcessMarkupSources(projectState.GeneratedSources, correctedIntermediateDiagnostics, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (correctedDiagnostics1, additionalProjectAdditionalFiles) = ProcessMarkupSources(projectState.AdditionalFiles.Concat(projectState.AdditionalFilesFactories.SelectMany(factory => factory())), correctedDiagnostics2, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);
var (correctedDiagnostics, additionalProjectAnalyzerConfigFiles) = ProcessMarkupSources(projectState.AnalyzerConfigFiles, correctedDiagnostics1, ref markupLocations, markupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, defaultPath);

var processedProjectState = new ProjectState(projectState);
processedProjectState.Sources.Clear();
processedProjectState.Sources.AddRange(additionalProjectSources);
processedProjectState.GeneratedSources.Clear();
processedProjectState.GeneratedSources.AddRange(additionalProjectGeneratedSources);
processedProjectState.AdditionalFiles.Clear();
processedProjectState.AdditionalFilesFactories.Clear();
processedProjectState.AdditionalFiles.AddRange(additionalProjectAdditionalFiles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,15 @@ public enum TestBehaviors
/// at the beginning of the file.
/// </summary>
SkipSuppressionCheck = 0x02,

/// <summary>
/// Skip a verification check that the contents of <see cref="ProjectState.GeneratedSources"/> match the sources
/// produced by the active source generators (if any).
/// </summary>
/// <remarks>
/// When this flag is set, the <see cref="ProjectState.GeneratedSources"/> property is completely ignored; tests
/// are encouraged to leave it empty for optimal readability.
/// </remarks>
SkipGeneratedSourcesCheck = 0x04,
}
}
Loading

0 comments on commit ea51db4

Please sign in to comment.