diff --git a/dub.json b/dub.json index 86e350c..99b8200 100644 --- a/dub.json +++ b/dub.json @@ -7,7 +7,7 @@ "targetType": "executable", "versions": ["built_with_dub"], "dependencies": { - "libdparse": "~>0.7.0-alpha9", + "libdparse": "~>0.7.0-alpha10", "dsymbol": "~>0.2.0-alpha6", "inifiled": ">=0.0.6", }, diff --git a/libdparse b/libdparse index 80f054e..c1dd27e 160000 --- a/libdparse +++ b/libdparse @@ -1 +1 @@ -Subproject commit 80f054e7d137de3b7907ee4a6406ffad5af9a6ea +Subproject commit c1dd27ea248111bd1dc9f8fb8a24f54592907110 diff --git a/src/analysis/config.d b/src/analysis/config.d index e2cf87e..7dc2625 100644 --- a/src/analysis/config.d +++ b/src/analysis/config.d @@ -116,4 +116,7 @@ struct StaticAnalysisConfig @INI("Checks for else if that should be else static if") bool static_if_else_check; + + @INI("Check for unclear lambda syntax") + bool lambda_return_check; } diff --git a/src/analysis/lambda_return_check.d b/src/analysis/lambda_return_check.d new file mode 100644 index 0000000..e16cea8 --- /dev/null +++ b/src/analysis/lambda_return_check.d @@ -0,0 +1,68 @@ +// Copyright Brian Schott (Hackerpilot) 2016. +// 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.lambda_return_check; + +import dparse.ast; +import dparse.lexer; +import analysis.base; + +class LambdaReturnCheck : BaseAnalyzer +{ + alias visit = BaseAnalyzer.visit; + + this(string fileName) + { + super(fileName, null); + } + + override void visit(const FunctionLiteralExpression fLit) + { + if (fLit.assignExpression is null) + return; + const UnaryExpression unary = cast(const UnaryExpression) fLit.assignExpression; + if (unary is null) + return; + if (unary.primaryExpression is null) + return; + if (unary.primaryExpression.functionLiteralExpression is null) + return; + if (unary.primaryExpression.functionLiteralExpression.parameters !is null) + return; + if (unary.primaryExpression.functionLiteralExpression.identifier != tok!"") + return; + if (unary.primaryExpression.functionLiteralExpression.functionBody is null) + return; + if (unary.primaryExpression.functionLiteralExpression.functionBody.blockStatement is null) + return; + addErrorMessage(fLit.line, fLit.column, KEY, "This lambda returns a lambda. Add parenthesis to clarify."); + } + +private: + enum KEY = "dscanner.confusing.lambda_returns_lambda"; +} + +unittest +{ + import analysis.helpers : assertAnalyzerWarnings; + import analysis.config : StaticAnalysisConfig; + import std.stdio : stderr; + + StaticAnalysisConfig sac; + sac.lambda_return_check = true; + + auto code = ` + void main() + { + int[] b; + auto a = b.map!(a => { return a * a + 2; }).array(); // [warn]: This lambda returns a lambda. Add parenthesis to clarify. + pragma(msg, typeof(a => { return a; })); // [warn]: This lambda returns a lambda. Add parenthesis to clarify. + pragma(msg, typeof((a) => { return a; })); // [warn]: This lambda returns a lambda. Add parenthesis to clarify. + pragma(msg, typeof({ return a; })); + pragma(msg, typeof(a => () { return a; })); + }`c; + assertAnalyzerWarnings(code, sac); + stderr.writeln("Unittest for LambdaReturnCheck passed."); +} diff --git a/src/analysis/run.d b/src/analysis/run.d index db216e1..06ce9e3 100644 --- a/src/analysis/run.d +++ b/src/analysis/run.d @@ -58,6 +58,7 @@ import analysis.incorrect_infinite_range; import analysis.useless_assert; import analysis.alias_syntax_check; import analysis.static_if_else; +import analysis.lambda_return_check; import dsymbol.string_interning : internString; import dsymbol.scope_; @@ -276,6 +277,8 @@ MessageSet analyze(string fileName, const Module m, const StaticAnalysisConfig a checks ~= new AliasSyntaxCheck(fileName); if (analysisConfig.static_if_else_check) checks ~= new StaticIfElse(fileName); + if (analysisConfig.lambda_return_check) + checks ~= new LambdaReturnCheck(fileName); version (none) if (analysisConfig.redundant_if_check) checks ~= new IfStatementCheck(fileName, moduleScope);