55#if DATACUTE_EXCLUDE_EQUATABLEIMMUTABLEARRAY
66#error AttributeContextAndData requires EquatableImmutableArray (remove DATACUTE_EXCLUDE_EQUATABLEIMMUTABLEARRAY or also exclude DATACUTE_EXCLUDE_ATTRIBUTECONTEXTANDDATA)
77#endif
8+ using Microsoft . CodeAnalysis ;
9+ using Microsoft . CodeAnalysis . CSharp ;
10+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
811using System ;
12+ using System . Collections . Generic ;
913using System . Collections . Immutable ;
1014using System . Text ;
1115using System . Threading ;
12- using Microsoft . CodeAnalysis ;
13- using Microsoft . CodeAnalysis . CSharp ;
14- using Microsoft . CodeAnalysis . CSharp . Syntax ;
1516
1617namespace Datacute . IncrementalGeneratorExtensions
1718{
@@ -79,25 +80,25 @@ public AttributeContextAndData(
7980 }
8081
8182 /// <inheritdoc />
82- public bool Equals ( AttributeContextAndData < T > other )
83- {
84- return Context . Equals ( other . Context ) && Equals ( ContainingTypes , other . ContainingTypes ) && AttributeData . Equals ( other . AttributeData ) ;
85- }
83+ public bool Equals ( AttributeContextAndData < T > other ) =>
84+ Context . Equals ( other . Context ) &&
85+ Equals ( ContainingTypes , other . ContainingTypes ) &&
86+ EqualityComparer < T > . Default . Equals ( AttributeData , other . AttributeData ) &&
87+ IsInFileScopedNamespace == other . IsInFileScopedNamespace ;
8688
8789 /// <inheritdoc />
88- public override bool Equals ( object obj )
89- {
90- return obj is AttributeContextAndData < T > other && Equals ( other ) ;
91- }
90+ public override bool Equals ( object obj ) =>
91+ obj is AttributeContextAndData < T > other && Equals ( other ) ;
9292
9393 /// <inheritdoc />
9494 public override int GetHashCode ( )
9595 {
9696 unchecked
9797 {
9898 var hashCode = Context . GetHashCode ( ) ;
99- hashCode = ( hashCode * 397 ) ^ ( ContainingTypes != null ? ContainingTypes . GetHashCode ( ) : 0 ) ;
100- hashCode = ( hashCode * 397 ) ^ ( AttributeData != null ? AttributeData . GetHashCode ( ) : 0 ) ;
99+ hashCode = ( hashCode * 397 ) ^ ContainingTypes . GetHashCode ( ) ;
100+ hashCode = ( hashCode * 397 ) ^ EqualityComparer < T > . Default . GetHashCode ( AttributeData ) ;
101+ hashCode = ( hashCode * 397 ) ^ ( IsInFileScopedNamespace ? 1 : 0 ) ;
101102 return hashCode ;
102103 }
103104 }
@@ -155,11 +156,11 @@ public static bool Predicate(SyntaxNode syntaxNode, CancellationToken token)
155156 LightweightTrace . IncrementCount ( GeneratorStage . ForAttributeWithMetadataNamePredicate ) ;
156157#endif
157158 // We are only interested in partial type declarations
158- var typeDeclaration = syntaxNode as TypeDeclarationSyntax ;
159- if ( typeDeclaration == null || ! typeDeclaration . Modifiers . Any ( SyntaxKind . PartialKeyword ) )
160- {
161- return false ;
162- }
159+ if ( ! ( syntaxNode is TypeDeclarationSyntax typeDeclaration ) )
160+ return false ; // Not a type declaration
161+
162+ if ( ! typeDeclaration . Modifiers . Any ( SyntaxKind . PartialKeyword ) )
163+ return false ; // Must be partial
163164
164165 // Now, ensure all containing types are also partial.
165166 // This is necessary to be able to generate code for partial types correctly.
@@ -213,19 +214,8 @@ public static AttributeContextAndData<T> Transform(
213214 var attributeTargetSymbol = ( ITypeSymbol ) generatorAttributeSyntaxContext . TargetSymbol ;
214215 var typeDeclaration = ( TypeDeclarationSyntax ) generatorAttributeSyntaxContext . TargetNode ;
215216
216- var isInFileScopedNamespace = false ;
217- if ( typeDeclaration . SyntaxTree . GetRoot ( token ) is CompilationUnitSyntax compilationUnit )
218- {
219- foreach ( var member in compilationUnit . Members )
220- {
221- if ( member . GetType ( ) . Name == "FileScopedNamespaceDeclarationSyntax" )
222- {
223- isInFileScopedNamespace = true ;
224- break ;
225- }
226- }
227- }
228-
217+ var isInFileScopedNamespace = HasFileScopedNamespace ( typeDeclaration . SyntaxTree , token ) ;
218+
229219 var isNullableContextEnabled = GetIsNullableContextEnabled ( generatorAttributeSyntaxContext . SemanticModel , typeDeclaration . SpanStart ) ;
230220
231221 EquatableImmutableArray < string > typeParameterNames ;
@@ -241,16 +231,7 @@ public static AttributeContextAndData<T> Transform(
241231 typeParameterNames = EquatableImmutableArray < string > . Empty ;
242232 }
243233
244- var typeContext = new TypeContext (
245- TypeContext . GetNamespaceDisplayString ( attributeTargetSymbol . ContainingNamespace ) ,
246- attributeTargetSymbol . Name ,
247- attributeTargetSymbol . IsStatic ,
248- isPartial : true , // Known to be true because of the predicate
249- attributeTargetSymbol . IsAbstract ,
250- attributeTargetSymbol . IsSealed ,
251- attributeTargetSymbol . DeclaredAccessibility ,
252- TypeContext . GetTypeDeclarationKeyword ( attributeTargetSymbol ) ,
253- typeParameterNames ) ;
234+ var typeContext = CreateTypeContext ( attributeTargetSymbol , isPartial : true , typeParameterNames ) ;
254235
255236 // Parse parent classes from symbol's containing types
256237 var parentClassCount = 0 ;
@@ -275,18 +256,9 @@ public static AttributeContextAndData<T> Transform(
275256 : EquatableImmutableArray < string > . Empty ;
276257
277258 // The predicate has already confirmed that this type is partial.
278- var containingTypeIsPartial = true ;
279-
280- containingTypesImmutableArrayBuilder . Insert ( 0 , new TypeContext (
281- TypeContext . GetNamespaceDisplayString ( containingType . ContainingNamespace ) ,
282- containingType . Name ,
283- containingType . IsStatic ,
284- containingTypeIsPartial ,
285- containingType . IsAbstract ,
286- containingType . IsSealed ,
287- containingType . DeclaredAccessibility ,
288- TypeContext . GetTypeDeclarationKeyword ( containingType ) ,
289- containingTypeTypeParameterNames ) ) ;
259+ const bool containingTypeIsPartial = true ;
260+ var containingTypeContext = CreateTypeContext ( containingType , containingTypeIsPartial , containingTypeTypeParameterNames ) ;
261+ containingTypesImmutableArrayBuilder . Insert ( 0 , containingTypeContext ) ;
290262 containingType = containingType . ContainingType ;
291263 }
292264
@@ -297,19 +269,48 @@ public static AttributeContextAndData<T> Transform(
297269 containingTypes = EquatableImmutableArray < TypeContext > . Empty ;
298270 }
299271
300- var attributeContextAndData = new AttributeContextAndData < T > (
272+ return new AttributeContextAndData < T > (
301273 typeContext ,
302274 containingTypes ,
303275 attributeData ,
304276 isInFileScopedNamespace ,
305277 isNullableContextEnabled ) ;
278+ }
279+ private static TypeContext CreateTypeContext (
280+ ITypeSymbol symbol ,
281+ bool isPartial ,
282+ EquatableImmutableArray < string > typeParameterNames )
283+ {
284+ return new TypeContext (
285+ TypeContext . GetNamespaceDisplayString ( symbol . ContainingNamespace ) ,
286+ symbol . Name ,
287+ symbol . IsStatic ,
288+ isPartial ,
289+ symbol . IsAbstract ,
290+ symbol . IsSealed ,
291+ symbol . DeclaredAccessibility ,
292+ TypeContext . GetTypeDeclarationKeyword ( symbol ) ,
293+ typeParameterNames ) ;
294+ }
306295
307- return attributeContextAndData ;
296+ private static bool HasFileScopedNamespace ( SyntaxTree syntaxTree , CancellationToken token )
297+ {
298+ if ( ! ( syntaxTree . GetRoot ( token ) is CompilationUnitSyntax root ) )
299+ return false ;
300+
301+ foreach ( var member in root . Members )
302+ {
303+ if ( member . GetType ( ) . Name == "FileScopedNamespaceDeclarationSyntax" )
304+ return true ;
305+ }
306+
307+ return false ;
308308 }
309-
310-
309+
311310 // This delegate is initialized to point to the bootstrap method.
312311 // After the first run, it will point to the final, efficient implementation.
312+ // ReSharper disable once StaticMemberInGenericType There's typically only one instance.
313+ // ReSharper disable once InconsistentNaming purposely named like a method
313314 private static Func < SemanticModel , int , bool > GetIsNullableContextEnabled = BootstrapGetIsNullableContextEnabled ;
314315
315316 private static bool BootstrapGetIsNullableContextEnabled ( SemanticModel semanticModel , int position )
0 commit comments