/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Test/Core/Diagnostics/DiagnosticExtensions.cs
457 строк
22 KB
Jan Jones
Free pooled object in Verify helper (#83834)
23 май 2026, 20:00
Не верифицирован
23 май 2026, 20:00
4ad7820
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis { public static class DiagnosticExtensions { /// <summary> /// This is obsolete. Use Verify instead. /// </summary> public static void VerifyErrorCodes(this IEnumerable<Diagnostic> actual, params DiagnosticDescription[] expected) { Verify(actual, expected, errorCodeOnly: true); } public static void VerifyErrorCodes(this ImmutableArray<Diagnostic> actual, params DiagnosticDescription[] expected) { VerifyErrorCodes((IEnumerable<Diagnostic>)actual, expected); } internal static void Verify(this DiagnosticBag actual, params DiagnosticDescription[] expected) { Verify(actual.AsEnumerable(), expected, errorCodeOnly: false); } public static void Verify(this IEnumerable<Diagnostic> actual, params DiagnosticDescription[] expected) { Verify(actual, expected, errorCodeOnly: false); } public static void Verify(this IEnumerable<Diagnostic> actual, bool fallbackToErrorCodeOnlyForNonEnglish, params DiagnosticDescription[] expected) { Verify(actual, expected, errorCodeOnly: fallbackToErrorCodeOnlyForNonEnglish && EnsureEnglishUICulture.PreferredOrNull != null); } public static void VerifyWithFallbackToErrorCodeOnlyForNonEnglish(this IEnumerable<Diagnostic> actual, params DiagnosticDescription[] expected) { Verify(actual, true, expected); } public static void Verify(this ImmutableArray<Diagnostic> actual, params DiagnosticDescription[] expected) { Verify((IEnumerable<Diagnostic>)actual, expected); } private static void Verify(IEnumerable<Diagnostic> actual, DiagnosticDescription[] expected, bool errorCodeOnly) { if (expected == null) { throw new ArgumentException("Must specify expected errors.", nameof(expected)); } var includeDefaultSeverity = expected.Any() && expected.All(e => e.DefaultSeverity != null); var includeEffectiveSeverity = expected.Any() && expected.All(e => e.EffectiveSeverity != null); var unmatchedActualDescription = actual.Select(d => new DiagnosticDescription(d, errorCodeOnly, includeDefaultSeverity, includeEffectiveSeverity)) .ToList(); var unmatchedActualIndex = actual.Select((_, i) => i).ToList(); var unmatchedExpected = ArrayBuilder<DiagnosticDescription>.GetInstance(); // Try to match each of the 'expected' errors to one of the 'actual' ones. // If any of the expected errors don't appear, fail test. foreach (var d in expected) { int index = unmatchedActualDescription.IndexOf(d); if (index > -1) { unmatchedActualDescription.RemoveAt(index); unmatchedActualIndex.RemoveAt(index); } else { unmatchedExpected.Add(d); } } if (unmatchedActualDescription.Count > 0 || unmatchedExpected.Count > 0) { var text = DiagnosticDescription.GetAssertText(expected, actual, unmatchedExpected.ToArray(), actual.Select((a, i) => (a, i)).Join(unmatchedActualIndex, ai => ai.i, i => i, (ai, _) => ai.a)); unmatchedExpected.Free(); Assert.Fail(text); return; } unmatchedExpected.Free(); } public static TCompilation VerifyDiagnostics<TCompilation>(this TCompilation c, params DiagnosticDescription[] expected) where TCompilation : Compilation { var diagnostics = c.GetDiagnostics(); diagnostics.Verify(expected); VerifyAssemblyIds(c, diagnostics); return c; } private static void VerifyAssemblyIds<TCompilation>( TCompilation c, ImmutableArray<Diagnostic> diagnostics) where TCompilation : Compilation { foreach (var diagnostic in diagnostics) { // If this is a diagnostic about a missing assembly, make sure that we can get back // an AssemblyIdentity when we query the compiler. If it's not a diagnostic about // a missing assembly, make sure we get no results back. if (c.IsUnreferencedAssemblyIdentityDiagnosticCode(diagnostic.Code)) { var assemblyIds = c.GetUnreferencedAssemblyIdentities(diagnostic); Assert.False(assemblyIds.IsEmpty); var diagnosticMessage = diagnostic.GetMessage(); foreach (var id in assemblyIds) { Assert.Contains(id.GetDisplayName(), diagnosticMessage); } } else { var assemblyIds = c.GetUnreferencedAssemblyIdentities(diagnostic); Assert.True(assemblyIds.IsEmpty); } } } public static void VerifyAnalyzerOccurrenceCount<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, int expectedCount, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null) where TCompilation : Compilation { Assert.Equal(expectedCount, c.GetAnalyzerDiagnostics(analyzers, null, onAnalyzerException).Length); } public static TCompilation VerifyAnalyzerDiagnostics<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null, params DiagnosticDescription[] expected) where TCompilation : Compilation { return VerifyAnalyzerDiagnostics(c, analyzers, reportSuppressedDiagnostics: false, options, onAnalyzerException, expected); } public static TCompilation VerifyAnalyzerDiagnostics<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, bool reportSuppressedDiagnostics, AnalyzerOptions options = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null, params DiagnosticDescription[] expected) where TCompilation : Compilation { var newCompilation = c.GetCompilationWithAnalyzerDiagnostics(analyzers, options, onAnalyzerException, reportSuppressedDiagnostics, includeCompilerDiagnostics: false, CancellationToken.None, out var diagnostics); diagnostics.Verify(expected); return newCompilation; } public static ImmutableArray<Diagnostic> GetAnalyzerDiagnostics<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null, CancellationToken cancellationToken = default) where TCompilation : Compilation { return GetAnalyzerDiagnostics(c, analyzers, reportSuppressedDiagnostics: false, options, onAnalyzerException, cancellationToken); } public static ImmutableArray<Diagnostic> GetAnalyzerDiagnostics<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, bool reportSuppressedDiagnostics, AnalyzerOptions options = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null, CancellationToken cancellationToken = default) where TCompilation : Compilation { _ = GetCompilationWithAnalyzerDiagnostics(c, analyzers, options, onAnalyzerException, reportSuppressedDiagnostics, includeCompilerDiagnostics: false, cancellationToken, out var diagnostics); return diagnostics; } public static TCompilation VerifySuppressedDiagnostics<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null, CancellationToken cancellationToken = default, params DiagnosticDescription[] expected) where TCompilation : Compilation { // Verify suppression is unaffected by toggling /warnaserror. // Only perform this additional verification if the caller hasn't // explicitly overridden specific or general diagnostic options. if (c.Options.GeneralDiagnosticOption == ReportDiagnostic.Default && c.Options.SpecificDiagnosticOptions.IsEmpty) { _ = c.VerifySuppressedDiagnostics(toggleWarnAsError: true, analyzers, options, onAnalyzerException, expected, cancellationToken); } return c.VerifySuppressedDiagnostics(toggleWarnAsError: false, analyzers, options, onAnalyzerException, expected, cancellationToken); } private static TCompilation VerifySuppressedDiagnostics<TCompilation>( this TCompilation c, bool toggleWarnAsError, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, DiagnosticDescription[] expectedDiagnostics, CancellationToken cancellationToken) where TCompilation : Compilation { if (toggleWarnAsError) { var toggledOption = c.Options.GeneralDiagnosticOption == ReportDiagnostic.Error ? ReportDiagnostic.Default : ReportDiagnostic.Error; c = (TCompilation)c.WithOptions(c.Options.WithGeneralDiagnosticOption(toggledOption)); var builder = ArrayBuilder<DiagnosticDescription>.GetInstance(expectedDiagnostics.Length); foreach (var expected in expectedDiagnostics) { // Toggle warnaserror and effective severity if following are true: // 1. Default severity is not specified or specified as Warning // 2. Effective severity is not specified or specified as Warning or Error var defaultSeverityCheck = !expected.DefaultSeverity.HasValue || expected.DefaultSeverity.Value == DiagnosticSeverity.Warning; var effectiveSeverityCheck = !expected.EffectiveSeverity.HasValue || expected.EffectiveSeverity.Value == DiagnosticSeverity.Warning || expected.EffectiveSeverity.Value == DiagnosticSeverity.Error; DiagnosticDescription newExpected; if (defaultSeverityCheck && effectiveSeverityCheck) { newExpected = expected.WithWarningAsError(!expected.IsWarningAsError); if (expected.EffectiveSeverity.HasValue) { var newEffectiveSeverity = expected.EffectiveSeverity.Value == DiagnosticSeverity.Error ? DiagnosticSeverity.Warning : DiagnosticSeverity.Error; newExpected = newExpected.WithEffectiveSeverity(newEffectiveSeverity); } } else { newExpected = expected; } builder.Add(newExpected); } expectedDiagnostics = builder.ToArrayAndFree(); } c = c.GetCompilationWithAnalyzerDiagnostics(analyzers, options, onAnalyzerException, reportSuppressedDiagnostics: true, includeCompilerDiagnostics: true, cancellationToken, out var diagnostics); diagnostics = diagnostics.WhereAsArray(d => d.IsSuppressed); diagnostics.Verify(expectedDiagnostics); return c; // note this is a new compilation } public static TCompilation VerifySuppressedAndFilteredDiagnostics<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options = null, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException = null, CancellationToken cancellationToken = default) where TCompilation : Compilation { // Verify suppressed diagnostics are filtered when reportSuppressedDiagnostics is false. // The actual verification is handled in GetCompilationWithAnalyzerDiagnostics. c = c.GetCompilationWithAnalyzerDiagnostics(analyzers, options, onAnalyzerException, reportSuppressedDiagnostics: false, includeCompilerDiagnostics: true, cancellationToken, out var diagnostics); return c; // note this is a new compilation } private static TCompilation GetCompilationWithAnalyzerDiagnostics<TCompilation>( this TCompilation c, DiagnosticAnalyzer[] analyzers, AnalyzerOptions options, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, bool reportSuppressedDiagnostics, bool includeCompilerDiagnostics, CancellationToken cancellationToken, out ImmutableArray<Diagnostic> diagnostics) where TCompilation : Compilation { var analyzersArray = analyzers.ToImmutableArray(); if (reportSuppressedDiagnostics != c.Options.ReportSuppressedDiagnostics) { c = (TCompilation)c.WithOptions(c.Options.WithReportSuppressedDiagnostics(reportSuppressedDiagnostics)); } var analyzerManager = new AnalyzerManager(analyzersArray); var driver = AnalyzerDriver.CreateAndAttachToCompilation(c, analyzersArray, options, analyzerManager, onAnalyzerException, analyzerExceptionFilter: null, reportAnalyzer: false, severityFilter: SeverityFilter.None, trackSuppressedDiagnosticIds: false, out var newCompilation, cancellationToken); Debug.Assert(newCompilation.SemanticModelProvider != null); var compilerDiagnostics = newCompilation.GetDiagnostics(cancellationToken); var analyzerDiagnostics = driver.GetDiagnosticsAsync(newCompilation, cancellationToken).Result; var allDiagnostics = includeCompilerDiagnostics ? compilerDiagnostics.AddRange(analyzerDiagnostics) : analyzerDiagnostics; diagnostics = driver.ApplyProgrammaticSuppressionsAndFilterDiagnostics(allDiagnostics, newCompilation, cancellationToken); if (!reportSuppressedDiagnostics) { Assert.True(diagnostics.All(d => !d.IsSuppressed)); } return (TCompilation)newCompilation; // note this is a new compilation } /// <summary> /// Given a set of compiler or <see cref="DiagnosticAnalyzer"/> generated <paramref name="diagnostics"/>, returns the effective diagnostics after applying the below filters: /// 1) <see cref="CompilationOptions.SpecificDiagnosticOptions"/> specified for the given <paramref name="compilation"/>. /// 2) <see cref="CompilationOptions.GeneralDiagnosticOption"/> specified for the given <paramref name="compilation"/>. /// 3) Diagnostic suppression through applied <see cref="System.Diagnostics.CodeAnalysis.SuppressMessageAttribute"/>. /// 4) Pragma directives for the given <paramref name="compilation"/>. /// </summary> public static IEnumerable<Diagnostic> GetEffectiveDiagnostics(this Compilation compilation, IEnumerable<Diagnostic> diagnostics) { return CompilationWithAnalyzers.GetEffectiveDiagnostics(diagnostics, compilation); } /// <summary> /// Returns true if all the diagnostics that can be produced by this analyzer are suppressed through options. /// </summary> [Obsolete("This API is no longer supported. See https://github.com/dotnet/roslyn/issues/67592 for details")] public static bool IsDiagnosticAnalyzerSuppressed(this DiagnosticAnalyzer analyzer, CompilationOptions options) { return CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(analyzer, options); } public static TCompilation VerifyEmitDiagnostics<TCompilation>(this TCompilation c, EmitOptions options, params DiagnosticDescription[] expected) where TCompilation : Compilation { c.GetEmitDiagnostics(options: options).Verify(expected); return c; } public static ImmutableArray<Diagnostic> GetEmitDiagnostics<TCompilation>( this TCompilation c, EmitOptions options = null, IEnumerable<ResourceDescription> manifestResources = null) where TCompilation : Compilation { var pdbStream = MonoHelpers.IsRunningOnMono() || options?.EmitMetadataOnly == true ? null : new MemoryStream(); return c.Emit(new MemoryStream(), pdbStream: pdbStream, options: options, manifestResources: manifestResources).Diagnostics; } public static TCompilation VerifyEmitDiagnostics<TCompilation>(this TCompilation c, params DiagnosticDescription[] expected) where TCompilation : Compilation { return VerifyEmitDiagnostics(c, EmitOptions.Default, expected); } public static ImmutableArray<Diagnostic> GetEmitDiagnostics<TCompilation>(this TCompilation c) where TCompilation : Compilation { return GetEmitDiagnostics(c, EmitOptions.Default); } public static TCompilation VerifyEmitDiagnostics<TCompilation>(this TCompilation c, IEnumerable<ResourceDescription> manifestResources, params DiagnosticDescription[] expected) where TCompilation : Compilation { c.GetEmitDiagnostics(manifestResources: manifestResources).Verify(expected); return c; } public static string Concat(this string[] str) { return str.Aggregate(new StringBuilder(), (sb, s) => sb.AppendLine(s), sb => sb.ToString()); } public static DiagnosticAnalyzer GetCompilerDiagnosticAnalyzer(string languageName) { return languageName == LanguageNames.CSharp ? (DiagnosticAnalyzer)new Diagnostics.CSharp.CSharpCompilerDiagnosticAnalyzer() : new Diagnostics.VisualBasic.VisualBasicCompilerDiagnosticAnalyzer(); } public static ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>> GetCompilerDiagnosticAnalyzersMap() { var builder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<DiagnosticAnalyzer>>(); builder.Add(LanguageNames.CSharp, ImmutableArray.Create(GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp))); builder.Add(LanguageNames.VisualBasic, ImmutableArray.Create(GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic))); return builder.ToImmutable(); } public static AnalyzerReference GetCompilerDiagnosticAnalyzerReference(string languageName) { var analyzer = GetCompilerDiagnosticAnalyzer(languageName); return new AnalyzerImageReference(ImmutableArray.Create(analyzer), display: analyzer.GetType().FullName); } public static ImmutableDictionary<string, ImmutableArray<AnalyzerReference>> GetCompilerDiagnosticAnalyzerReferencesMap() { var builder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<AnalyzerReference>>(); builder.Add(LanguageNames.CSharp, ImmutableArray.Create(GetCompilerDiagnosticAnalyzerReference(LanguageNames.CSharp))); builder.Add(LanguageNames.VisualBasic, ImmutableArray.Create(GetCompilerDiagnosticAnalyzerReference(LanguageNames.VisualBasic))); return builder.ToImmutable(); } internal static string GetExpectedErrorLogHeader(CommonCompiler compiler) { var expectedToolName = compiler.GetToolName(); var expectedVersion = compiler.GetAssemblyVersion(); var expectedSemanticVersion = compiler.GetAssemblyVersion().ToString(fieldCount: 3); var expectedFileVersion = compiler.GetCompilerVersion(); var expectedLanguage = compiler.GetCultureName(); return string.Format(@"{{ ""$schema"": ""http://json.schemastore.org/sarif-1.0.0"", ""version"": ""1.0.0"", ""runs"": [ {{ ""tool"": {{ ""name"": ""{0}"", ""version"": ""{1}"", ""fileVersion"": ""{2}"", ""semanticVersion"": ""{3}"", ""language"": ""{4}"" }},", expectedToolName, expectedVersion, expectedFileVersion, expectedSemanticVersion, expectedLanguage); } public static string Inspect(this Diagnostic e) => e.Location.IsInSource ? $"{e.Severity} {e.Id}: {e.GetMessage(CultureInfo.CurrentCulture)}" : e.Location.IsInMetadata ? "metadata: " : "no location: "; public static string ToString(this Diagnostic d, IFormatProvider formatProvider) { IFormattable formattable = d; return formattable.ToString(null, formatProvider); } } }