Skip to content

Commit f630fc7

Browse files
committed
Support string.Format culture info parameter analyzer
1 parent 6171e92 commit f630fc7

File tree

1 file changed

+60
-26
lines changed

1 file changed

+60
-26
lines changed

Flow.Launcher.Localization.Analyzers/Localize/OldGetTranslateAnalyzer.cs

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace Flow.Launcher.Localization.Analyzers.Localize
1111
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1212
public class OldGetTranslateAnalyzer : DiagnosticAnalyzer
1313
{
14+
#region DiagnosticAnalyzer
15+
1416
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
1517
AnalyzerDiagnostics.OldLocalizationApiUsed
1618
);
@@ -22,29 +24,42 @@ public override void Initialize(AnalysisContext context)
2224
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression);
2325
}
2426

27+
#endregion
28+
29+
#region Analyze Methods
30+
2531
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
2632
{
2733
var invocationExpr = (InvocationExpressionSyntax)context.Node;
2834
var semanticModel = context.SemanticModel;
2935
var symbolInfo = semanticModel.GetSymbolInfo(invocationExpr);
3036

37+
// Check if the method is a format string call
3138
if (!(symbolInfo.Symbol is IMethodSymbol methodSymbol)) return;
3239

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))
3542
{
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+
}
4761
}
62+
// Second branch: direct translate call (outside of a Format call)
4863
else if (IsTranslateCall(methodSymbol) && GetFirstArgumentStringValue(invocationExpr) is string translationKey)
4964
{
5065
if (IsParentFormatStringCall(semanticModel, invocationExpr)) return;
@@ -59,27 +74,42 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
5974
}
6075
}
6176

62-
private static string GetInvocationArguments(InvocationExpressionSyntax invocationExpr) =>
63-
string.Join(", ", invocationExpr.ArgumentList.Arguments.Skip(1));
77+
#region Utils
6478

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));
6981

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+
}
72102

73103
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;
76106

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;
79109

80110
private static bool IsTranslateCall(SymbolInfo symbolInfo) =>
81111
symbolInfo.Symbol is IMethodSymbol innerMethodSymbol &&
82-
innerMethodSymbol.Name is Constants.OldLocalizationMethodName &&
112+
innerMethodSymbol.Name == Constants.OldLocalizationMethodName &&
83113
Constants.OldLocalizationClasses.Contains(innerMethodSymbol.ContainingType.Name);
84114

85115
private static bool IsTranslateCall(IMethodSymbol methodSymbol) =>
@@ -92,5 +122,9 @@ private static string GetFirstArgumentStringValue(InvocationExpressionSyntax inv
92122
return syntax.Token.ValueText;
93123
return null;
94124
}
125+
126+
#endregion
127+
128+
#endregion
95129
}
96130
}

0 commit comments

Comments
 (0)