/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Test/Core/Diagnostics/TrackingDiagnosticAnalyzer.cs
183 строки
10 KB
AlekseyTs
Initial support for union declarations (#82500)
25 фев 2026, 22:37
Не верифицирован
25 фев 2026, 22:37
49a4215
Код
Авторство
О чём код?
// 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.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.Diagnostics; using Xunit; namespace Microsoft.CodeAnalysis.Test.Utilities { public abstract class TrackingDiagnosticAnalyzer<TLanguageKindEnum> : TestDiagnosticAnalyzer<TLanguageKindEnum> where TLanguageKindEnum : struct { #region Tracking public class Entry { public readonly string AbstractMemberName; public readonly string CallerName; public readonly TLanguageKindEnum SyntaxKind; public readonly SymbolKind? SymbolKind; public readonly MethodKind? MethodKind; public readonly bool ReturnsVoid; public Entry(string abstractMemberName, string callerName, SyntaxNode node, ISymbol symbol) { AbstractMemberName = abstractMemberName; CallerName = callerName; SyntaxKind = node == null ? default(TLanguageKindEnum) : (TLanguageKindEnum)(object)(ushort)node.RawKind; SymbolKind = symbol == null ? (SymbolKind?)null : symbol.Kind; MethodKind = symbol is IMethodSymbol ? ((IMethodSymbol)symbol).MethodKind : (MethodKind?)null; ReturnsVoid = symbol is IMethodSymbol ? ((IMethodSymbol)symbol).ReturnsVoid : false; } public override string ToString() { return CallerName + "(" + string.Join(", ", SymbolKind, MethodKind, SyntaxKind) + ")"; } } private readonly ConcurrentQueue<Entry> _callLog = new ConcurrentQueue<Entry>(); protected override void OnAbstractMember(string abstractMemberName, SyntaxNode node, ISymbol symbol, string callerName) { _callLog.Enqueue(new Entry(abstractMemberName, callerName, node, symbol)); } public IEnumerable<Entry> CallLog { get { return _callLog; } } #endregion #region Analysis private static readonly Regex s_omittedSyntaxKindRegex = new Regex(@"None|Trivia|Token|Keyword|List|Xml|Cref|Compilation|Namespace|Class|Struct|Union|Enum|Interface|Delegate|Field|Property|Indexer|Event|Operator|Constructor|Access|Incomplete|Attribute|Filter|InterpolatedString"); private bool FilterByAbstractName(Entry entry, string abstractMemberName) { return abstractMemberName == entry.AbstractMemberName; } public void VerifyAllAnalyzerMembersWereCalled() { var actualMembers = _callLog.Select(e => e.CallerName).Distinct(); AssertSequenceEqual(AllAnalyzerMemberNames, actualMembers); } public void VerifyAnalyzeSymbolCalledForAllSymbolKinds() { var expectedSymbolKinds = new[] { SymbolKind.Event, SymbolKind.Field, SymbolKind.Method, SymbolKind.NamedType, SymbolKind.Namespace, SymbolKind.Parameter, SymbolKind.Property }; var actualSymbolKinds = _callLog.Where(a => FilterByAbstractName(a, "Symbol")).Where(e => e.SymbolKind.HasValue).Select(e => e.SymbolKind.Value).Distinct(); AssertSequenceEqual(expectedSymbolKinds, actualSymbolKinds); } protected virtual bool IsAnalyzeNodeSupported(TLanguageKindEnum syntaxKind) { return !s_omittedSyntaxKindRegex.IsMatch(syntaxKind.ToString()); } public void VerifyAnalyzeNodeCalledForAllSyntaxKinds(HashSet<TLanguageKindEnum> expectedMissingSyntaxKinds) { var expectedSyntaxKinds = AllSyntaxKinds.Where(IsAnalyzeNodeSupported); var actualSyntaxKinds = new HashSet<TLanguageKindEnum>(_callLog.Where(a => FilterByAbstractName(a, "SyntaxNode")).Select(e => e.SyntaxKind)); var savedSyntaxKindsPatterns = new HashSet<TLanguageKindEnum>(expectedMissingSyntaxKinds); expectedMissingSyntaxKinds.IntersectWith(actualSyntaxKinds); Assert.True(expectedMissingSyntaxKinds.Count == 0, "AllInOne test contains ignored SyntaxKinds: " + string.Join(", ", expectedMissingSyntaxKinds)); actualSyntaxKinds.UnionWith(savedSyntaxKindsPatterns); AssertIsSuperset(expectedSyntaxKinds, actualSyntaxKinds); } protected virtual bool IsOnCodeBlockSupported(SymbolKind symbolKind, MethodKind methodKind, bool returnsVoid) { return true; } public void VerifyOnCodeBlockCalledForAllSymbolAndMethodKinds(HashSet<SymbolKind> symbolKindsWithNoCodeBlocks = null, bool allowUnexpectedCalls = false) { const MethodKind InvalidMethodKind = (MethodKind)(-1); var expectedArguments = new[] { new { SymbolKind = SymbolKind.Event, MethodKind = InvalidMethodKind, ReturnsVoid = false }, new { SymbolKind = SymbolKind.Field, MethodKind = InvalidMethodKind, ReturnsVoid = false }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.Constructor, ReturnsVoid = true }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.Conversion, ReturnsVoid = false }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.Destructor, ReturnsVoid = true }, // C# only new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.EventAdd, ReturnsVoid = true }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.EventRemove, ReturnsVoid = true }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.EventRaise, ReturnsVoid = true }, // VB only new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.ExplicitInterfaceImplementation, ReturnsVoid = true }, // C# only new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.Ordinary, ReturnsVoid = false }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.Ordinary, ReturnsVoid = true }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.PropertyGet, ReturnsVoid = false }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.PropertySet, ReturnsVoid = true }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.StaticConstructor, ReturnsVoid = true }, new { SymbolKind = SymbolKind.Method, MethodKind = MethodKind.UserDefinedOperator, ReturnsVoid = false }, new { SymbolKind = SymbolKind.Property, MethodKind = InvalidMethodKind, ReturnsVoid = false }, new { SymbolKind = SymbolKind.NamedType, MethodKind = InvalidMethodKind, ReturnsVoid = false }, new { SymbolKind = SymbolKind.Namespace, MethodKind = InvalidMethodKind, ReturnsVoid = false } }.AsEnumerable(); if (symbolKindsWithNoCodeBlocks != null) { expectedArguments = expectedArguments.Where(a => !symbolKindsWithNoCodeBlocks.Contains(a.SymbolKind)); } expectedArguments = expectedArguments.Where(a => IsOnCodeBlockSupported(a.SymbolKind, a.MethodKind, a.ReturnsVoid)); var actualOnCodeBlockStartedArguments = _callLog.Where(a => FilterByAbstractName(a, "CodeBlockStart")) .Select(e => new { SymbolKind = e.SymbolKind.Value, MethodKind = e.MethodKind ?? InvalidMethodKind, e.ReturnsVoid }).Distinct(); var actualOnCodeBlockEndedArguments = _callLog.Where(a => FilterByAbstractName(a, "CodeBlock")) .Select(e => new { SymbolKind = e.SymbolKind.Value, MethodKind = e.MethodKind ?? InvalidMethodKind, e.ReturnsVoid }).Distinct(); if (!allowUnexpectedCalls) { AssertSequenceEqual(expectedArguments, actualOnCodeBlockStartedArguments, items => items.OrderBy(p => p.SymbolKind).ThenBy(p => p.MethodKind).ThenBy(p => p.ReturnsVoid)); AssertSequenceEqual(expectedArguments, actualOnCodeBlockEndedArguments, items => items.OrderBy(p => p.SymbolKind).ThenBy(p => p.MethodKind).ThenBy(p => p.ReturnsVoid)); } else { AssertIsSuperset(expectedArguments, actualOnCodeBlockStartedArguments); AssertIsSuperset(expectedArguments, actualOnCodeBlockEndedArguments); } } private void AssertSequenceEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual, Func<IEnumerable<T>, IOrderedEnumerable<T>> sorter = null) { sorter = sorter ?? new Func<IEnumerable<T>, IOrderedEnumerable<T>>(items => items.OrderBy(i => i)); expected = sorter(expected); actual = sorter(actual); Assert.True(expected.SequenceEqual(actual), Environment.NewLine + "Expected: " + string.Join(", ", expected) + Environment.NewLine + "Actual: " + string.Join(", ", actual)); } private void AssertIsSuperset<T>(IEnumerable<T> expectedSubset, IEnumerable<T> actualSuperset) { var missingElements = expectedSubset.GroupJoin(actualSuperset, e => e, a => a, (e, a) => new { Element = e, IsMissing = !a.Any() }) .Where(p => p.IsMissing).Select(p => p.Element).ToList(); var presentElements = expectedSubset.Except(missingElements); Assert.True(missingElements.Count == 0, Environment.NewLine + "Missing: " + string.Join(", ", missingElements) + Environment.NewLine + "Present: " + string.Join(", ", presentElements)); } #endregion } }