/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyTestBase.cs
420 строк
14 KB
Cyrus Najmabadi
Make sealed
26 мар 2025, 22:21
26 мар 2025, 22:21
d99b771
Код
Авторство
О чём код?
// 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.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SymbolId; public abstract class SymbolKeyTestBase : CSharpTestBase { [Flags] internal enum SymbolKeyComparison { None = 0x0, IgnoreCase = 0x1, IgnoreAssemblyIds = 0x2 } [Flags] internal enum SymbolCategory { All = 0, DeclaredNamespace = 2, DeclaredType = 4, NonTypeMember = 8, Parameter = 16, } #region "Verification" internal static void ResolveAndVerifySymbolList(IEnumerable<ISymbol> newSymbols, IEnumerable<ISymbol> originalSymbols, CSharpCompilation originalComp) { var newlist = newSymbols.OrderBy(s => s.Name).ToList(); var origlist = originalSymbols.OrderBy(s => s.Name).ToList(); Assert.Equal(origlist.Count, newlist.Count); for (var i = 0; i < newlist.Count; i++) { ResolveAndVerifySymbol(newlist[i], origlist[i], originalComp); } } internal static void ResolveAndVerifyTypeSymbol(ExpressionSyntax node, ITypeSymbol sourceSymbol, SemanticModel model, CSharpCompilation sourceComp) { var typeinfo = model.GetTypeInfo(node); ResolveAndVerifySymbol(typeinfo.Type ?? typeinfo.ConvertedType, sourceSymbol, sourceComp); } internal static void ResolveAndVerifySymbol(ExpressionSyntax node, ISymbol sourceSymbol, SemanticModel model, CSharpCompilation sourceComp, SymbolKeyComparison comparison = SymbolKeyComparison.None) { var syminfo = model.GetSymbolInfo(node); ResolveAndVerifySymbol(syminfo.Symbol, sourceSymbol, sourceComp, comparison); } internal static void ResolveAndVerifySymbol(ISymbol symbol1, ISymbol symbol2, Compilation compilation2, SymbolKeyComparison comparison = SymbolKeyComparison.None) { // same ID AssertSymbolKeysEqual(symbol1, symbol2, comparison); var resolvedSymbol = ResolveSymbol(symbol1, compilation2, comparison); Assert.NotNull(resolvedSymbol); // same Symbol Assert.Equal(symbol2, resolvedSymbol); Assert.Equal(symbol2.GetHashCode(), resolvedSymbol.GetHashCode()); } internal static ISymbol ResolveSymbol(ISymbol originalSymbol, Compilation targetCompilation, SymbolKeyComparison comparison) { var sid = SymbolKey.Create(originalSymbol, CancellationToken.None); // Verify that serialization works. var serialized = sid.ToString(); var deserialized = new SymbolKey(serialized); var comparer = SymbolKey.GetComparer(ignoreCase: false, ignoreAssemblyKeys: false); Assert.True(comparer.Equals(sid, deserialized)); var symInfo = sid.Resolve(targetCompilation, (comparison & SymbolKeyComparison.IgnoreAssemblyIds) == SymbolKeyComparison.IgnoreAssemblyIds); return symInfo.Symbol; } internal static void AssertSymbolKeysEqual(ISymbol symbol1, ISymbol symbol2, SymbolKeyComparison comparison, bool expectEqual = true) { var sid1 = SymbolKey.Create(symbol1, CancellationToken.None); var sid2 = SymbolKey.Create(symbol2, CancellationToken.None); // default is Insensitive var ignoreCase = (comparison & SymbolKeyComparison.IgnoreCase) == SymbolKeyComparison.IgnoreCase; // default is NOT ignore var ignoreAssemblyIds = (comparison & SymbolKeyComparison.IgnoreAssemblyIds) == SymbolKeyComparison.IgnoreAssemblyIds; var message = string.Concat( ignoreCase ? "SymbolID IgnoreCase" : "SymbolID", ignoreAssemblyIds ? " IgnoreAssemblyIds " : " ", "Compare"); var ret = CodeAnalysis.SymbolKey.GetComparer(ignoreCase, ignoreAssemblyIds).Equals(sid2, sid1); if (expectEqual) { Assert.True(ret, message); } else { Assert.False(ret, message); } } #endregion #region "Utilities" internal static List<BlockSyntax> GetBlockSyntaxList(IMethodSymbol symbol) { var list = new List<BlockSyntax>(); foreach (var node in symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax())) { BlockSyntax body = null; if (node is BaseMethodDeclarationSyntax baseMethod) { body = baseMethod.Body; } else if (node is AccessorDeclarationSyntax accessor) { body = accessor.Body; } if (body != null && body.Statements.Any()) { list.Add(body); } } return list; } internal static IEnumerable<ISymbol> GetSourceSymbols(Microsoft.CodeAnalysis.CSharp.CSharpCompilation compilation, SymbolCategory category) { // NYI for local symbols var list = GetSourceSymbols(compilation, includeLocal: false); var kinds = new List<SymbolKind>(); if ((category & SymbolCategory.DeclaredNamespace) != 0) { kinds.Add(SymbolKind.Namespace); } if ((category & SymbolCategory.DeclaredType) != 0) { kinds.Add(SymbolKind.NamedType); kinds.Add(SymbolKind.TypeParameter); } if ((category & SymbolCategory.NonTypeMember) != 0) { kinds.Add(SymbolKind.Field); kinds.Add(SymbolKind.Event); kinds.Add(SymbolKind.Property); kinds.Add(SymbolKind.Method); } if ((category & SymbolCategory.Parameter) != 0) { kinds.Add(SymbolKind.Parameter); } return list.Where(s => { if (s.IsImplicitlyDeclared) { return false; } foreach (var k in kinds) { if (s.Kind == k) { return true; } } return false; }); } internal static IList<ISymbol> GetSourceSymbols(CSharpCompilation compilation, bool includeLocal) { var list = new List<ISymbol>(); var localDumper = includeLocal ? new LocalSymbolDumper(compilation) : null; GetSourceMemberSymbols(compilation.SourceModule.GlobalNamespace.GetPublicSymbol(), list, localDumper); // ?? // if (includeLocal) GetSourceAliasSymbols(compilation, list); Compilation c = compilation; list.Add(c.Assembly); list.AddRange(c.Assembly.Modules); return list; } #endregion #region "Private Helpers" private static void GetSourceMemberSymbols(INamespaceOrTypeSymbol symbol, List<ISymbol> list, LocalSymbolDumper localDumper) { foreach (var memberSymbol in symbol.GetMembers()) { list.Add(memberSymbol); switch (memberSymbol.Kind) { case SymbolKind.NamedType: case SymbolKind.Namespace: GetSourceMemberSymbols((INamespaceOrTypeSymbol)memberSymbol, list, localDumper); break; case SymbolKind.Method: var method = (IMethodSymbol)memberSymbol; foreach (var parameter in method.Parameters) { list.Add(parameter); } localDumper?.GetLocalSymbols(method.GetSymbol(), list); break; case SymbolKind.Field: localDumper?.GetLocalSymbols(memberSymbol.GetSymbol<FieldSymbol>(), list); break; } } } private static void GetSourceAliasSymbols(CSharpCompilation comp, List<ISymbol> list) { foreach (var tree in comp.SyntaxTrees) { var usingNodes = tree.GetRoot().DescendantNodes().OfType<UsingDirectiveSyntax>(); var model = comp.GetSemanticModel(tree); foreach (var u in usingNodes) { if (u.Alias != null) { // var sym = model.GetSymbolInfo(u.Alias.Identifier).Symbol; var sym = model.GetDeclaredSymbol(u); if (sym != null && !list.Contains(sym)) { list.Add(sym); } } } } } #endregion private sealed class LocalSymbolDumper { private readonly CSharpCompilation _compilation; public LocalSymbolDumper(CSharpCompilation compilation) => _compilation = compilation; public void GetLocalSymbols(FieldSymbol symbol, List<ISymbol> list) { foreach (var node in symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax())) { if (node is VariableDeclaratorSyntax declarator && declarator.Initializer != null) { var model = _compilation.GetSemanticModel(declarator.SyntaxTree); // Expression var df = model.AnalyzeDataFlow(declarator.Initializer.Value); GetLocalAndType(df, list); GetAnonymousExprSymbols(declarator.Initializer.Value, model, list); } } } public void GetLocalSymbols(MethodSymbol symbol, List<ISymbol> list) { foreach (var node in symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax())) { BlockSyntax body = null; if (node is BaseMethodDeclarationSyntax baseMethod) { body = baseMethod.Body; } else if (node is AccessorDeclarationSyntax accessor) { body = accessor.Body; } var model = _compilation.GetSemanticModel(node.SyntaxTree); if (body != null && body.Statements.Any()) { var df = model.AnalyzeDataFlow(body); GetLocalAndType(df, list); GetAnonymousTypeOrFuncSymbols(body, model, list); GetLabelSymbols(body, model, list); } // C# specific (this|base access) if (node is ConstructorDeclarationSyntax ctor && ctor.Initializer != null) { foreach (var a in ctor.Initializer.ArgumentList.Arguments) { var df = model.AnalyzeDataFlow(a.Expression); // VisitLocals(arg, df); list.AddRange(df.VariablesDeclared); GetAnonymousExprSymbols(a.Expression, model, list); } } } } private static void GetLocalAndType(DataFlowAnalysis df, List<ISymbol> list) { foreach (var v in df.VariablesDeclared) { list.Add(v); if (v is ILocalSymbol local && (local.Type.Kind == SymbolKind.ArrayType || local.Type.Kind == SymbolKind.PointerType)) { list.Add(local.Type); } } } private static void GetLabelSymbols(BlockSyntax body, SemanticModel model, List<ISymbol> list) { var labels = body.DescendantNodes().OfType<LabeledStatementSyntax>(); foreach (var n in labels) { // Label: -> 'Label' is token var sym = model.GetDeclaredSymbol(n); list.Add(sym); } var swlabels = body.DescendantNodes().OfType<SwitchLabelSyntax>(); foreach (var n in swlabels) { // label value has NO symbol, Type is expr's type // e.g. case "A": -> string type // var info1 = model.GetTypeInfo(n.Value); // var info2 = model.GetSymbolInfo(n.Value); var sym = model.GetDeclaredSymbol(n); list.Add(sym); } } private static void GetAnonymousTypeOrFuncSymbols(BlockSyntax body, SemanticModel model, List<ISymbol> list) { IEnumerable<ExpressionSyntax> exprs = body.DescendantNodes().OfType<SimpleLambdaExpressionSyntax>(); IEnumerable<ExpressionSyntax> tmp = body.DescendantNodes().OfType<ParenthesizedLambdaExpressionSyntax>(); exprs = exprs.Concat(tmp); tmp = body.DescendantNodes().OfType<AnonymousMethodExpressionSyntax>(); exprs = exprs.Concat(tmp); tmp = body.DescendantNodes().OfType<AnonymousObjectCreationExpressionSyntax>(); exprs = exprs.Concat(tmp); foreach (var expr in exprs) { GetAnonymousExprSymbols(expr, model, list); } } private static void GetAnonymousExprSymbols(ExpressionSyntax expr, SemanticModel model, List<ISymbol> list) { var kind = expr.Kind(); if (kind is not SyntaxKind.AnonymousObjectCreationExpression and not SyntaxKind.AnonymousMethodExpression and not SyntaxKind.ParenthesizedLambdaExpression and not SyntaxKind.SimpleLambdaExpression) { return; } var tinfo = model.GetTypeInfo(expr); var conv = model.GetConversion(expr); if (conv.IsAnonymousFunction) { // Lambda has no Type unless in part of case expr (C# specific) // var f = (Func<int>)(() => { return 1; }); Type is delegate // method symbol var sinfo = model.GetSymbolInfo(expr); list.Add(sinfo.Symbol); } else if (tinfo.Type != null && tinfo.Type.TypeKind != TypeKind.Delegate) { // bug#12625 // GetSymbolInfo -> .ctor (part of members) list.Add(tinfo.Type); // NamedType with empty name foreach (var m in tinfo.Type.GetMembers()) { list.Add(m); } } } } }