为什么这个 Roslyn 分析器测试失败?
Why is this Roslyn analyzer test failing?
我试图理解为什么这个测试失败了:
Maak.Test.InitializeNewUnitTest.NoPropertiesInitialized_Diagnostic
Assert.AreEqual failed. Expected:<37>. Actual:<38>. Context:
Diagnostics of test state Expected diagnostic to end at column "38"
was actually at column "39"
Expected diagnostic:
// /0/Test0.cs(13,9,13,38): warning MaakInitializeNew VerifyCS.Diagnostic().WithSpan(13, 9, 13, 38),
Actual diagnostic:
// /0/Test0.cs(13,9): warning MaakInitializeNew: can be fully initialized VerifyCS.Diagnostic().WithSpan(13, 9, 13, 39),
测试:
[TestMethod]
public async Task NoPropertiesInitialized_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
public class MyClass
{
public string Name { get; set; }
}
class Program
{
static void Main()
{
[|var myClass = new MyClass { }|];
}
}
", @"
using System;
public class MyClass
{
public string Name { get; set; }
}
class Program
{
static void Main()
{
var myClass = new MyClass { Name = ""Stan"" };
}
}
");
}
分析器代码:
public override void Initialize(AnalysisContext context)
{
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.LocalDeclarationStatement);
}
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
var typeDeclaration = localDeclaration.Declaration.Type;
var symbolInfo = context.SemanticModel.GetSymbolInfo(typeDeclaration);
var typeSymbol = symbolInfo.Symbol;
if(typeSymbol == null)
return;
// Special case: Ensure that 'var' isn't actually an alias to another type. (e.g. using var = System.String).
var aliasInfo = context.SemanticModel.GetAliasInfo(typeDeclaration);
if (aliasInfo != null)
return;
var namedSymbol = context.Compilation.GetTypeByMetadataName(typeSymbol.MetadataName);
if (namedSymbol?.TypeKind != TypeKind.Class)
return;
var hasDefaultConstructor = (namedSymbol?.Constructors)?.SingleOrDefault(c => !c.Parameters.Any()) != null;
var properties = namedSymbol?.GetMembers()
.Where(m => m.Kind == SymbolKind.Property
&& m.DeclaredAccessibility == Accessibility.Public
&& !((IPropertySymbol)m).IsReadOnly
&& !((IPropertySymbol)m).IsStatic)
.Select(m => new
{
Name = m.Name,
Type = ((IPropertySymbol)m).Type
})
.ToList();
var hasValidProperties = properties?.Any() != false;
if (!hasValidProperties)
return;
var initializerExpressions = (localDeclaration.Declaration.Variables.FirstOrDefault().Initializer.Value
as ObjectCreationExpressionSyntax)?.Initializer.Expressions.ToList();
var except = properties.Select(p => p.Name)
.Except(initializerExpressions.Select(e => (e as AssignmentExpressionSyntax)?.Left.ToString()))
.ToList();
if(except.Count == 0)
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
}
报告正确的位置修复了它:
context.ReportDiagnostic(Diagnostic.Create(Rule, localDeclaration.Declaration.GetLocation()));
我试图理解为什么这个测试失败了:
Maak.Test.InitializeNewUnitTest.NoPropertiesInitialized_Diagnostic
Assert.AreEqual failed. Expected:<37>. Actual:<38>. Context: Diagnostics of test state Expected diagnostic to end at column "38" was actually at column "39"
Expected diagnostic: // /0/Test0.cs(13,9,13,38): warning MaakInitializeNew VerifyCS.Diagnostic().WithSpan(13, 9, 13, 38),
Actual diagnostic: // /0/Test0.cs(13,9): warning MaakInitializeNew: can be fully initialized VerifyCS.Diagnostic().WithSpan(13, 9, 13, 39),
测试:
[TestMethod]
public async Task NoPropertiesInitialized_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
public class MyClass
{
public string Name { get; set; }
}
class Program
{
static void Main()
{
[|var myClass = new MyClass { }|];
}
}
", @"
using System;
public class MyClass
{
public string Name { get; set; }
}
class Program
{
static void Main()
{
var myClass = new MyClass { Name = ""Stan"" };
}
}
");
}
分析器代码:
public override void Initialize(AnalysisContext context)
{
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.LocalDeclarationStatement);
}
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
var typeDeclaration = localDeclaration.Declaration.Type;
var symbolInfo = context.SemanticModel.GetSymbolInfo(typeDeclaration);
var typeSymbol = symbolInfo.Symbol;
if(typeSymbol == null)
return;
// Special case: Ensure that 'var' isn't actually an alias to another type. (e.g. using var = System.String).
var aliasInfo = context.SemanticModel.GetAliasInfo(typeDeclaration);
if (aliasInfo != null)
return;
var namedSymbol = context.Compilation.GetTypeByMetadataName(typeSymbol.MetadataName);
if (namedSymbol?.TypeKind != TypeKind.Class)
return;
var hasDefaultConstructor = (namedSymbol?.Constructors)?.SingleOrDefault(c => !c.Parameters.Any()) != null;
var properties = namedSymbol?.GetMembers()
.Where(m => m.Kind == SymbolKind.Property
&& m.DeclaredAccessibility == Accessibility.Public
&& !((IPropertySymbol)m).IsReadOnly
&& !((IPropertySymbol)m).IsStatic)
.Select(m => new
{
Name = m.Name,
Type = ((IPropertySymbol)m).Type
})
.ToList();
var hasValidProperties = properties?.Any() != false;
if (!hasValidProperties)
return;
var initializerExpressions = (localDeclaration.Declaration.Variables.FirstOrDefault().Initializer.Value
as ObjectCreationExpressionSyntax)?.Initializer.Expressions.ToList();
var except = properties.Select(p => p.Name)
.Except(initializerExpressions.Select(e => (e as AssignmentExpressionSyntax)?.Left.ToString()))
.ToList();
if(except.Count == 0)
return;
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
}
报告正确的位置修复了它:
context.ReportDiagnostic(Diagnostic.Create(Rule, localDeclaration.Declaration.GetLocation()));