@@ -43,6 +43,98 @@ public override void Initialize(AnalysisContext context)
4343 } ) ;
4444 }
4545
46+ /// <summary>
47+ /// Determines whether an invocation is within a lambda expression that is being converted to an Expression tree.
48+ /// </summary>
49+ /// <param name="operation">The invocation operation to check.</param>
50+ /// <returns>True if the invocation is within a lambda converted to an Expression; false otherwise.</returns>
51+ private static bool IsWithinExpressionLambda ( IInvocationOperation operation )
52+ {
53+ // Walk up the operation tree to find the containing lambda
54+ IOperation ? current = operation . Parent ;
55+ while ( current is not null )
56+ {
57+ if ( current is IAnonymousFunctionOperation lambda )
58+ {
59+ // Found a lambda, now check if it's being converted to an Expression<>
60+ return IsLambdaConvertedToExpression ( lambda ) ;
61+ }
62+
63+ current = current . Parent ;
64+ }
65+
66+ return false ;
67+ }
68+
69+ /// <summary>
70+ /// Determines whether a lambda is being converted to an Expression tree type.
71+ /// </summary>
72+ /// <param name="lambda">The lambda operation to check.</param>
73+ /// <returns>True if the lambda is being converted to an Expression; false otherwise.</returns>
74+ private static bool IsLambdaConvertedToExpression ( IAnonymousFunctionOperation lambda )
75+ {
76+ // Walk up from the lambda to find conversion or argument operations
77+ IOperation ? current = lambda . Parent ;
78+ while ( current is not null )
79+ {
80+ // Check if the lambda's parent is a conversion operation
81+ if ( current is IConversionOperation conversion )
82+ {
83+ // Check if the target type is Expression<> or a related expression tree type
84+ return IsExpressionTreeType ( conversion . Type ) ;
85+ }
86+
87+ // Check if the lambda is being passed as an argument to a method expecting Expression<>
88+ if ( current is IArgumentOperation argument &&
89+ argument . Parameter ? . Type is INamedTypeSymbol parameterType )
90+ {
91+ return IsExpressionTreeType ( parameterType ) ;
92+ }
93+
94+ // Allow certain operations to be skipped (like parentheses)
95+ if ( current is IParenthesizedOperation )
96+ {
97+ current = current . Parent ;
98+ continue ;
99+ }
100+
101+ // Stop walking up at other operation types to avoid false positives
102+ break ;
103+ }
104+
105+ return false ;
106+ }
107+
108+ /// <summary>
109+ /// Determines whether a type is an Expression tree type (Expression<T> or related types).
110+ /// </summary>
111+ /// <param name="type">The type to check.</param>
112+ /// <returns>True if the type is an Expression tree type; false otherwise.</returns>
113+ private static bool IsExpressionTreeType ( ITypeSymbol ? type )
114+ {
115+ if ( type is not INamedTypeSymbol namedType )
116+ {
117+ return false ;
118+ }
119+
120+ // Check for System.Linq.Expressions.Expression<T>
121+ if ( namedType . Name == "Expression" &&
122+ namedType . ContainingNamespace ? . ToDisplayString ( ) == "System.Linq.Expressions" &&
123+ namedType . IsGenericType )
124+ {
125+ return true ;
126+ }
127+
128+ // Check for LambdaExpression and other expression types
129+ if ( namedType . ContainingNamespace ? . ToDisplayString ( ) == "System.Linq.Expressions" &&
130+ ( namedType . Name == "LambdaExpression" || namedType . Name . EndsWith ( "Expression" ) ) )
131+ {
132+ return true ;
133+ }
134+
135+ return false ;
136+ }
137+
46138 private void AnalyzeInvocation ( OperationAnalysisContext context , CommonInterest . AwaitableTypeTester awaitableTypes )
47139 {
48140 var operation = ( IInvocationOperation ) context . Operation ;
@@ -57,6 +149,13 @@ private void AnalyzeInvocation(OperationAnalysisContext context, CommonInterest.
57149 return ;
58150 }
59151
152+ // Check if this invocation is within a lambda that's being converted to an Expression<>
153+ if ( IsWithinExpressionLambda ( operation ) )
154+ {
155+ // This invocation is within a lambda converted to an expression tree, so it's not actually being invoked.
156+ return ;
157+ }
158+
60159 // Only consider invocations that are direct statements (or are statements through limited steps).
61160 // Otherwise, we assume their result is awaited, assigned, or otherwise consumed.
62161 IOperation ? parentOperation = operation . Parent ;
0 commit comments