@@ -178,7 +178,7 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
178178 return ;
179179 }
180180
181- var methodFullName = ( methodSymbol . ContainingType ? . ToDisplayString ( ) ?? "" ) + "." + methodSymbol . Name ;
181+ var methodFullName = GetFullMethodName ( methodSymbol ) ;
182182
183183 // Analyze the arguments
184184 foreach ( var arg in invocation . ArgumentList . Arguments )
@@ -225,7 +225,7 @@ private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
225225 return ;
226226 }
227227
228- var ctorFullName = ( constructorSymbol . ContainingType ? . ToDisplayString ( ) ?? "" ) + "." + constructorSymbol . Name ;
228+ var ctorFullName = GetFullMethodName ( constructorSymbol ) ;
229229
230230 // Analyze the arguments
231231 if ( objectCreation . ArgumentList != null )
@@ -277,7 +277,7 @@ private void ReportInfoDiagnostic(SyntaxNodeAnalysisContext context)
277277 context . ReportDiagnostic ( diagnostic ) ;
278278
279279 // If default excluded methods are enabled, report them in a separate diagnostic
280- if ( UseDefaultExcludedMethods && DefaultExcludedMethods . Count > 0 )
280+ if ( UseDefaultExcludedMethods && DefaultExcludedMethods . Count > 0 )
281281 {
282282 var defaultMethodsStr = string . Join ( ", " , DefaultExcludedMethods ) ;
283283 var defaultMethodsInfo = Diagnostic . Create (
@@ -308,7 +308,7 @@ private static INamedTypeSymbol GetContainingType(InvocationExpressionSyntax inv
308308 return null ;
309309
310310 // For extension methods, the real containing type is the first parameter type
311- if ( methodSymbol . IsExtensionMethod && methodSymbol . Parameters . Length > 0 )
311+ if ( methodSymbol . IsExtensionMethod && methodSymbol . Parameters . Length > 0 )
312312 {
313313 return methodSymbol . Parameters [ 0 ] . Type as INamedTypeSymbol ;
314314 }
@@ -325,7 +325,7 @@ private bool IsMethodExcluded(string methodName, IMethodSymbol methodSymbol = nu
325325 // Check default excluded methods (by fully qualified name) if enabled
326326 if ( UseDefaultExcludedMethods && methodSymbol != null )
327327 {
328- var fullName = methodSymbol . ContainingType ? . ToDisplayString ( ) + "." + methodSymbol . Name ;
328+ var fullName = GetFullMethodName ( methodSymbol ) ;
329329 if ( DefaultExcludedMethods . Contains ( fullName ) )
330330 return true ;
331331 }
@@ -336,7 +336,7 @@ private bool IsMethodExcluded(string methodName, IMethodSymbol methodSymbol = nu
336336 // Parse exclusion list: allow both simple and fully qualified names
337337 var excludedMethods = ExcludedMethodNames
338338 . Split ( separator , StringSplitOptions . RemoveEmptyEntries )
339- . Select ( m => m . Trim ( ) )
339+ . Select ( m => m . Trim ( ) . TrimStart ( 'g' , 'l' , 'o' , 'b' , 'a' , 'l' , ':' , ':' ) ) // remove optional global:: prefix if given
340340 . Where ( m => ! string . IsNullOrEmpty ( m ) )
341341 . ToImmutableHashSet ( ) ;
342342
@@ -347,7 +347,7 @@ private bool IsMethodExcluded(string methodName, IMethodSymbol methodSymbol = nu
347347 // Check for fully qualified name (Namespace.Type.Method)
348348 if ( methodSymbol != null )
349349 {
350- var fullName = methodSymbol . ContainingType ? . ToDisplayString ( ) + "." + methodSymbol . Name ;
350+ var fullName = GetFullMethodName ( methodSymbol ) ;
351351 if ( excludedMethods . Contains ( fullName ) )
352352 return true ;
353353 }
@@ -360,8 +360,8 @@ private static bool IsRecord(INamedTypeSymbol type)
360360 if ( type == null )
361361 return false ;
362362
363- // Method 1 : Check IsRecord property directly through reflection
364- // This works for C# 9+ record declarations in newer Roslyn versions
363+ // Method1 : Check IsRecord property directly through reflection
364+ // This works for C#9+ record declarations in newer Roslyn versions
365365 try
366366 {
367367 var propertyInfo = type . GetType ( ) . GetProperty ( "IsRecord" ) ;
@@ -377,8 +377,8 @@ private static bool IsRecord(INamedTypeSymbol type)
377377 // Reflection failed, continue with other detection methods
378378 }
379379
380- // Method 2 : Check for record keyword in declaration syntax (for C# 9+)
381- if ( type . DeclaringSyntaxReferences . Length > 0 )
380+ // Method2 : Check for record keyword in declaration syntax (for C#9+)
381+ if ( type . DeclaringSyntaxReferences . Length > 0 )
382382 {
383383 try
384384 {
@@ -398,16 +398,16 @@ private static bool IsRecord(INamedTypeSymbol type)
398398 }
399399 }
400400
401- // Method 3 : Check for record runtime characteristics
401+ // Method3 : Check for record runtime characteristics
402402
403403 // Check for record's generated Equals/GetHashCode overrides
404404 bool hasSpecialEquals = type . GetMembers ( )
405405 . Where ( m => m . Name == "Equals" && m is IMethodSymbol )
406- . Any ( m => ( ( IMethodSymbol ) m ) . Parameters . Length == 1 &&
406+ . Any ( m => ( ( IMethodSymbol ) m ) . Parameters . Length == 1 &&
407407 ( ( IMethodSymbol ) m ) . GetAttributes ( ) . Any ( attr =>
408408 attr . AttributeClass ? . Name == "CompilerGeneratedAttribute" ) ) ;
409409
410- // Check for EqualityContract property (most reliable for .NET Standard 2 .0)
410+ // Check for EqualityContract property (most reliable for .NET Standard2 .0)
411411 bool hasEqualityContract = false ;
412412 foreach ( var member in type . GetMembers ( ) )
413413 {
@@ -423,19 +423,19 @@ private static bool IsRecord(INamedTypeSymbol type)
423423 }
424424 }
425425
426- // Method 4 : Check for other record characteristics
426+ // Method4 : Check for other record characteristics
427427 bool hasClone = type . GetMembers ( ) . Any ( m => m . Name == "<Clone>$" && m is IMethodSymbol ) ;
428428 bool hasPrintMembers = type . GetMembers ( ) . Any ( m => m . Name == "PrintMembers" && m is IMethodSymbol ) ;
429429 bool hasDeconstruct = type . GetMembers ( ) . Any ( m => m . Name == "Deconstruct" && m is IMethodSymbol ) ;
430430
431- // Method 5 : Check for record-specific ToString override pattern
431+ // Method5 : Check for record-specific ToString override pattern
432432 bool hasSpecialToString = type . GetMembers ( )
433433 . Where ( m => m . Name == "ToString" && m is IMethodSymbol )
434- . Any ( m => ( ( IMethodSymbol ) m ) . Parameters . Length == 0 &&
434+ . Any ( m => ( ( IMethodSymbol ) m ) . Parameters . Length == 0 &&
435435 ( ( IMethodSymbol ) m ) . GetAttributes ( ) . Any ( attr =>
436436 attr . AttributeClass ? . Name == "CompilerGeneratedAttribute" ) ) ;
437437
438- // Method 6 : Check for property pattern with init-only setters (common in records)
438+ // Method6 : Check for property pattern with init-only setters (common in records)
439439 bool hasInitOnlyProperties = type . GetMembers ( )
440440 . Where ( m => m is IPropertySymbol )
441441 . Cast < IPropertySymbol > ( )
@@ -444,5 +444,25 @@ private static bool IsRecord(INamedTypeSymbol type)
444444 return hasClone || hasPrintMembers || hasDeconstruct || hasSpecialToString ||
445445 hasSpecialEquals || hasInitOnlyProperties ;
446446 }
447+
448+ private static string GetFullMethodName ( IMethodSymbol methodSymbol )
449+ {
450+ if ( methodSymbol == null ) return string . Empty ;
451+ var typeName = GetFullTypeName ( methodSymbol . ContainingType ) ;
452+ return string . IsNullOrEmpty ( typeName ) ? methodSymbol . Name : typeName + "." + methodSymbol . Name ;
453+ }
454+
455+ private static string GetFullTypeName ( INamedTypeSymbol typeSymbol )
456+ {
457+ if ( typeSymbol == null ) return string . Empty ;
458+ var full = typeSymbol . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) ;
459+ // strip global:: prefix if present
460+ const string GlobalPrefix = "global::" ;
461+ if ( full . StartsWith ( GlobalPrefix , StringComparison . Ordinal ) )
462+ {
463+ full = full . Substring ( GlobalPrefix . Length ) ;
464+ }
465+ return full ;
466+ }
447467 }
448468}
0 commit comments