Roslyn 消息格式不适用于分析
Roslyn Message Format is not working with analysis
我创建了一个类似于默认项目消息的消息格式:
Variable named '{0}' can be made into a constant
我已经创建了我的规则:
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Warning,
isEnabledByDefault: true, description: Description);
已注册:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeLocalizationNode, SyntaxKind.LocalDeclarationStatement);
}
完整分析器:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class LocalizationToolAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "LocalizationTool";
// You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources));
private const string Category = "Naming";
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.
context.RegisterSyntaxNodeAction(AnalyzeLocalizationNode, SyntaxKind.LocalDeclarationStatement);
}
private static void AnalyzeLocalizationNode(SyntaxNodeAnalysisContext context)
{
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
// Only consider local variable declarations that aren't already const.
if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
{
return;
}
var variableTypeName = localDeclaration.Declaration.Type;
var variableType = context.SemanticModel.GetTypeInfo(variableTypeName).ConvertedType;
// Ensure that all variables in the local declaration have initializers that
// are assigned with constant values. i.e int x, y = 0, (is not valid)
foreach (var variable in localDeclaration.Declaration.Variables)
{
if (false == LocalizationToolAnalyzer.CanMakeGlobalConst(context, variable, variableType))
{
return;
}
}
// Perform data flow analysis on the local declaration.
var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
foreach (var variable in localDeclaration.Declaration.Variables)
{
var variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
}
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
}
private static bool CanMakeGlobalConst(SyntaxNodeAnalysisContext context, VariableDeclaratorSyntax variable,
ITypeSymbol variableType)
{
var initializer = variable.Initializer;
if (initializer == null)
{
return false;
}
var constantValue = context.SemanticModel.GetConstantValue(initializer.Value);
if (!constantValue.HasValue)
{
return false;
}
// Ensure that the initializer value can be converted to the type of the
// local declaration without a user-defined conversion.
var conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
if (!conversion.Exists || conversion.IsUserDefined)
{
return false;
}
// Special cases:
// * If the constant value is a string, the type of the local declaration
// must be System.String.
// * If the constant value is null, the type of the local declaration must
// be a reference type.
if (constantValue.Value is string)
{
if (variableType.SpecialType != SpecialType.System_String)
{
return false;
}
}
else if (variableType.IsReferenceType && constantValue.Value != null)
{
return false;
}
return true;
}
}
但是我的显示消息从未替换变量我该如何解决这个问题?
您没有传递任何东西来替代它。
在其 simplest overload 中,Diagnostic.Create
采用 Location
,后跟消息格式参数。
您需要显式传递变量名(或您要替换的任何其他名称)。
作为快速技巧,您可以将声明转换为符号并获取名称:
var firstVariable = localDeclaration.Declaration.Variables.FirstOrDefault();
var firstSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable);
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), firstSymbol.Name));
我创建了一个类似于默认项目消息的消息格式:
Variable named '{0}' can be made into a constant
我已经创建了我的规则:
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Warning,
isEnabledByDefault: true, description: Description);
已注册:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeLocalizationNode, SyntaxKind.LocalDeclarationStatement);
}
完整分析器:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class LocalizationToolAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "LocalizationTool";
// You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources));
private const string Category = "Naming";
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.
context.RegisterSyntaxNodeAction(AnalyzeLocalizationNode, SyntaxKind.LocalDeclarationStatement);
}
private static void AnalyzeLocalizationNode(SyntaxNodeAnalysisContext context)
{
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
// Only consider local variable declarations that aren't already const.
if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
{
return;
}
var variableTypeName = localDeclaration.Declaration.Type;
var variableType = context.SemanticModel.GetTypeInfo(variableTypeName).ConvertedType;
// Ensure that all variables in the local declaration have initializers that
// are assigned with constant values. i.e int x, y = 0, (is not valid)
foreach (var variable in localDeclaration.Declaration.Variables)
{
if (false == LocalizationToolAnalyzer.CanMakeGlobalConst(context, variable, variableType))
{
return;
}
}
// Perform data flow analysis on the local declaration.
var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
foreach (var variable in localDeclaration.Declaration.Variables)
{
var variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
}
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
}
private static bool CanMakeGlobalConst(SyntaxNodeAnalysisContext context, VariableDeclaratorSyntax variable,
ITypeSymbol variableType)
{
var initializer = variable.Initializer;
if (initializer == null)
{
return false;
}
var constantValue = context.SemanticModel.GetConstantValue(initializer.Value);
if (!constantValue.HasValue)
{
return false;
}
// Ensure that the initializer value can be converted to the type of the
// local declaration without a user-defined conversion.
var conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
if (!conversion.Exists || conversion.IsUserDefined)
{
return false;
}
// Special cases:
// * If the constant value is a string, the type of the local declaration
// must be System.String.
// * If the constant value is null, the type of the local declaration must
// be a reference type.
if (constantValue.Value is string)
{
if (variableType.SpecialType != SpecialType.System_String)
{
return false;
}
}
else if (variableType.IsReferenceType && constantValue.Value != null)
{
return false;
}
return true;
}
}
但是我的显示消息从未替换变量我该如何解决这个问题?
您没有传递任何东西来替代它。
在其 simplest overload 中,Diagnostic.Create
采用 Location
,后跟消息格式参数。
您需要显式传递变量名(或您要替换的任何其他名称)。
作为快速技巧,您可以将声明转换为符号并获取名称:
var firstVariable = localDeclaration.Declaration.Variables.FirstOrDefault();
var firstSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable);
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), firstSymbol.Name));