From ab08f0de78298ce03e9ca38d1c6025877f650fbf Mon Sep 17 00:00:00 2001 From: Tau <4602612+bash@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:55:42 +0200 Subject: [PATCH] Extract to a separate file --- .../SyntaxNodeExtensions.ExpressionTree.cs | 64 +++++++++++++++++++ .../Funcky.Analyzers/SyntaxNodeExtensions.cs | 59 +---------------- 2 files changed, 65 insertions(+), 58 deletions(-) create mode 100644 Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.ExpressionTree.cs diff --git a/Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.ExpressionTree.cs b/Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.ExpressionTree.cs new file mode 100644 index 00000000..84dc0a7d --- /dev/null +++ b/Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.ExpressionTree.cs @@ -0,0 +1,64 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Funcky.Analyzers; + +internal static partial class SyntaxNodeExtensions +{ + // Adapted from Roslyn's source code as this API is not public: + // https://github.com/dotnet/roslyn/blob/232f7afa4966411958759c880de3a1765bdb28a0/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs#L925 + public static bool IsInExpressionTree( + [NotNullWhen(returnValue: true)] this SyntaxNode? node, + SemanticModel semanticModel, + [NotNullWhen(returnValue: true)] INamedTypeSymbol? expressionType, + CancellationToken cancellationToken) + => expressionType is not null + && node is not null + && node + .AncestorsAndSelf() + .Any(current => IsExpressionTree(new(current, expressionType, semanticModel, cancellationToken))); + + private static bool IsExpressionTree(IsExpressionTreeContext context) + => context.Syntax switch + { + var node when node.IsAnyLambda() => LambdaIsExpressionTree(context), + SelectOrGroupClauseSyntax or OrderingSyntax => QueryExpressionIsExpressionTree(context), + QueryClauseSyntax queryClause => QueryClauseIsExpressionTree(context, queryClause), + _ => false, + }; + + private static bool LambdaIsExpressionTree(IsExpressionTreeContext context) + { + var typeInfo = context.SemanticModel.GetTypeInfo(context.Syntax, context.CancellationToken); + return SymbolEqualityComparer.Default.Equals(context.ExpressionType, typeInfo.ConvertedType?.OriginalDefinition); + } + + private static bool QueryExpressionIsExpressionTree(IsExpressionTreeContext context) + { + var info = context.SemanticModel.GetSymbolInfo(context.Syntax, context.CancellationToken); + return TakesExpressionTree(info, context.ExpressionType); + } + + private static bool QueryClauseIsExpressionTree(IsExpressionTreeContext context, QueryClauseSyntax queryClause) + { + var info = context.SemanticModel.GetQueryClauseInfo(queryClause, context.CancellationToken); + return TakesExpressionTree(info.CastInfo, context.ExpressionType) + || TakesExpressionTree(info.OperationInfo, context.ExpressionType); + } + + private static bool TakesExpressionTree(SymbolInfo info, INamedTypeSymbol expressionType) + => GetAllSymbols(info).Any(symbol => TakesExpressionTreeAsFirstArgument(symbol, expressionType)); + + private static bool TakesExpressionTreeAsFirstArgument(ISymbol symbol, INamedTypeSymbol expressionType) + => symbol is IMethodSymbol method + && method.Parameters.Length > 0 + && SymbolEqualityComparer.Default.Equals(expressionType, method.Parameters[0].Type?.OriginalDefinition); + + private sealed record IsExpressionTreeContext( + SyntaxNode Syntax, + INamedTypeSymbol ExpressionType, + SemanticModel SemanticModel, + CancellationToken CancellationToken); +} diff --git a/Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.cs b/Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.cs index fb7451d6..ddce46ae 100644 --- a/Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.cs +++ b/Funcky.Analyzers/Funcky.Analyzers/SyntaxNodeExtensions.cs @@ -2,62 +2,11 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Funcky.Analyzers; -internal static class SyntaxNodeExtensions +internal static partial class SyntaxNodeExtensions { - // Adapted from Roslyn's source code as this API is not public: - // https://github.com/dotnet/roslyn/blob/232f7afa4966411958759c880de3a1765bdb28a0/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs#L925 - public static bool IsInExpressionTree( - [NotNullWhen(returnValue: true)] this SyntaxNode? node, - SemanticModel semanticModel, - [NotNullWhen(returnValue: true)] INamedTypeSymbol? expressionTypeOpt, - CancellationToken cancellationToken) - { - return expressionTypeOpt is not null - && node is not null - && node.AncestorsAndSelf().Any(current => IsExpressionTree(new IsExpressionTreeContext(current, expressionTypeOpt, semanticModel, cancellationToken))); - - static bool IsExpressionTree(IsExpressionTreeContext context) - { - return context.Syntax switch { - var node when node.IsAnyLambda() => LambdaIsExpressionTree(context), - SelectOrGroupClauseSyntax or OrderingSyntax => QueryExpressionIsExpressionTree(context), - QueryClauseSyntax queryClause => QueryClauseIsExpressionTree(context, queryClause), - _ => false, - }; - } - - static bool LambdaIsExpressionTree(IsExpressionTreeContext context) - { - var typeInfo = context.SemanticModel.GetTypeInfo(context.Syntax, context.CancellationToken); - return SymbolEqualityComparer.Default.Equals(context.ExpressionType, typeInfo.ConvertedType?.OriginalDefinition); - } - - static bool QueryExpressionIsExpressionTree(IsExpressionTreeContext context) - { - var info = context.SemanticModel.GetSymbolInfo(context.Syntax, context.CancellationToken); - return TakesExpressionTree(info, context.ExpressionType); - } - - static bool QueryClauseIsExpressionTree(IsExpressionTreeContext context, QueryClauseSyntax queryClause) - { - var info = context.SemanticModel.GetQueryClauseInfo(queryClause, context.CancellationToken); - return TakesExpressionTree(info.CastInfo, context.ExpressionType) - || TakesExpressionTree(info.OperationInfo, context.ExpressionType); - } - - static bool TakesExpressionTree(SymbolInfo info, INamedTypeSymbol expressionType) - => GetAllSymbols(info).Any(symbol => TakesExpressionTreeAsFirstArgument(symbol, expressionType)); - - static bool TakesExpressionTreeAsFirstArgument(ISymbol symbol, INamedTypeSymbol expressionType) - => symbol is IMethodSymbol method - && method.Parameters.Length > 0 - && SymbolEqualityComparer.Default.Equals(expressionType, method.Parameters[0].Type?.OriginalDefinition); - } - internal static ImmutableArray GetAllSymbols(SymbolInfo info) => info.Symbol == null ? info.CandidateSymbols @@ -67,10 +16,4 @@ internal static ImmutableArray GetAllSymbols(SymbolInfo info) // https://github.com/dotnet/roslyn/blob/232f7afa4966411958759c880de3a1765bdb28a0/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs#L925 internal static bool IsAnyLambda([NotNullWhen(returnValue: true)] this SyntaxNode? node) => node?.Kind() is SyntaxKind.ParenthesizedLambdaExpression or SyntaxKind.SimpleLambdaExpression; - - private sealed record IsExpressionTreeContext( - SyntaxNode Syntax, - INamedTypeSymbol ExpressionType, - SemanticModel SemanticModel, - CancellationToken CancellationToken); }