diff --git a/README.md b/README.md index f158722..f9e2e4c 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ the given source files. * Subtraction from .length properties. (These may be unsigned and could lead to integer underflow) * Class, struct, and union member variables whose names conflict with built-in type properties. * Confusing asm syntax. +* Placement of const, immutable, or inout before a function return type instead of after the parameters. #### Wishlish * Assigning to foreach variables that are not "ref". diff --git a/src/analysis/config.d b/src/analysis/config.d index 67d50ac..c1d215f 100644 --- a/src/analysis/config.d +++ b/src/analysis/config.d @@ -71,4 +71,7 @@ struct StaticAnalysisConfig @INI("Checks for undocumented public declarations") bool undocumented_declaration_check; + + @INI("Checks for poor placement of function attributes") + bool function_attribute_check; } diff --git a/src/analysis/function_attributes.d b/src/analysis/function_attributes.d new file mode 100644 index 0000000..041ca2e --- /dev/null +++ b/src/analysis/function_attributes.d @@ -0,0 +1,60 @@ +// Copyright Brian Schott (Hackerpilot) 2014. +// 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.function_attributes; + +import std.d.ast; +import std.d.lexer; +import analysis.base; + +import std.stdio; + +/** + * Prefer + * --- + * int getStuff() const {} + * --- + * to + * --- + * const int getStuff() {} + * --- + */ +class FunctionAttributeCheck : BaseAnalyzer +{ + alias visit = BaseAnalyzer.visit; + + this(string fileName) + { + super(fileName); + } + + override void visit(const Declaration dec) + { + if (dec.functionDeclaration is null) + goto end; + if (dec.attributes.length == 0) + goto end; + foreach (attr; dec.attributes) + { + if (attr.storageClass is null) + continue; + if (attr.storageClass.token == tok!"const" + || attr.storageClass.token == tok!"inout" + || attr.storageClass.token == tok!"immutable") + { + import std.string : format; + immutable string attrString = str(attr.storageClass.token.type); + addErrorMessage(dec.functionDeclaration.name.line, + dec.functionDeclaration.name.column, KEY, + format("'%s' is not an attribute of the return type." + ~ " Place it after the parameter list to clarify.", attrString)); + } + } + end: + dec.accept(this); + } + + private enum KEY = "dscanner.confusing.function_attributes"; +} diff --git a/src/analysis/run.d b/src/analysis/run.d index 9f71984..7b46a59 100644 --- a/src/analysis/run.d +++ b/src/analysis/run.d @@ -36,6 +36,7 @@ import analysis.asm_style; import analysis.logic_precedence; import analysis.stats_collector; import analysis.undocumented; +import analysis.function_attributes; bool first = true; @@ -181,6 +182,7 @@ MessageSet analyze(string fileName, const Module m, if (analysisConfig.asm_style_check) checks ~= new AsmStyleCheck(fileName); if (analysisConfig.logical_precedence_check) checks ~= new LogicPrecedenceCheck(fileName); if (analysisConfig.undocumented_declaration_check) checks ~= new UndocumentedDeclarationCheck(fileName); + if (analysisConfig.function_attribute_check) checks ~= new FunctionAttributeCheck(fileName); foreach (check; checks) {