diff --git a/src/analysis/config.d b/src/analysis/config.d index 01fd1ae..cfc80fe 100644 --- a/src/analysis/config.d +++ b/src/analysis/config.d @@ -104,4 +104,7 @@ struct StaticAnalysisConfig @INI("Checks for assignment to auto-ref function parameters") bool auto_ref_assignment_check; + + @INI("Checks for incorrect infinite range definitions") + bool incorrect_infinite_range; } diff --git a/src/analysis/incorrect_infinite_range.d b/src/analysis/incorrect_infinite_range.d new file mode 100644 index 0000000..8bf08c8 --- /dev/null +++ b/src/analysis/incorrect_infinite_range.d @@ -0,0 +1,120 @@ +// 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.incorrect_infinite_range; + +import analysis.base; +import analysis.helpers; +import dparse.ast; +import dparse.lexer; + +/** + * Checks for incorrect infinite range definitions + */ +class IncorrectInfiniteRangeCheck : BaseAnalyzer +{ + alias visit = BaseAnalyzer.visit; + + /// + this(string fileName) + { + super(fileName, null); + } + + override void visit(const StructBody structBody) + { + inStruct++; + structBody.accept(this); + inStruct--; + } + + override void visit(const FunctionDeclaration fd) + { + if (inStruct > 0 && fd.name.text == "empty") + { + line = fd.name.line; + column = fd.name.column; + fd.accept(this); + } + } + + override void visit(const FunctionBody fb) + { + if (fb.bodyStatement !is null) + visit(fb.bodyStatement.blockStatement); + else + visit(fb.blockStatement); + } + + override void visit(const BlockStatement bs) + { + if (bs.declarationsAndStatements is null) + return; + if (bs.declarationsAndStatements.declarationsAndStatements is null) + return; + if (bs.declarationsAndStatements.declarationsAndStatements.length != 1) + return; + visit(bs.declarationsAndStatements); + } + + override void visit(const ReturnStatement rs) + { + if (rs.expression.items.length != 1) + return; + UnaryExpression unary = cast(UnaryExpression) rs.expression.items[0]; + if (unary is null) + return; + if (unary.primaryExpression is null) + return; + if (unary.primaryExpression.primary != tok!"false") + return; + addErrorMessage(line, column, KEY, MESSAGE); + } + + override void visit(const Unittest u) + { + } + +private: + uint inStruct; + enum KEY = "dscanner.suspicios.incorrect_infinite_range"; + enum MESSAGE = "Use `enum bool empty = false;` to define an infinite range."; + size_t line; + size_t column; +} + +unittest +{ + import std.stdio : stderr; + import analysis.config : StaticAnalysisConfig; + import std.format : format; + + StaticAnalysisConfig sac; + sac.builtin_property_names_check = true; + assertAnalyzerWarnings(q{struct InfiniteRange +{ + bool empty() // [warn]: %1$s + { + return false; + } + + bool stuff() + { + return false; + } + + unittest + { + return false; + } +} + +bool empty() { return false; } +class C { bool empty() { return false; } } // [warn]: %1$s + +}c + .format(IncorrectInfiniteRangeCheck.MESSAGE), sac); + stderr.writeln("Unittest for IncorrectInfiniteRangeCheck passed."); +} diff --git a/src/analysis/run.d b/src/analysis/run.d index 79c7ce5..3f9230e 100644 --- a/src/analysis/run.d +++ b/src/analysis/run.d @@ -53,6 +53,7 @@ import analysis.mismatched_args; import analysis.label_var_same_name_check; import analysis.line_length; import analysis.auto_ref_assignment; +import analysis.incorrect_infinite_range; import dsymbol.string_interning : internString; import dsymbol.scope_; @@ -263,6 +264,7 @@ 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); version (none) if (analysisConfig.redundant_if_check) checks ~= new IfStatementCheck(fileName, moduleScope);