From 8b7612d76a9d7788e725bf83619f9e270d593f1f Mon Sep 17 00:00:00 2001 From: Vladiwostok <55026261+Vladiwostok@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:53:12 +0200 Subject: [PATCH] Use DMD in NumberStyleCheck (#88) * Replace libdparse with DMD in NumberStyleCheck * Fix re-lexing for windows (CRLF terminators) files * Improve unit test --- src/dscanner/analysis/numbers.d | 93 ++++++++++++++++++++------------- src/dscanner/analysis/run.d | 10 ++-- 2 files changed, 64 insertions(+), 39 deletions(-) diff --git a/src/dscanner/analysis/numbers.d b/src/dscanner/analysis/numbers.d index a962996..7599e7e 100644 --- a/src/dscanner/analysis/numbers.d +++ b/src/dscanner/analysis/numbers.d @@ -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); diff --git a/src/dscanner/analysis/run.d b/src/dscanner/analysis/run.d index e3fc866..c422367 100644 --- a/src/dscanner/analysis/run.d +++ b/src/dscanner/analysis/run.d @@ -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);