@@ -11,6 +11,8 @@ namespace Flow.Launcher.Localization.Analyzers.Localize
11
11
[ DiagnosticAnalyzer ( LanguageNames . CSharp ) ]
12
12
public class OldGetTranslateAnalyzer : DiagnosticAnalyzer
13
13
{
14
+ #region DiagnosticAnalyzer
15
+
14
16
public override ImmutableArray < DiagnosticDescriptor > SupportedDiagnostics => ImmutableArray . Create (
15
17
AnalyzerDiagnostics . OldLocalizationApiUsed
16
18
) ;
@@ -22,29 +24,42 @@ public override void Initialize(AnalysisContext context)
22
24
context . RegisterSyntaxNodeAction ( AnalyzeNode , SyntaxKind . InvocationExpression ) ;
23
25
}
24
26
27
+ #endregion
28
+
29
+ #region Analyze Methods
30
+
25
31
private static void AnalyzeNode ( SyntaxNodeAnalysisContext context )
26
32
{
27
33
var invocationExpr = ( InvocationExpressionSyntax ) context . Node ;
28
34
var semanticModel = context . SemanticModel ;
29
35
var symbolInfo = semanticModel . GetSymbolInfo ( invocationExpr ) ;
30
36
37
+ // Check if the method is a format string call
31
38
if ( ! ( symbolInfo . Symbol is IMethodSymbol methodSymbol ) ) return ;
32
39
33
- if ( IsFormatStringCall ( methodSymbol ) &&
34
- GetFirstArgumentInvocationExpression ( invocationExpr ) is InvocationExpressionSyntax innerInvocationExpr )
40
+ // First branch: detect a call to string.Format containing a translate call anywhere in its arguments.
41
+ if ( IsFormatStringCall ( methodSymbol ) )
35
42
{
36
- if ( ! IsTranslateCall ( semanticModel . GetSymbolInfo ( innerInvocationExpr ) ) ||
37
- ! ( GetFirstArgumentStringValue ( innerInvocationExpr ) is string translationKey ) )
38
- return ;
39
-
40
- var diagnostic = Diagnostic . Create (
41
- AnalyzerDiagnostics . OldLocalizationApiUsed ,
42
- invocationExpr . GetLocation ( ) ,
43
- translationKey ,
44
- GetInvocationArguments ( invocationExpr )
45
- ) ;
46
- context . ReportDiagnostic ( diagnostic ) ;
43
+ var arguments = invocationExpr . ArgumentList . Arguments ;
44
+ // Check all arguments is an invocation (i.e. a candidate for Context.API.GetTranslation(…))
45
+ for ( int i = 0 ; i < arguments . Count ; i ++ )
46
+ {
47
+ if ( GetArgumentInvocationExpression ( invocationExpr , i ) is InvocationExpressionSyntax innerInvocationExpr &&
48
+ IsTranslateCall ( semanticModel . GetSymbolInfo ( innerInvocationExpr ) ) &&
49
+ GetFirstArgumentStringValue ( innerInvocationExpr ) is string translationKey )
50
+ {
51
+ var diagnostic = Diagnostic . Create (
52
+ AnalyzerDiagnostics . OldLocalizationApiUsed ,
53
+ invocationExpr . GetLocation ( ) ,
54
+ translationKey ,
55
+ GetInvocationArguments ( invocationExpr , i )
56
+ ) ;
57
+ context . ReportDiagnostic ( diagnostic ) ;
58
+ return ;
59
+ }
60
+ }
47
61
}
62
+ // Second branch: direct translate call (outside of a Format call)
48
63
else if ( IsTranslateCall ( methodSymbol ) && GetFirstArgumentStringValue ( invocationExpr ) is string translationKey )
49
64
{
50
65
if ( IsParentFormatStringCall ( semanticModel , invocationExpr ) ) return ;
@@ -59,27 +74,42 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
59
74
}
60
75
}
61
76
62
- private static string GetInvocationArguments ( InvocationExpressionSyntax invocationExpr ) =>
63
- string . Join ( ", " , invocationExpr . ArgumentList . Arguments . Skip ( 1 ) ) ;
77
+ #region Utils
64
78
65
- private static bool IsParentFormatStringCall ( SemanticModel semanticModel , SyntaxNode syntaxNode ) =>
66
- syntaxNode is InvocationExpressionSyntax invocationExpressionSyntax &&
67
- invocationExpressionSyntax . Parent ? . Parent ? . Parent is SyntaxNode parent &&
68
- IsFormatStringCall ( semanticModel ? . GetSymbolInfo ( parent ) ) ;
79
+ private static string GetInvocationArguments ( InvocationExpressionSyntax invocationExpr , int translateArgIndex ) =>
80
+ string . Join ( ", " , invocationExpr . ArgumentList . Arguments . Skip ( translateArgIndex + 1 ) ) ;
69
81
70
- private static bool IsFormatStringCall ( SymbolInfo ? symbolInfo ) =>
71
- symbolInfo is SymbolInfo info && IsFormatStringCall ( info . Symbol as IMethodSymbol ) ;
82
+ /// <summary>
83
+ /// Walk up the tree to see if we're already inside a Format call
84
+ /// </summary>
85
+ private static bool IsParentFormatStringCall ( SemanticModel semanticModel , SyntaxNode syntaxNode )
86
+ {
87
+ var parent = syntaxNode . Parent ;
88
+ while ( parent != null )
89
+ {
90
+ if ( parent is InvocationExpressionSyntax parentInvocation )
91
+ {
92
+ var symbol = semanticModel . GetSymbolInfo ( parentInvocation ) . Symbol as IMethodSymbol ;
93
+ if ( IsFormatStringCall ( symbol ) )
94
+ {
95
+ return true ;
96
+ }
97
+ }
98
+ parent = parent . Parent ;
99
+ }
100
+ return false ;
101
+ }
72
102
73
103
private static bool IsFormatStringCall ( IMethodSymbol methodSymbol ) =>
74
- methodSymbol ? . Name is Constants . StringFormatMethodName &&
75
- methodSymbol . ContainingType . ToDisplayString ( ) is Constants . StringFormatTypeName ;
104
+ methodSymbol ? . Name == Constants . StringFormatMethodName &&
105
+ methodSymbol . ContainingType . ToDisplayString ( ) == Constants . StringFormatTypeName ;
76
106
77
- private static InvocationExpressionSyntax GetFirstArgumentInvocationExpression ( InvocationExpressionSyntax invocationExpr ) =>
78
- invocationExpr . ArgumentList . Arguments . FirstOrDefault ( ) ? . Expression as InvocationExpressionSyntax ;
107
+ private static InvocationExpressionSyntax GetArgumentInvocationExpression ( InvocationExpressionSyntax invocationExpr , int index ) =>
108
+ invocationExpr . ArgumentList . Arguments [ index ] . Expression as InvocationExpressionSyntax ;
79
109
80
110
private static bool IsTranslateCall ( SymbolInfo symbolInfo ) =>
81
111
symbolInfo . Symbol is IMethodSymbol innerMethodSymbol &&
82
- innerMethodSymbol . Name is Constants . OldLocalizationMethodName &&
112
+ innerMethodSymbol . Name == Constants . OldLocalizationMethodName &&
83
113
Constants . OldLocalizationClasses . Contains ( innerMethodSymbol . ContainingType . Name ) ;
84
114
85
115
private static bool IsTranslateCall ( IMethodSymbol methodSymbol ) =>
@@ -92,5 +122,9 @@ private static string GetFirstArgumentStringValue(InvocationExpressionSyntax inv
92
122
return syntax . Token . ValueText ;
93
123
return null ;
94
124
}
125
+
126
+ #endregion
127
+
128
+ #endregion
95
129
}
96
130
}
0 commit comments