diff --git a/libdparse b/libdparse index 4bf4e5a..c5b458b 160000 --- a/libdparse +++ b/libdparse @@ -1 +1 @@ -Subproject commit 4bf4e5aa09453570121134a819a4f436275fe3f7 +Subproject commit c5b458b518ec26c6a59b3d16ffb39d10fe121d27 diff --git a/src/analysis/config.d b/src/analysis/config.d index cfc80fe..5872e66 100644 --- a/src/analysis/config.d +++ b/src/analysis/config.d @@ -106,5 +106,8 @@ struct StaticAnalysisConfig bool auto_ref_assignment_check; @INI("Checks for incorrect infinite range definitions") - bool incorrect_infinite_range; + bool incorrect_infinite_range_check; + + @INI("Checks for asserts that are always true") + bool useless_assert_check; } diff --git a/src/analysis/run.d b/src/analysis/run.d index 3f9230e..a67054c 100644 --- a/src/analysis/run.d +++ b/src/analysis/run.d @@ -54,6 +54,7 @@ import analysis.label_var_same_name_check; import analysis.line_length; import analysis.auto_ref_assignment; import analysis.incorrect_infinite_range; +import analysis.useless_assert; import dsymbol.string_interning : internString; import dsymbol.scope_; @@ -264,7 +265,10 @@ MessageSet analyze(string fileName, const Module m, checks ~= new LineLengthCheck(fileName, tokens); if (analysisConfig.auto_ref_assignment_check) checks ~= new AutoRefAssignmentCheck(fileName); - checks ~= new IncorrectInfiniteRangeCheck(fileName); + if (analysisConfig.incorrect_infinite_range_check) + checks ~= new IncorrectInfiniteRangeCheck(fileName); + if (analysisConfig.useless_assert_check) + checks ~= new UselessAssertCheck(fileName); version (none) if (analysisConfig.redundant_if_check) checks ~= new IfStatementCheck(fileName, moduleScope); diff --git a/src/analysis/useless_assert.d b/src/analysis/useless_assert.d new file mode 100644 index 0000000..18d4296 --- /dev/null +++ b/src/analysis/useless_assert.d @@ -0,0 +1,96 @@ +// Copyright Brian Schott (Hackerpilot) 2015. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +module analysis.useless_assert; + +import analysis.base; +import analysis.helpers; +import dparse.ast; +import dparse.lexer; + +import std.stdio; + +/** + * Checks for asserts that always succeed + */ +class UselessAssertCheck : BaseAnalyzer +{ + alias visit = BaseAnalyzer.visit; + + /// + this(string fileName) + { + super(fileName, null); + } + + override void visit(const AssertExpression ae) + { + import std.conv : to; + + UnaryExpression unary = cast(UnaryExpression) ae.assertion; + if (unary is null) + { + stderr.writeln("unary is null"); + return; + } + + if (unary.primaryExpression is null) + return; + immutable token = unary.primaryExpression.primary; + immutable skipSwitch = unary.primaryExpression.arrayLiteral !is null + || unary.primaryExpression.assocArrayLiteral !is null + || unary.primaryExpression.functionLiteralExpression !is null; + if (!skipSwitch) switch (token.type) + { + case tok!"doubleLiteral": + if (!token.text.to!double) + return; + break; + case tok!"floatLiteral": + if (!token.text.to!float) + return; + break; + case tok!"idoubleLiteral": + case tok!"ifloatLiteral": + case tok!"irealLiteral": + return; // `to` doesn't support imaginary numbers + case tok!"intLiteral": + if (!token.text.to!int) + return; + break; + case tok!"longLiteral": + if (!token.text.to!long) + return; + break; + case tok!"realLiteral": + if (!token.text.to!real) + return; + break; + case tok!"uintLiteral": + if (!token.text.to!uint) + return; + break; + case tok!"ulongLiteral": + if (!token.text.to!ulong) + return; + break; + case tok!"characterLiteral": + if (token.text == `'\0'`) + return; + break; + case tok!"dstringLiteral": + case tok!"stringLiteral": + case tok!"wstringLiteral": + case tok!"true": + break; + default: + return; + } + addErrorMessage(ae.line, ae.column, KEY, "Assert condition is always true"); + } + +private: + enum string KEY = "dscanner.suspicious.useless_assert"; +}