@@ -33,6 +33,14 @@ namespace Datacute.IncrementalGeneratorExtensions
3333 /// The data associated with the attribute, which is typically collected from the attribute's syntax context.
3434 /// </summary>
3535 public readonly T AttributeData ;
36+ /// <summary>
37+ /// Indicates whether the attribute is in a file with a file-scoped namespace.
38+ /// </summary>
39+ public readonly bool IsInFileScopedNamespace ;
40+ /// <summary>
41+ /// True if the nullable context is enabled at the location of the attribute.
42+ /// </summary>
43+ public readonly bool IsNullableContextEnabled ;
3644
3745 /// <summary>
3846 /// Indicates whether the containing namespace is the global namespace.
@@ -53,14 +61,20 @@ namespace Datacute.IncrementalGeneratorExtensions
5361 /// <param name="context">The context of the type to which the attribute is applied.</param>
5462 /// <param name="containingTypes">A collection of contexts for the containing types of the attribute's target symbol.</param>
5563 /// <param name="attributeData">The data associated with the attribute, which is typically collected from the attribute's syntax context.</param>
64+ /// <param name="isInFileScopedNamespace">True if the attribute is in a file with a file-scoped namespace</param>
65+ /// <param name="isNullableContextEnabled">True if the nullable context is enabled at the location of the attribute.</param>
5666 public AttributeContextAndData (
5767 TypeContext context ,
5868 EquatableImmutableArray < TypeContext > containingTypes ,
59- T attributeData )
69+ T attributeData ,
70+ bool isInFileScopedNamespace ,
71+ bool isNullableContextEnabled )
6072 {
6173 Context = context ;
6274 ContainingTypes = containingTypes ;
6375 AttributeData = attributeData ;
76+ IsInFileScopedNamespace = isInFileScopedNamespace ;
77+ IsNullableContextEnabled = isNullableContextEnabled ;
6478 }
6579
6680 /// <inheritdoc />
@@ -198,6 +212,21 @@ public static AttributeContextAndData<T> Transform(
198212 var attributeTargetSymbol = ( ITypeSymbol ) generatorAttributeSyntaxContext . TargetSymbol ;
199213 var typeDeclaration = ( TypeDeclarationSyntax ) generatorAttributeSyntaxContext . TargetNode ;
200214
215+ var isInFileScopedNamespace = false ;
216+ if ( typeDeclaration . SyntaxTree . GetRoot ( token ) is CompilationUnitSyntax compilationUnit )
217+ {
218+ foreach ( var member in compilationUnit . Members )
219+ {
220+ if ( member . GetType ( ) . Name == "FileScopedNamespaceDeclarationSyntax" )
221+ {
222+ isInFileScopedNamespace = true ;
223+ break ;
224+ }
225+ }
226+ }
227+
228+ var isNullableContextEnabled = GetIsNullableContextEnabled ( generatorAttributeSyntaxContext . SemanticModel , typeDeclaration . SpanStart ) ;
229+
201230 EquatableImmutableArray < string > typeParameterNames ;
202231 if ( generatorAttributeSyntaxContext . TargetSymbol is INamedTypeSymbol namedTypeTargetSymbol )
203232 {
@@ -270,10 +299,43 @@ public static AttributeContextAndData<T> Transform(
270299 var attributeContextAndData = new AttributeContextAndData < T > (
271300 typeContext ,
272301 containingTypes ,
273- attributeData ) ;
302+ attributeData ,
303+ isInFileScopedNamespace ,
304+ isNullableContextEnabled ) ;
274305
275306 return attributeContextAndData ;
276307 }
308+
309+
310+ // This delegate is initialized to point to the bootstrap method.
311+ // After the first run, it will point to the final, efficient implementation.
312+ private static Func < SemanticModel , int , bool > GetIsNullableContextEnabled = BootstrapGetIsNullableContextEnabled ;
313+
314+ private static bool BootstrapGetIsNullableContextEnabled ( SemanticModel semanticModel , int position )
315+ {
316+ var getNullableContextMethod = typeof ( SemanticModel ) . GetMethod ( "GetNullableContext" , new [ ] { typeof ( int ) } ) ;
317+
318+ if ( getNullableContextMethod != null )
319+ {
320+ // The GetNullableContext method exists.
321+ // Replace the delegate with an implementation that uses reflection to get the value.
322+ GetIsNullableContextEnabled = ( sm , pos ) =>
323+ {
324+ var nullableContext = getNullableContextMethod . Invoke ( sm , new object [ ] { pos } ) ;
325+ // NullableContext.AnnotationsEnabled is 1 << 1 = 2
326+ return ( ( int ) nullableContext & 2 ) != 0 ;
327+ } ;
328+ }
329+ else
330+ {
331+ // The method doesn't exist on this version of Roslyn.
332+ // Replace the delegate with an implementation that always returns false.
333+ GetIsNullableContextEnabled = ( sm , pos ) => false ;
334+ }
335+
336+ // Call the newly assigned delegate to return the result for the current invocation.
337+ return GetIsNullableContextEnabled ( semanticModel , position ) ;
338+ }
277339 }
278340}
279341#endif
0 commit comments