Skip to content
This repository has been archived by the owner on Mar 1, 2022. It is now read-only.

Commit

Permalink
Add dub.resolveDiagnosticRange
Browse files Browse the repository at this point in the history
Implements first resolver: ==/!= with null comparer
  • Loading branch information
WebFreak001 committed May 1, 2020
1 parent 2fd8f06 commit 462fc68
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
20 changes: 20 additions & 0 deletions source/workspaced/com/dub.d
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,26 @@ class DubComponent : ComponentWrapper
return dst.data;
}

/// Tries to find a suitable code byte range where a given dub build issue
/// applies to.
/// Returns: `[pos, pos]` if not found, otherwise range in bytes which might
/// not contain the position at all.
int[2] resolveDiagnosticRange(scope const(char)[] code, int position,
scope const(char)[] diagnostic)
{
import dparse.lexer : getTokensForParser, LexerConfig;
import dparse.parser : parseModule;
import dparse.rollback_allocator : RollbackAllocator;
import workspaced.dub.diagnostics : resolveDubDiagnosticRange;

LexerConfig config;
RollbackAllocator rba;
auto tokens = getTokensForParser(cast(ubyte[]) code, config, &workspaced.stringCache);
auto parsed = parseModule(tokens, "equal_finder.d", &rba);

return resolveDubDiagnosticRange(code, tokens, parsed, position, diagnostic);
}

private:
Dub _dub;
bool _dubRunning = false;
Expand Down
87 changes: 87 additions & 0 deletions source/workspaced/dub/diagnostics.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
module workspaced.dub.diagnostics;

import workspaced.api;

import std.algorithm;
import std.string;

import dparse.ast;
import dparse.lexer;
import dparse.parser;
import dparse.rollback_allocator;

int[2] resolveDubDiagnosticRange(scope const(char)[] code,
scope const(Token)[] tokens, Module parsed, int position,
scope const(char)[] diagnostic)
{
if (diagnostic.startsWith("use `is` instead of `==`",
"use `!is` instead of `!=`"))
{
auto expr = new EqualComparisionFinder(position);
expr.visit(parsed);
if (expr.result !is null)
{
const left = &expr.result.left.tokens[$ - 1];
const right = &expr.result.right.tokens[0];
auto between = left[1 .. right - left];
const tok = between[0];
if (tok.type == expr.result.operator)
{
auto index = cast(int) tok.index;
return [index, index + 2];
}
}
}
return [position, position];
}

/// Finds the equals comparision at the given index.
/// Used to resolve issue locations for diagnostics of type
/// - use `is` instead of `==`
/// - use `!is` instead of `!=`
class EqualComparisionFinder : ASTVisitor
{
this(size_t index)
{
this.index = index;
}

override void visit(const(CmpExpression) expr)
{
if (expr.equalExpression !is null)
{
const start = expr.tokens[0].index;
const last = expr.tokens[$ - 1];
const end = last.index + last.text.length;
if (index >= start && index < end)
{
result = cast(EqualExpression) expr.equalExpression;
}
}
super.visit(expr);
}

alias visit = ASTVisitor.visit;
size_t index;
EqualExpression result;
}

unittest
{
string code = q{void main() {
if (foo(a == 4) == null)
{
}
}};

LexerConfig config;
RollbackAllocator rba;
StringCache cache = StringCache(64);
auto tokens = getTokensForParser(cast(ubyte[]) code, config, &cache);
auto parsed = parseModule(tokens, "equal_finder.d", &rba);

auto range = resolveDubDiagnosticRange(code, tokens, parsed, 19,
"use `is` instead of `==` when comparing with `null`");

shouldEqual(range, [31, 33]);
}

0 comments on commit 462fc68

Please sign in to comment.