1+ using System . Collections . Immutable ;
2+ using System . Composition ;
3+ using Microsoft . CodeAnalysis ;
4+ using Microsoft . CodeAnalysis . CodeActions ;
5+ using Microsoft . CodeAnalysis . CodeFixes ;
6+ using Microsoft . CodeAnalysis . CSharp ;
7+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
8+ using Microsoft . CodeAnalysis . Text ;
9+
10+ namespace AutoMapper . Analyzers . Common ;
11+
12+ [ ExportCodeFixProvider ( LanguageNames . CSharp , Name = nameof ( FlattingComplexModelCodeFixProvider ) ) , Shared ]
13+ public class FlattingComplexModelCodeFixProvider : CodeFixProvider
14+ {
15+ public override ImmutableArray < string > FixableDiagnosticIds => ImmutableArray . Create ( FlattingComplexModelAnalyzer . DiagnosticId ) ;
16+
17+ public override FixAllProvider GetFixAllProvider ( ) => WellKnownFixAllProviders . BatchFixer ;
18+
19+ public override async Task RegisterCodeFixesAsync ( CodeFixContext context )
20+ {
21+ var root = await context . Document . GetSyntaxRootAsync ( context . CancellationToken ) . ConfigureAwait ( false ) ;
22+
23+ var diagnostic = context . Diagnostics . First ( ) ;
24+ var diagnosticSpans = new List < TextSpan > { diagnostic . Location . SourceSpan } ;
25+ diagnosticSpans . AddRange ( diagnostic . AdditionalLocations . Select ( al => al . SourceSpan ) ) ;
26+
27+ var declarations = diagnosticSpans . Select ( s =>
28+ root . FindToken ( s . Start ) . Parent . Ancestors ( ) . OfType < InvocationExpressionSyntax > ( ) . First ( ) ) ;
29+
30+ context . RegisterCodeFix (
31+ CodeAction . Create ( "Replace manual complex flatting by IncludeMembers call" ,
32+ c => UseIncludeMembers ( context . Document , declarations , c ) , "FlattingComplexModelFixTitle" ) , diagnostic ) ;
33+ }
34+
35+ private async Task < Document > UseIncludeMembers ( Document document ,
36+ IEnumerable < InvocationExpressionSyntax > declarations , CancellationToken cancellationToken )
37+ {
38+ var syntaxRoot = await document . GetSyntaxRootAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
39+ syntaxRoot = syntaxRoot . ReplaceNodes ( declarations . Select ( d => syntaxRoot . FindNode ( d . Span ) ) ,
40+ ( _ , syntaxNode ) => syntaxNode . DescendantNodes ( ) . OfType < InvocationExpressionSyntax > ( ) . FirstOrDefault ( ) ) ;
41+
42+ var lambda = BuildLambdaExpression ( declarations ) ;
43+ var includeMembers = GetIncludeMembersInvocation ( declarations , ref syntaxRoot ) ;
44+
45+ var argumentList = new List < ArgumentSyntax > ( includeMembers . ArgumentList . Arguments ) { SyntaxFactory . Argument ( lambda ) } ;
46+
47+ var includeCall = SyntaxFactory . InvocationExpression ( includeMembers . Expression ,
48+ SyntaxFactory . ArgumentList ( SyntaxFactory . SeparatedList ( argumentList ) ) ) ;
49+
50+ return document . WithSyntaxRoot ( syntaxRoot . ReplaceNode ( includeMembers , includeCall ) . NormalizeWhitespace ( ) ) ;
51+ }
52+
53+ private InvocationExpressionSyntax GetIncludeMembersInvocation ( IEnumerable < InvocationExpressionSyntax > declarations , ref SyntaxNode ? syntaxRoot )
54+ {
55+ var invocationExpressions = syntaxRoot . FindToken ( declarations . ElementAt ( 0 ) . SpanStart ) . Parent . Ancestors ( ) . OfType < InvocationExpressionSyntax > ( ) ;
56+ var includeMembersName = nameof ( IMappingExpression . IncludeMembers ) ;
57+ var includeMembers = invocationExpressions . FirstOrDefault ( i => i . ToString ( ) . Contains ( includeMembersName ) ) ;
58+ if ( includeMembers == null )
59+ {
60+ SyntaxNode createMap = invocationExpressions . Where ( i => i . Expression is GenericNameSyntax ) . FirstOrDefault ( i => i . ToString ( ) . StartsWith ( nameof ( Profile . CreateMap ) ) ) ;
61+ var newCreateMapString = createMap . Parent . ToFullString ( ) . Replace ( createMap . ToFullString ( ) . Trim ( ) , $ "{ createMap . ToFullString ( ) } .{ includeMembersName } ()") ;
62+ var newCreateMap = SyntaxFactory . ParseExpression ( newCreateMapString ) ;
63+ if ( createMap . Parent is MemberAccessExpressionSyntax )
64+ {
65+ createMap = createMap . Parent ;
66+ }
67+ syntaxRoot = syntaxRoot . ReplaceNode ( createMap , newCreateMap ) ;
68+ includeMembers = syntaxRoot . FindToken ( declarations . ElementAt ( 0 ) . SpanStart ) . Parent . Ancestors ( ) . OfType < InvocationExpressionSyntax > ( )
69+ . First ( i => i . ToString ( ) . Contains ( includeMembersName ) ) ;
70+ }
71+
72+ return includeMembers ;
73+ }
74+
75+ private static SimpleLambdaExpressionSyntax BuildLambdaExpression ( IEnumerable < InvocationExpressionSyntax > declarations )
76+ {
77+ var srcProperty =
78+ ForMemberAnalyzer . GetLambdaExpressions ( declarations . ElementAt ( 0 ) ) . srcExpression as SimpleLambdaExpressionSyntax ;
79+ var srcCall = srcProperty . ExpressionBody . ToFullString ( ) ;
80+ srcCall = srcCall . Substring ( 0 , srcCall . LastIndexOf ( '.' ) ) ;
81+ var srcName = srcProperty . Parameter . Identifier . Text ;
82+
83+ return SyntaxFactory . SimpleLambdaExpression ( SyntaxFactory . Parameter ( SyntaxFactory . ParseToken ( srcName ) ) ,
84+ SyntaxFactory . ParseExpression ( srcCall ) ) ;
85+ }
86+ }
0 commit comments