Skip to content

诊断位置和范围

本文档详细介绍如何精确指定诊断的位置和范围。

文档信息

  • 难度级别: 中级
  • 预计阅读时间: 15 分钟

🎯 学习目标

  • ✅ 理解 Location 类的使用
  • 学会使用 TextSpan 指定范围
  • 掌握精确位置指定技巧

Location 类

Location 类表示源代码中的一个位置。

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

public class LocationExamples
{
    public Location GetLocationFromNode(SyntaxNode node)
    {
        return node.GetLocation();
    }
    
    public Location GetLocationFromToken(SyntaxToken token)
    {
        return token.GetLocation();
    }
    
    public Location GetLocationFromSymbol(ISymbol symbol)
    {
        return symbol.Locations.FirstOrDefault();
    }
    
    public Location CreateCustomLocation(
        SyntaxTree syntaxTree,
        int start,
        int length)
    {
        var span = new TextSpan(start, length);
        return Location.Create(syntaxTree, span);
    }
}

TextSpan 使用

TextSpan 表示文本中的一个范围。

csharp
using Microsoft.CodeAnalysis.Text;

public class TextSpanExamples
{
    public TextSpan CreateTextSpan(int start, int length)
    {
        return new TextSpan(start, length);
    }
    
    public TextSpan CreateTextSpanFromPositions(int start, int end)
    {
        return TextSpan.FromBounds(start, end);
    }
    
    public bool AreOverlapping(TextSpan span1, TextSpan span2)
    {
        return span1.OverlapsWith(span2);
    }
}

精确位置指定

csharp
public class PreciseLocationExamples
{
    // 只标记方法名
    public void ReportOnMethodName(
        SyntaxNodeAnalysisContext context,
        MethodDeclarationSyntax method)
    {
        var diagnostic = Diagnostic.Create(
            Rule,
            method.Identifier.GetLocation());
        
        context.ReportDiagnostic(diagnostic);
    }
    
    // 标记方法签名(名称 + 参数列表)
    public void ReportOnMethodSignature(
        SyntaxNodeAnalysisContext context,
        MethodDeclarationSyntax method)
    {
        var start = method.Identifier.SpanStart;
        var end = method.ParameterList.Span.End;
        var span = TextSpan.FromBounds(start, end);
        
        var location = Location.Create(method.SyntaxTree, span);
        
        var diagnostic = Diagnostic.Create(Rule, location);
        context.ReportDiagnostic(diagnostic);
    }
}

相关文档

跨行诊断

处理跨越多行的诊断:

csharp
public class MultiLineDiagnosticExamples
{
    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
        id: "MULTI001",
        title: "跨行问题",
        messageFormat: "此代码块有问题",
        category: "Demo",
        defaultSeverity: DiagnosticSeverity.Warning,
        isEnabledByDefault: true);
    
    public void ReportOnCodeBlock(
        SyntaxNodeAnalysisContext context,
        BlockSyntax block)
    {
        var diagnostic = Diagnostic.Create(
            Rule,
            block.GetLocation());
        
        context.ReportDiagnostic(diagnostic);
    }
    
    public void ReportOnBlockContent(
        SyntaxNodeAnalysisContext context,
        BlockSyntax block)
    {
        if (block.Statements.Count == 0)
            return;
        
        var start = block.Statements.First().SpanStart;
        var end = block.Statements.Last().Span.End;
        var span = TextSpan.FromBounds(start, end);
        
        var location = Location.Create(block.SyntaxTree, span);
        
        var diagnostic = Diagnostic.Create(Rule, location);
        context.ReportDiagnostic(diagnostic);
    }
    
    public void ReportOnDisjointLocations(
        SyntaxNodeAnalysisContext context,
        params SyntaxNode[] nodes)
    {
        if (nodes.Length == 0)
            return;
        
        var mainLocation = nodes[0].GetLocation();
        var additionalLocations = nodes.Skip(1)
            .Select(n => n.GetLocation())
            .ToImmutableArray();
        
        var diagnostic = Diagnostic.Create(
            Rule,
            mainLocation,
            additionalLocations,
            null);
        
        context.ReportDiagnostic(diagnostic);
    }
}

最佳实践

  1. 使用精确的位置 - 只标记有问题的部分
  2. 验证位置有效性 - 避免在元数据位置报告
  3. 使用 TextSpan 创建自定义范围 - 精确控制诊断范围
  4. 使用附加位置 - 显示相关代码
  5. 处理 partial 类 - 遍历所有位置

常见问题

Q: 如何只标记标识符而不是整个节点?

A: 使用 Token 的位置:

csharp
classDeclaration.Identifier.GetLocation()

Q: 如何标记多个不连续的位置?

A: 使用附加位置:

csharp
var additionalLocations = ImmutableArray.Create(
    location1, location2, location3);

var diagnostic = Diagnostic.Create(
    Rule,
    mainLocation,
    additionalLocations,
    null);

Q: 如何检查位置是否在元数据中?

A: 使用 IsInMetadata 属性:

csharp
if (!location.IsInMetadata)
{
    // 报告诊断
}

基于 MIT 许可发布