1+ using System . Collections . Generic ;
2+ using System . Collections . Immutable ;
3+ using System . Composition ;
4+ using System . Linq ;
5+ using FluentAssertions . Analyzers . Utilities ;
6+ using Microsoft . CodeAnalysis ;
7+ using Microsoft . CodeAnalysis . CodeFixes ;
8+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
9+ using Microsoft . CodeAnalysis . Diagnostics ;
10+ using SF = Microsoft . CodeAnalysis . CSharp . SyntaxFactory ;
11+
12+ namespace FluentAssertions . Analyzers . Xunit ;
13+
14+ [ DiagnosticAnalyzer ( LanguageNames . CSharp ) ]
15+ public class AssertIsAssignableFromAnalyzer : XunitAnalyzer
16+ {
17+ public const string DiagnosticId = Constants . Tips . Xunit . AssertIsAssignableFrom ;
18+ public const string Category = Constants . Tips . Category ;
19+
20+ public const string Message = "Use .Should().BeAssignableTo()." ;
21+
22+ protected override DiagnosticDescriptor Rule => new ( DiagnosticId , Title , Message , Category , DiagnosticSeverity . Info , true ) ;
23+
24+ protected override IEnumerable < FluentAssertionsCSharpSyntaxVisitor > Visitors => new FluentAssertionsCSharpSyntaxVisitor [ ]
25+ {
26+ new AssertIsAssignableFromGenericTypeSyntaxVisitor ( ) ,
27+ new AssertIsAssignableFromTypeSyntaxVisitor ( )
28+ } ;
29+
30+ //public static T IsAssignableFrom<T>(object? @object)
31+ public class AssertIsAssignableFromGenericTypeSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
32+ {
33+ public AssertIsAssignableFromGenericTypeSyntaxVisitor ( ) : base (
34+ MemberValidator . HasArguments ( "IsAssignableFrom" , 1 )
35+ )
36+ {
37+ }
38+ }
39+
40+ //public static T IsAssignableFrom(Type expectedType, object? @object)
41+ public class AssertIsAssignableFromTypeSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
42+ {
43+ public AssertIsAssignableFromTypeSyntaxVisitor ( ) : base (
44+ MemberValidator . HasArguments ( "IsAssignableFrom" , 2 )
45+ )
46+ {
47+ }
48+ }
49+ }
50+
51+ [ ExportCodeFixProvider ( LanguageNames . CSharp , Name = nameof ( AssertIsAssignableFromCodeFix ) ) , Shared ]
52+ public class AssertIsAssignableFromCodeFix : XunitCodeFixProvider
53+ {
54+ public override ImmutableArray < string > FixableDiagnosticIds => ImmutableArray . Create ( AssertIsAssignableFromAnalyzer . DiagnosticId ) ;
55+
56+ protected override ExpressionSyntax GetNewExpression (
57+ ExpressionSyntax expression ,
58+ FluentAssertionsDiagnosticProperties properties )
59+ {
60+ switch ( properties . VisitorName )
61+ {
62+ case nameof ( AssertIsAssignableFromAnalyzer . AssertIsAssignableFromGenericTypeSyntaxVisitor ) :
63+ return RenameMethodAndReorderActualExpectedAndReplaceWithSubjectShould ( expression , "IsAssignableFrom" , "BeAssignableTo" ) ;
64+ case nameof ( AssertIsAssignableFromAnalyzer . AssertIsAssignableFromTypeSyntaxVisitor ) :
65+ var newExpression = RenameMethodAndReorderActualExpectedAndReplaceWithSubjectShould ( expression , "IsAssignableFrom" , "BeAssignableTo" ) ;
66+
67+ var beAssignableTo = newExpression . DescendantNodes ( )
68+ . OfType < MemberAccessExpressionSyntax > ( )
69+ . First ( node => node . Name . Identifier . Text == "BeAssignableTo" ) ;
70+
71+ if ( beAssignableTo . Parent is InvocationExpressionSyntax invocation )
72+ {
73+ var arguments = invocation . ArgumentList . Arguments ;
74+ if ( arguments . Any ( ) && arguments [ 0 ] . Expression is TypeOfExpressionSyntax typeOfExpression )
75+ {
76+ var genericBeOfType = beAssignableTo . WithName ( SF . GenericName ( beAssignableTo . Name . Identifier . Text )
77+ . AddTypeArgumentListArguments ( typeOfExpression . Type )
78+ ) ;
79+ newExpression = newExpression . ReplaceNode ( beAssignableTo , genericBeOfType ) ;
80+ return GetNewExpression ( newExpression , NodeReplacement . RemoveFirstArgument ( "BeAssignableTo" ) ) ;
81+ }
82+ }
83+
84+ return newExpression ;
85+ default :
86+ throw new System . InvalidOperationException ( $ "Invalid visitor name - { properties . VisitorName } ") ;
87+ }
88+ }
89+ }
0 commit comments