@@ -72,7 +72,7 @@ private static Diagnostic CreateDiagnostic(DiagnosticInfo info) =>
7272 "MCP001" => Diagnostics . InvalidXmlDocumentation ,
7373 "MCP002" => Diagnostics . McpMethodMustBePartial ,
7474 _ => throw new InvalidOperationException ( $ "Unknown diagnostic ID: { info . Id } ")
75- } , info . Location , info . MessageArgs ) ;
75+ } , info . Location ? . ToLocation ( ) , info . MessageArgs ) ;
7676
7777 private static IncrementalValuesProvider < MethodToGenerate > CreateProviderForAttribute (
7878 IncrementalGeneratorInitializationContext context ,
@@ -108,15 +108,15 @@ private static MethodToGenerate ExtractMethodInfo(
108108 HasGeneratableContent ( xmlDocs , methodSymbol , descriptionAttribute ) )
109109 {
110110 return MethodToGenerate . CreateDiagnosticOnly (
111- new DiagnosticInfo ( "MCP002" , methodDeclaration . Identifier . GetLocation ( ) , methodSymbol . Name ) ) ;
111+ DiagnosticInfo . Create ( "MCP002" , methodDeclaration . Identifier . GetLocation ( ) , methodSymbol . Name ) ) ;
112112 }
113113
114114 return MethodToGenerate . Empty ;
115115 }
116116
117117 // For partial methods with invalid XML, report diagnostic but still generate partial declaration.
118118 EquatableArray < DiagnosticInfo > diagnostics = hasInvalidXml ?
119- new EquatableArray < DiagnosticInfo > ( ImmutableArray . Create ( new DiagnosticInfo ( "MCP001" , methodSymbol . Locations . FirstOrDefault ( ) , methodSymbol . Name ) ) ) :
119+ new EquatableArray < DiagnosticInfo > ( ImmutableArray . Create ( DiagnosticInfo . Create ( "MCP001" , methodSymbol . Locations . FirstOrDefault ( ) , methodSymbol . Name ) ) ) :
120120 default ;
121121
122122 bool needsMethodDescription = xmlDocs is not null &&
@@ -514,29 +514,29 @@ private readonly record struct TypeDeclarationInfo(
514514 string Name ,
515515 string TypeKeyword ) ;
516516
517- /// <summary>Holds diagnostic information to be reported.</summary>
518- private readonly struct DiagnosticInfo
517+ /// <summary>Holds serializable location information for incremental generator caching.</summary>
518+ /// <remarks>
519+ /// Roslyn <see cref="Location"/> objects cannot be stored in incremental generator cached data
520+ /// because they contain references to syntax trees from specific compilations. Storing them
521+ /// causes issues when the generator returns cached data with locations from earlier compilations.
522+ /// </remarks>
523+ private readonly record struct LocationInfo ( string FilePath , TextSpan TextSpan , LinePositionSpan LineSpan )
519524 {
520- public string Id { get ; }
521- public Location ? Location { get ; }
522- public string MethodName { get ; }
523-
524- public DiagnosticInfo ( string id , Location ? location , string methodName )
525- {
526- Id = id ;
527- Location = location ;
528- MethodName = methodName ;
529- }
530-
531- public object ? [ ] MessageArgs => [ MethodName ] ;
525+ public static LocationInfo ? FromLocation ( Location ? location ) =>
526+ location is null || ! location . IsInSource ? null :
527+ new LocationInfo ( location . SourceTree ? . FilePath ?? "" , location . SourceSpan , location . GetLineSpan ( ) . Span ) ;
532528
533- // For incremental generator caching, we compare only the logical content, not the Location object
534- public bool Equals ( DiagnosticInfo other ) =>
535- Id == other . Id && MethodName == other . MethodName ;
529+ public Location ToLocation ( ) =>
530+ Location . Create ( FilePath , TextSpan , LineSpan ) ;
531+ }
536532
537- public override bool Equals ( object ? obj ) => obj is DiagnosticInfo other && Equals ( other ) ;
533+ /// <summary>Holds diagnostic information to be reported.</summary>
534+ private readonly record struct DiagnosticInfo ( string Id , LocationInfo ? Location , string MethodName )
535+ {
536+ public static DiagnosticInfo Create ( string id , Location ? location , string methodName ) =>
537+ new ( id , LocationInfo . FromLocation ( location ) , methodName ) ;
538538
539- public override int GetHashCode ( ) => ( Id , MethodName ) . GetHashCode ( ) ;
539+ public object ? [ ] MessageArgs => [ MethodName ] ;
540540 }
541541
542542 /// <summary>Holds extracted XML documentation for a method (used only during extraction, not cached).</summary>
0 commit comments