1

I'd like to enforce some hard limits to code line length.

What Roslyn's API should I use for it?

Registering action for every syntax node and checking node's location seems to be not the most efficient approach.

Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137
  • I suggest you to enforce that restriction while the build happens. So that, the developer will fix it to make the build success. https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview#enable-on-build – Rakesh Androtula Mar 26 '22 at 06:55
  • Humm obfuscation by horizontal scroll! Have you been successful? And may you share your solution in answers. – Drag and Drop Sep 15 '22 at 14:23
  • 1
    @DragandDrop yes, though it's hardcoded, and not selective to language constructs. I'll add code. – Pavel Voronin Sep 15 '22 at 16:55

2 Answers2

1

An easy approach might be to register for syntax trees, but then immediately just call GetText() on the syntax tree; from there you can look at the lines of the text directly and at least find "long" lines since you can directly get line spans and lengths that way. You'll still potentially need to filter out things though like long string literals or something.

Jason Malinowski
  • 18,148
  • 1
  • 38
  • 55
1

Not perfect solution:

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class LineMaxLengthAnalyzer : DiagnosticAnalyzer
{
    public const int LineMaxLength = 150;
    public const string DiagnosticId = nameof(LineMaxLengthAnalyzer);

    private static readonly LocalizableString Title = new LocalizableResourceString(
        nameof(Resources.LineMaxLengthAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
    private static readonly LocalizableString MessageFormat = new LocalizableResourceString(
        nameof(Resources.LineMaxLengthAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
    private static readonly LocalizableString Description = new LocalizableResourceString(
        nameof(Resources.LineMaxLengthAnalyzerDescription), Resources.ResourceManager, typeof(Resources));
    private const string Category = "Readability";

    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
        DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);

    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();

        var nodeTypes = Enum.GetValues(typeof(SyntaxKind)).Cast<SyntaxKind>().ToArray();
        context.RegisterSyntaxTreeAction(VerifyLines);
    }

    private void VerifyLines(SyntaxTreeAnalysisContext context)
    {
        var text = context.Tree.GetText();
        text.Lines.Where(line => line.End - line.Start + 1 > LineMaxLength).ToList()
            .ForEach(line =>
            {
                var location = Location.Create(context.Tree, line.Span);
                var diagnostic = Diagnostic.Create(Rule, location, LineMaxLength);
                context.ReportDiagnostic(diagnostic);
            });
    }
}
Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137