mirror of
https://github.com/dlang-community/D-Scanner.git
synced 2025-04-26 13:20:07 +03:00
Use DMD in NumberStyleCheck (#88)
* Replace libdparse with DMD in NumberStyleCheck * Fix re-lexing for windows (CRLF terminators) files * Improve unit test
This commit is contained in:
parent
860ddf1994
commit
8b7612d76a
2 changed files with 64 additions and 39 deletions
|
@ -5,60 +5,83 @@
|
|||
|
||||
module dscanner.analysis.numbers;
|
||||
|
||||
import std.stdio;
|
||||
import std.regex;
|
||||
import dparse.ast;
|
||||
import dparse.lexer;
|
||||
import dscanner.analysis.base;
|
||||
import dscanner.analysis.helpers;
|
||||
import dsymbol.scope_ : Scope;
|
||||
import dmd.tokens : TOK;
|
||||
import std.conv;
|
||||
import std.regex;
|
||||
|
||||
/**
|
||||
* Checks for long and hard-to-read number literals
|
||||
*/
|
||||
final class NumberStyleCheck : BaseAnalyzer
|
||||
extern (C++) class NumberStyleCheck(AST) : BaseAnalyzerDmd
|
||||
{
|
||||
public:
|
||||
alias visit = BaseAnalyzer.visit;
|
||||
|
||||
alias visit = BaseAnalyzerDmd.visit;
|
||||
mixin AnalyzerInfo!"number_style_check";
|
||||
|
||||
/**
|
||||
* Constructs the style checker with the given file name.
|
||||
*/
|
||||
this(BaseAnalyzerArguments args)
|
||||
private enum KEY = "dscanner.style.number_literals";
|
||||
private enum string MSG = "Use underscores to improve number constant readability.";
|
||||
|
||||
private auto badBinaryRegex = ctRegex!(`^0b[01]{9,}`);
|
||||
private auto badDecimalRegex = ctRegex!(`^\d{5,}`);
|
||||
|
||||
extern (D) this(string fileName, bool skipTests = false)
|
||||
{
|
||||
super(args);
|
||||
super(fileName, skipTests);
|
||||
}
|
||||
|
||||
override void visit(const Token t)
|
||||
override void visit(AST.IntegerExp intExpr)
|
||||
{
|
||||
import std.algorithm : startsWith;
|
||||
import dscanner.utils : readFile;
|
||||
import dmd.errorsink : ErrorSinkNull;
|
||||
import dmd.globals : global;
|
||||
import dmd.lexer : Lexer;
|
||||
|
||||
if (isNumberLiteral(t.type) && !t.text.startsWith("0x")
|
||||
&& ((t.text.startsWith("0b") && !t.text.matchFirst(badBinaryRegex)
|
||||
.empty) || !t.text.matchFirst(badDecimalRegex).empty))
|
||||
auto bytes = readFile(fileName) ~ '\0';
|
||||
bytes = bytes[intExpr.loc.fileOffset .. $];
|
||||
|
||||
__gshared ErrorSinkNull errorSinkNull;
|
||||
if (!errorSinkNull)
|
||||
errorSinkNull = new ErrorSinkNull;
|
||||
|
||||
scope lexer = new Lexer(null, cast(char*) bytes, 0, bytes.length, 0, 0, errorSinkNull, &global.compileEnv);
|
||||
auto tokenValue = lexer.nextToken();
|
||||
bool isInt = false;
|
||||
|
||||
while (tokenValue != TOK.semicolon && tokenValue != TOK.endOfFile)
|
||||
{
|
||||
addErrorMessage(t, KEY,
|
||||
"Use underscores to improve number constant readability.");
|
||||
if (isIntegerLiteral(tokenValue))
|
||||
{
|
||||
isInt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
tokenValue = lexer.nextToken();
|
||||
}
|
||||
|
||||
if (!isInt)
|
||||
return;
|
||||
|
||||
auto tokenText = to!string(lexer.token.ptr);
|
||||
|
||||
if (!matchFirst(tokenText, badDecimalRegex).empty || !matchFirst(tokenText, badBinaryRegex).empty)
|
||||
addErrorMessage(intExpr.loc.linnum, intExpr.loc.charnum, KEY, MSG);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum string KEY = "dscanner.style.number_literals";
|
||||
|
||||
auto badBinaryRegex = ctRegex!(`^0b[01]{9,}`);
|
||||
auto badDecimalRegex = ctRegex!(`^\d{5,}`);
|
||||
private bool isIntegerLiteral(TOK token)
|
||||
{
|
||||
return token >= TOK.int32Literal && token <= TOK.uns128Literal;
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
|
||||
import dscanner.analysis.helpers : assertAnalyzerWarningsDMD;
|
||||
import std.stdio : stderr;
|
||||
|
||||
StaticAnalysisConfig sac = disabledConfig();
|
||||
sac.number_style_check = Check.enabled;
|
||||
assertAnalyzerWarnings(q{
|
||||
assertAnalyzerWarningsDMD(q{
|
||||
void testNumbers()
|
||||
{
|
||||
int a;
|
||||
|
@ -66,12 +89,12 @@ unittest
|
|||
a = 10; // ok
|
||||
a = 100; // ok
|
||||
a = 1000; // ok
|
||||
a = 10000; /+
|
||||
^^^^^ [warn]: Use underscores to improve number constant readability. +/
|
||||
a = 100000; /+
|
||||
^^^^^^ [warn]: Use underscores to improve number constant readability. +/
|
||||
a = 1000000; /+
|
||||
^^^^^^^ [warn]: Use underscores to improve number constant readability. +/
|
||||
a = 10_00; // ok
|
||||
a = 10_000; // ok
|
||||
a = 100_000; // ok
|
||||
a = 10000; // [warn]: Use underscores to improve number constant readability.
|
||||
a = 100000; // [warn]: Use underscores to improve number constant readability.
|
||||
a = 1000000; // [warn]: Use underscores to improve number constant readability.
|
||||
}
|
||||
}c, sac);
|
||||
|
||||
|
|
|
@ -855,10 +855,6 @@ private BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName,
|
|||
checks ~= new MismatchedArgumentCheck(args.setSkipTests(
|
||||
analysisConfig.mismatched_args_check == Check.skipTests && !ut));
|
||||
|
||||
if (moduleName.shouldRun!NumberStyleCheck(analysisConfig))
|
||||
checks ~= new NumberStyleCheck(args.setSkipTests(
|
||||
analysisConfig.number_style_check == Check.skipTests && !ut));
|
||||
|
||||
if (moduleName.shouldRun!StyleChecker(analysisConfig))
|
||||
checks ~= new StyleChecker(args.setSkipTests(
|
||||
analysisConfig.style_check == Check.skipTests && !ut));
|
||||
|
@ -1345,6 +1341,12 @@ MessageSet analyzeDmd(string fileName, ASTCodegen.Module m, const char[] moduleN
|
|||
config.redundant_storage_classes == Check.skipTests && !ut
|
||||
);
|
||||
|
||||
if (moduleName.shouldRunDmd!(NumberStyleCheck!ASTCodegen)(config))
|
||||
visitors ~= new NumberStyleCheck!ASTCodegen(
|
||||
fileName,
|
||||
config.number_style_check == Check.skipTests && !ut
|
||||
);
|
||||
|
||||
foreach (visitor; visitors)
|
||||
{
|
||||
m.accept(visitor);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue