33using Microsoft . CodeAnalysis . CSharp . Syntax ;
44using System . Collections . Immutable ;
55using System . Reflection ;
6- using System . Security . Cryptography . X509Certificates ;
7- using System . Xml . Linq ;
86
97namespace ConsoleAppFramework ;
108
@@ -39,14 +37,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3937
4038 return false ;
4139 } , ( context , ct ) => new RunContext ( ( InvocationExpressionSyntax ) context . Node , context . SemanticModel ) )
42- . WithTrackingName ( "ConsoleApp.Run.CreateSyntaxProvider " ) ; // annotate for IncrementalGeneratorTest
40+ . WithTrackingName ( "ConsoleApp.Run.0_CreateSyntaxProvider " ) ; // annotate for IncrementalGeneratorTest
4341
4442 context . RegisterSourceOutput ( runSource , EmitConsoleAppRun ) ;
4543
4644 // ConsoleAppBuilder
4745 var builderSource = context . SyntaxProvider
4846 . CreateSyntaxProvider ( ( node , ct ) =>
4947 {
48+ ct . ThrowIfCancellationRequested ( ) ;
5049 if ( node . IsKind ( SyntaxKind . InvocationExpression ) )
5150 {
5251 var invocationExpression = ( node as InvocationExpressionSyntax ) ;
@@ -66,16 +65,18 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
6665 } , ( context , ct ) => new BuilderContext (
6766 ( InvocationExpressionSyntax ) context . Node ,
6867 ( ( context . Node as InvocationExpressionSyntax ) ! . Expression as MemberAccessExpressionSyntax ) ! . Name . Identifier . Text ,
69- context . SemanticModel ) )
70- . WithTrackingName ( "ConsoleApp.Builder.CreateSyntaxProvider" )
68+ context . SemanticModel ,
69+ ct ) )
70+ . WithTrackingName ( "ConsoleApp.Builder.0_CreateSyntaxProvider" )
7171 . Where ( x =>
7272 {
73- var model = x . Model . GetTypeInfo ( ( x . Node . Expression as MemberAccessExpressionSyntax ) ! . Expression ) ;
73+ var model = x . Model . GetTypeInfo ( ( x . Node . Expression as MemberAccessExpressionSyntax ) ! . Expression , x . CancellationToken ) ;
7474 return model . Type ? . Name == "ConsoleAppBuilder" ;
7575 } )
76- . WithTrackingName ( "ConsoleApp.Builder.Where " )
76+ . WithTrackingName ( "ConsoleApp.Builder.1_Where " )
7777 . Collect ( )
78- . WithTrackingName ( "ConsoleApp.Builder.Collect" ) ;
78+ . WithComparer ( CollectBuilderContextComparer . Default )
79+ . WithTrackingName ( "ConsoleApp.Builder.2_Collect" ) ;
7980
8081 context . RegisterSourceOutput ( builderSource , EmitConsoleAppBuilder ) ;
8182 }
@@ -783,7 +784,8 @@ public static bool DelegateEquals(InvocationExpressionSyntax node, SemanticModel
783784 {
784785 // check async, returntype, parameters
785786
786- var lambda2 = ( ParenthesizedLambdaExpressionSyntax ) args2 [ 1 ] . Expression ;
787+ var lambda2 = args2 [ 1 ] . Expression as ParenthesizedLambdaExpressionSyntax ;
788+ if ( lambda2 == null ) return false ;
787789
788790 if ( ! lambda1 . AsyncKeyword . IsKind ( lambda2 . AsyncKeyword . Kind ( ) ) ) return false ;
789791
@@ -800,8 +802,8 @@ public static bool DelegateEquals(InvocationExpressionSyntax node, SemanticModel
800802 ImmutableArray < ISymbol > methodSymbols2 ;
801803 if ( expression . IsKind ( SyntaxKind . AddressOfExpression ) )
802804 {
803- var operand1 = ( expression as PrefixUnaryExpressionSyntax ) ! . Operand ;
804- var operand2 = ( args2 [ 1 ] . Expression as PrefixUnaryExpressionSyntax ) ! . Operand ;
805+ var operand1 = ( expression as PrefixUnaryExpressionSyntax ) ? . Operand ;
806+ var operand2 = ( args2 [ 1 ] . Expression as PrefixUnaryExpressionSyntax ) ? . Operand ;
805807 if ( operand1 == null || operand2 == null ) return false ;
806808
807809 methodSymbols1 = model . GetMemberGroup ( operand1 ) ;
@@ -854,35 +856,62 @@ public static bool MethodSymbolEquals(IMethodSymbol methodSymbol1, IMethodSymbol
854856 }
855857 }
856858
857- readonly struct BuilderContext ( InvocationExpressionSyntax node , string name , SemanticModel model ) : IEquatable < BuilderContext >
859+ public class CollectBuilderContextComparer : IEqualityComparer < ImmutableArray < BuilderContext > >
858860 {
859- public InvocationExpressionSyntax Node => node ;
860- public string Name => name ;
861- public SemanticModel Model => model ;
861+ public static CollectBuilderContextComparer Default = new CollectBuilderContextComparer ( ) ;
862862
863- public bool Equals ( BuilderContext other )
863+ bool IEqualityComparer < ImmutableArray < BuilderContext > > . Equals ( ImmutableArray < BuilderContext > x , ImmutableArray < BuilderContext > y )
864+ {
865+ if ( x . Length != y . Length ) return false ;
866+
867+ for ( int i = 0 ; i < x . Length ; i ++ )
868+ {
869+ if ( ! Equals ( x [ i ] , y [ i ] ) ) return false ;
870+ }
871+
872+ return true ;
873+ }
874+
875+ int IEqualityComparer < ImmutableArray < BuilderContext > > . GetHashCode ( ImmutableArray < BuilderContext > obj )
876+ {
877+ return 0 ;
878+ }
879+
880+
881+ static bool Equals ( BuilderContext self , BuilderContext other )
864882 {
865- if ( this . Name != other . Name ) return false ;
883+ if ( self . Name != other . Name ) return false ;
866884
867- var typeInfo = Model . GetTypeInfo ( ( Node . Expression as MemberAccessExpressionSyntax ) ! . Expression ) ;
885+ var typeInfo = self . Model . GetTypeInfo ( ( self . Node . Expression as MemberAccessExpressionSyntax ) ! . Expression , self . CancellationToken ) ;
868886 if ( typeInfo . Type ? . Name != "ConsoleAppBuilder" )
869887 {
870888 return false ;
871889 }
872890
873- switch ( Name )
891+ var typeInfo2 = other . Model . GetTypeInfo ( ( other . Node . Expression as MemberAccessExpressionSyntax ) ! . Expression , other . CancellationToken ) ;
892+ if ( typeInfo2 . Type ? . Name != "ConsoleAppBuilder" )
893+ {
894+ return false ;
895+ }
896+
897+ switch ( self . Name )
874898 {
875899 case "Add" : // Add or Add<T>
876- if ( ( Node . Expression as MemberAccessExpressionSyntax ) ? . Name . IsKind ( SyntaxKind . GenericName ) ?? false )
900+ if ( ( self . Node . Expression as MemberAccessExpressionSyntax ) ? . Name . IsKind ( SyntaxKind . GenericName ) ?? false )
877901 {
878- return EqualsAddClass ( other ) ;
902+ return EqualsAddClass ( self , other ) ;
879903 }
880904 else
881905 {
882- return RunContext . DelegateEquals ( node , model , ( other . Node , other . Model ) ) ;
906+ var first = GetFirstStringConstant ( self . Node ) ;
907+ var second = GetFirstStringConstant ( other . Node ) ;
908+ if ( first == null || second == null ) return false ;
909+ if ( first != second ) return false ;
910+
911+ return RunContext . DelegateEquals ( self . Node , self . Model , ( other . Node , other . Model ) ) ;
883912 }
884913 case "UseFilter" :
885- return EqualsUseFilter ( other ) ;
914+ return EqualsUseFilter ( self , other ) ;
886915 case "Run" :
887916 case "RunAsync" :
888917 return true ; // only check name
@@ -893,10 +922,27 @@ public bool Equals(BuilderContext other)
893922 return false ;
894923 }
895924
896- bool EqualsAddClass ( BuilderContext other )
925+ static string ? GetFirstStringConstant ( InvocationExpressionSyntax invocationExpression )
926+ {
927+ if ( invocationExpression . ArgumentList . Arguments . Count != 2 ) return null ;
928+ var commandName = invocationExpression . ArgumentList . Arguments [ 0 ] ;
929+
930+ if ( ! commandName . Expression . IsKind ( SyntaxKind . StringLiteralExpression ) )
931+ {
932+ return null ;
933+ }
934+
935+ var name = ( commandName . Expression as LiteralExpressionSyntax ) ! . Token . ValueText ;
936+ return name ;
937+ }
938+
939+ static bool EqualsAddClass ( BuilderContext self , BuilderContext other )
897940 {
898- var typeAndPath1 = GetTypeSymbolAndPath ( node , model ) ;
899- var typeAndPath2 = GetTypeSymbolAndPath ( other . Node , other . Model ) ;
941+ var node = self . Node ;
942+ var model = self . Model ;
943+
944+ var typeAndPath1 = GetTypeSymbolAndPath ( node , model , self . CancellationToken ) ;
945+ var typeAndPath2 = GetTypeSymbolAndPath ( other . Node , other . Model , other . CancellationToken ) ;
900946
901947 if ( typeAndPath1 == null || typeAndPath2 == null ) return false ;
902948
@@ -905,19 +951,44 @@ bool EqualsAddClass(BuilderContext other)
905951
906952 if ( path1 != path2 ) return false ;
907953
908- // TODO:
954+ if ( type1 . DeclaringSyntaxReferences . Length == 0 ) return false ;
955+ if ( type2 . DeclaringSyntaxReferences . Length == 0 ) return false ;
956+
957+ var syntax1 = type1 . DeclaringSyntaxReferences [ 0 ] . GetSyntax ( ) as TypeDeclarationSyntax ;
958+ var syntax2 = type2 . DeclaringSyntaxReferences [ 0 ] . GetSyntax ( ) as TypeDeclarationSyntax ;
959+
960+ if ( syntax1 == null || syntax2 == null ) return false ;
909961
910- // Type:Attributes
911- // Type:Interface
962+ // interface
963+ if ( ! type1 . AllInterfaces . Select ( x => x . Name ) . SequenceEqual ( type2 . AllInterfaces . Select ( x => x . Name ) ) )
964+ {
965+ return false ;
966+ }
912967
913968 // Public Constructor
969+ var ctor1 = type1 . GetMembers ( ) . FirstOrDefault ( x => ( x as IMethodSymbol ) ? . MethodKind == Microsoft . CodeAnalysis . MethodKind . Constructor ) ;
970+ var ctor2 = type2 . GetMembers ( ) . FirstOrDefault ( x => ( x as IMethodSymbol ) ? . MethodKind == Microsoft . CodeAnalysis . MethodKind . Constructor ) ;
971+ var ctorParameter1 = ctor1 ? . DeclaringSyntaxReferences . FirstOrDefault ( ) ? . GetSyntax ( ) . GetParameterListOfConstructor ( ) ;
972+ var ctorParameter2 = ctor2 ? . DeclaringSyntaxReferences . FirstOrDefault ( ) ? . GetSyntax ( ) . GetParameterListOfConstructor ( ) ;
973+
974+ if ( ! SyntaxNodeTextEqualityComparer . Default . Equals ( ctorParameter1 , ctorParameter2 ) )
975+ {
976+ return false ;
977+ }
978+
979+ // attributes
980+ if ( ! SyntaxNodeTextEqualityComparer . Default . Equals ( syntax1 . AttributeLists ! , syntax2 . AttributeLists ! ) )
981+ {
982+ return false ;
983+ }
914984
915985 // Public Methods
916986 var methods1 = GetMethodSymbols ( type1 ) ;
917987 var methods2 = GetMethodSymbols ( type2 ) ;
918- return methods1 . ZipEquals ( methods2 , RunContext . MethodSymbolEquals ) ;
988+ var methodEquals = methods1 . ZipEquals ( methods2 , RunContext . MethodSymbolEquals ) ;
989+ return methodEquals ;
919990
920- static ( ITypeSymbol , string ? ) ? GetTypeSymbolAndPath ( InvocationExpressionSyntax node , SemanticModel model )
991+ static ( ITypeSymbol , string ? ) ? GetTypeSymbolAndPath ( InvocationExpressionSyntax node , SemanticModel model , CancellationToken cancellationToken )
921992 {
922993 // Add<T>
923994 var genericName = ( node . Expression as MemberAccessExpressionSyntax ) ? . Name as GenericNameSyntax ;
@@ -938,40 +1009,49 @@ bool EqualsAddClass(BuilderContext other)
9381009 }
9391010
9401011 // T
941- var type = model . GetTypeInfo ( genericType ) . Type ! ;
1012+ var type = model . GetTypeInfo ( genericType , cancellationToken ) . Type ! ;
9421013 return ( type , commandPath ) ;
9431014 }
9441015
9451016 static IEnumerable < IMethodSymbol > GetMethodSymbols ( ITypeSymbol type )
9461017 {
9471018 return type . GetMembers ( )
948- . Where ( x => x . DeclaredAccessibility == Accessibility . Public )
9491019 . OfType < IMethodSymbol > ( )
9501020 . Where ( x => x . DeclaredAccessibility == Accessibility . Public && ! x . IsStatic )
9511021 . Where ( x => x . MethodKind == Microsoft . CodeAnalysis . MethodKind . Ordinary )
9521022 . Where ( x => ! ( x . Name is "Dispose" or "DisposeAsync" or "GetHashCode" or "Equals" or "ToString" ) ) ;
9531023 }
9541024 }
9551025
956- bool EqualsUseFilter ( BuilderContext other )
1026+ static bool EqualsUseFilter ( BuilderContext self , BuilderContext other )
9571027 {
958- var l = GetType ( Node , model ) ;
959- var r = GetType ( other . Node , other . Model ) ;
1028+ var node = self . Node ;
1029+ var model = self . Model ;
1030+
1031+ var l = GetType ( node , model , self . CancellationToken ) ;
1032+ var r = GetType ( other . Node , other . Model , other . CancellationToken ) ;
9601033
9611034 return l . EqualsNamespaceAndName ( r ) ;
9621035
963- static ITypeSymbol ? GetType ( InvocationExpressionSyntax expression , SemanticModel model )
1036+ static ITypeSymbol ? GetType ( InvocationExpressionSyntax expression , SemanticModel model , CancellationToken cancellationToken )
9641037 {
9651038 var genericName = ( expression . Expression as MemberAccessExpressionSyntax ) ? . Name as GenericNameSyntax ;
9661039 var genericType = genericName ! . TypeArgumentList . Arguments [ 0 ] ;
967- return model . GetTypeInfo ( genericType ) . Type ;
1040+ return model . GetTypeInfo ( genericType , cancellationToken ) . Type ;
9681041 }
9691042 }
1043+ }
9701044
971- public override int GetHashCode ( )
1045+ readonly struct BuilderContext ( InvocationExpressionSyntax node , string name , SemanticModel model , CancellationToken cancellationToken ) : IEquatable < BuilderContext >
1046+ {
1047+ public InvocationExpressionSyntax Node => node ;
1048+ public string Name => name ;
1049+ public SemanticModel Model => model ;
1050+ public CancellationToken CancellationToken => cancellationToken ;
1051+
1052+ public bool Equals ( BuilderContext other )
9721053 {
973- // maybe this does not called so don't care impl.
974- return SyntaxNodeTextEqualityComparer . Default . GetHashCode ( node ) ;
1054+ return Node == other . Node ;
9751055 }
9761056 }
977- }
1057+ }
0 commit comments