11using System ;
2- using System . Collections . Generic ;
32using System . Linq ;
43using System . Threading ;
54using System . Threading . Tasks ;
65using ICSharpCode . CodeConverter . Util ;
76using Microsoft . CodeAnalysis ;
87using Microsoft . CodeAnalysis . CSharp ;
9- using Microsoft . CodeAnalysis . CSharp . Syntax ;
10- using Microsoft . CodeAnalysis . Options ;
8+ using Microsoft . CodeAnalysis . Operations ;
119using Microsoft . CodeAnalysis . Simplification ;
1210using VBasic = Microsoft . CodeAnalysis . VisualBasic ;
1311using VBSyntax = Microsoft . CodeAnalysis . VisualBasic . Syntax ;
@@ -17,32 +15,157 @@ namespace ICSharpCode.CodeConverter.CSharp
1715{
1816 internal static class DocumentExtensions
1917 {
20- public static async Task < Document > WithSimplifiedSyntaxRootAsync ( this Document doc , SyntaxNode syntaxRoot = null )
18+ public static async Task < Document > SimplifyStatements < TUsingDirectiveSyntax , TExpressionSyntax > ( this Document convertedDocument , string unresolvedTypeDiagnosticId )
19+ where TUsingDirectiveSyntax : SyntaxNode where TExpressionSyntax : SyntaxNode
20+ {
21+ var originalRoot = await convertedDocument . GetSyntaxRootAsync ( ) ;
22+ var nodesWithUnresolvedTypes = ( await convertedDocument . GetSemanticModelAsync ( ) ) . GetDiagnostics ( )
23+ . Where ( d => d . Id == unresolvedTypeDiagnosticId && d . Location . IsInSource )
24+ . Select ( d => originalRoot . FindNode ( d . Location . SourceSpan ) . GetAncestor < TUsingDirectiveSyntax > ( ) )
25+ . ToLookup ( d => ( SyntaxNode ) d ) ;
26+
27+ var toSimplify = originalRoot
28+ . DescendantNodes ( n => ! ( n is TExpressionSyntax ) && ! nodesWithUnresolvedTypes . Contains ( n ) )
29+ . Where ( n => ! nodesWithUnresolvedTypes . Contains ( n ) ) ;
30+ var newRoot = originalRoot . ReplaceNodes ( toSimplify , ( orig , rewritten ) =>
31+ rewritten . WithAdditionalAnnotations ( Simplifier . Annotation )
32+ ) ;
33+
34+ var document = await convertedDocument . WithReducedRootAsync ( newRoot . WithAdditionalAnnotations ( Simplifier . Annotation ) ) ;
35+ return document ;
36+ }
37+
38+ public static async Task < Document > WithExpandedRootAsync ( this Document document )
2139 {
22- var root = syntaxRoot ?? await doc . GetSyntaxRootAsync ( ) ;
23- var withSyntaxRoot = doc . WithSyntaxRoot ( root . WithAdditionalAnnotations ( Simplifier . Annotation ) ) ;
40+ var shouldExpand = document . Project . Language == LanguageNames . VisualBasic
41+ ? ( Func < SemanticModel , SyntaxNode , bool > ) ShouldExpandVbNode
42+ : ShouldExpandCsNode ;
43+ document = await WorkaroundBugsInExpandVbAsync ( document , shouldExpand ) ;
44+
45+ #if SimplifierBugsAreFixed //See https://github.com/icsharpcode/CodeConverter/pull/449 and https://github.com/icsharpcode/CodeConverter/pull/464
46+ document = await ExpandVbAsync ( document , shouldExpand ) ;
47+ document = await UndoVbExpansionsHardToReverseInCSharpSemanticModel ( document ) ;
48+ #endif
49+ return document ;
50+ }
51+
52+ private static async Task < Document > WorkaroundBugsInExpandVbAsync ( Document document , Func < SemanticModel , SyntaxNode , bool > shouldExpand )
53+ {
54+ var semanticModel = await document . GetSemanticModelAsync ( ) ;
55+ var root = ( VBasic . VisualBasicSyntaxNode ) await document . GetSyntaxRootAsync ( ) ;
56+
57+ try {
58+ var newRoot = root . ReplaceNodes ( root . DescendantNodes ( n => ! shouldExpand ( semanticModel , n ) ) . Where ( n => shouldExpand ( semanticModel , n ) ) ,
59+ ( node , rewrittenNode ) => {
60+ var symbol = semanticModel . GetSymbolInfo ( node ) . Symbol ;
61+ if ( rewrittenNode is VBSyntax . SimpleNameSyntax sns && IsMyBaseBug ( semanticModel , root , node , symbol ) && semanticModel . GetOperation ( node ) is IMemberReferenceOperation mro ) {
62+ return VBasic . SyntaxFactory . MemberAccessExpression ( VBasic . SyntaxKind . SimpleMemberAccessExpression ,
63+ ( VBSyntax . ExpressionSyntax ) mro . Instance . Syntax ,
64+ VBasic . SyntaxFactory . Token ( VBasic . SyntaxKind . DotToken ) ,
65+ sns ) ;
66+ } ;
67+ return rewrittenNode ;
68+ } ) ;
69+ return document . WithSyntaxRoot ( newRoot ) ;
70+ } catch ( Exception ) {
71+ return document . WithSyntaxRoot ( root ) ;
72+ }
73+ }
74+
75+ /// <returns>True iff calling Expand would qualify with MyBase when the symbol isn't in the base type
76+ /// See https://github.com/dotnet/roslyn/blob/97123b393c3a5a91cc798b329db0d7fc38634784/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.Expander.vb#L657</returns>
77+ private static bool IsMyBaseBug ( SemanticModel semanticModel , VBasic . VisualBasicSyntaxNode root , SyntaxNode node ,
78+ ISymbol symbol )
79+ {
80+ if ( symbol ? . IsStatic == false && ( symbol . Kind == SymbolKind . Method || symbol . Kind ==
81+ SymbolKind . Field || symbol . Kind == SymbolKind . Property ) )
82+ {
83+ INamedTypeSymbol nodeEnclosingNamedType = GetEnclosingNamedType ( semanticModel , root , node . SpanStart ) ;
84+ if ( ! Equals ( nodeEnclosingNamedType , symbol . ContainingType ) ) {
85+ return ! Equals ( nodeEnclosingNamedType , symbol . ContainingType ? . BaseType ) ;
86+ }
87+ }
88+
89+ return false ;
90+ }
91+
92+ /// <summary>
93+ /// Pasted from AbstractGenerateFromMembersCodeRefactoringProvider
94+ /// Gets the enclosing named type for the specified position. We can't use
95+ /// <see cref="SemanticModel.GetEnclosingSymbol"/> because that doesn't return
96+ /// the type you're current on if you're on the header of a class/interface.
97+ /// </summary>
98+ private static INamedTypeSymbol GetEnclosingNamedType (
99+ SemanticModel semanticModel , SyntaxNode root , int start , CancellationToken cancellationToken = default ( CancellationToken ) )
100+ {
101+ var token = root . FindToken ( start ) ;
102+ if ( token == ( ( ICompilationUnitSyntax ) root ) . EndOfFileToken ) {
103+ token = token . GetPreviousToken ( ) ;
104+ }
105+
106+ for ( var node = token . Parent ; node != null ; node = node . Parent ) {
107+ if ( semanticModel . GetDeclaredSymbol ( node ) is INamedTypeSymbol declaration ) {
108+ return declaration ;
109+ }
110+ }
111+
112+ return null ;
113+ }
114+
115+ private static async Task < Document > ExpandVbAsync ( Document document , Func < SemanticModel , SyntaxNode , bool > shouldExpand )
116+ {
117+ var semanticModel = await document . GetSemanticModelAsync ( ) ;
118+ var workspace = document . Project . Solution . Workspace ;
119+ var root = ( VBasic . VisualBasicSyntaxNode ) await document . GetSyntaxRootAsync ( ) ;
120+ try {
121+ var newRoot = root . ReplaceNodes ( root . DescendantNodes ( n => ! shouldExpand ( semanticModel , n ) ) . Where ( n => shouldExpand ( semanticModel , n ) ) ,
122+ ( node , rewrittenNode ) => TryExpandNode ( node , semanticModel , workspace )
123+ ) ;
124+ return document . WithSyntaxRoot ( newRoot ) ;
125+ } catch ( Exception ) {
126+ return document . WithSyntaxRoot ( root ) ;
127+ }
128+ }
129+ private static async Task < Document > UndoVbExpansionsHardToReverseInCSharpSemanticModel ( Document document )
130+ {
131+ var root = ( VBasic . VisualBasicSyntaxNode ) await document . GetSyntaxRootAsync ( ) ;
132+ var toSimplify = root . DescendantNodes ( )
133+ . Where ( n => n . IsKind ( VBasic . SyntaxKind . PredefinedCastExpression , VBasic . SyntaxKind . CTypeExpression , VBasic . SyntaxKind . DirectCastExpression ) )
134+ . Where ( n => n . HasAnnotation ( Simplifier . Annotation ) ) ;
135+ root = root . ReplaceNodes ( toSimplify , ( orig , rewritten ) =>
136+ rewritten . WithAdditionalAnnotations ( Simplifier . Annotation )
137+ ) ;
138+ return await document . WithReducedRootAsync ( root ) ;
139+ }
140+ private static async Task < Document > WithReducedRootAsync ( this Document doc , SyntaxNode syntaxRoot = null )
141+ {
142+ var root = syntaxRoot ?? await doc . GetSyntaxRootAsync ( ) ;
143+ var withSyntaxRoot = doc . WithSyntaxRoot ( root ) ;
24144 try {
25145 return await Simplifier . ReduceAsync ( withSyntaxRoot ) ;
26146 } catch {
27147 return doc ;
28148 }
29149 }
30150
31- public static async Task < Document > SimplifyStatements < TUsingDirectiveSyntax , TExpressionSyntax > ( this Document convertedDocument , string unresolvedTypeDiagnosticId )
32- where TUsingDirectiveSyntax : SyntaxNode where TExpressionSyntax : SyntaxNode
151+
152+ private static SyntaxNode TryExpandNode ( SyntaxNode node , SemanticModel semanticModel , Workspace workspace )
33153 {
34- var root = await convertedDocument . GetSyntaxRootAsync ( ) ;
35- var nodesWithUnresolvedTypes = ( await convertedDocument . GetSemanticModelAsync ( ) ) . GetDiagnostics ( )
36- . Where ( d => d . Id == unresolvedTypeDiagnosticId && d . Location . IsInSource )
37- . Select ( d => root . FindNode ( d . Location . SourceSpan ) . GetAncestor < TUsingDirectiveSyntax > ( ) )
38- . ToLookup ( d => ( SyntaxNode ) d ) ;
154+ try {
155+ return Simplifier . Expand ( node , semanticModel , workspace ) ;
156+ } catch ( Exception ) {
157+ return node ;
158+ }
159+ }
39160
40- root = root . ReplaceNodes (
41- root . DescendantNodes ( n => ! ( n is TExpressionSyntax ) && ! nodesWithUnresolvedTypes . Contains ( n ) ) ,
42- ( orig , rewritten ) => ! nodesWithUnresolvedTypes . Contains ( rewritten ) ? rewritten . WithAdditionalAnnotations ( Simplifier . Annotation ) : rewritten ) ;
161+ private static bool ShouldExpandVbNode ( SemanticModel semanticModel , SyntaxNode node )
162+ {
163+ return node is VBSyntax . NameSyntax || node is VBSyntax . InvocationExpressionSyntax && ! semanticModel . GetSymbolInfo ( node ) . Symbol . IsReducedTypeParameterMethod ( ) ;
164+ }
43165
44- var document = await convertedDocument . WithSimplifiedSyntaxRootAsync ( root ) ;
45- return document ;
166+ private static bool ShouldExpandCsNode ( SemanticModel semanticModel , SyntaxNode node )
167+ {
168+ return false ;
46169 }
47170 }
48171}
0 commit comments