Replace libdparse with DMD in LambdaReturnCheck (#114)
This commit is contained in:
parent
4a0016c9ed
commit
f4dd1c0c72
|
|
@ -5,85 +5,90 @@
|
||||||
|
|
||||||
module dscanner.analysis.lambda_return_check;
|
module dscanner.analysis.lambda_return_check;
|
||||||
|
|
||||||
import dparse.ast;
|
|
||||||
import dparse.lexer;
|
|
||||||
import dscanner.analysis.base;
|
import dscanner.analysis.base;
|
||||||
import dscanner.utils : safeAccess;
|
import dmd.tokens : Token, TOK;
|
||||||
|
|
||||||
final class LambdaReturnCheck : BaseAnalyzer
|
// TODO: Fix AutoFix
|
||||||
|
extern (C++) class LambdaReturnCheck(AST) : BaseAnalyzerDmd
|
||||||
{
|
{
|
||||||
alias visit = BaseAnalyzer.visit;
|
alias visit = BaseAnalyzerDmd.visit;
|
||||||
|
|
||||||
mixin AnalyzerInfo!"lambda_return_check";
|
mixin AnalyzerInfo!"lambda_return_check";
|
||||||
|
|
||||||
this(BaseAnalyzerArguments args)
|
private enum KEY = "dscanner.confusing.lambda_returns_lambda";
|
||||||
|
private enum MSG = "This lambda returns a lambda. Add parenthesis to clarify.";
|
||||||
|
|
||||||
|
private Token[] tokens;
|
||||||
|
|
||||||
|
extern (D) this(string fileName, bool skipTests = false)
|
||||||
{
|
{
|
||||||
super(args);
|
super(fileName, skipTests);
|
||||||
|
lexFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(const FunctionLiteralExpression fLit)
|
private void lexFile()
|
||||||
{
|
{
|
||||||
import std.algorithm : find;
|
import dscanner.utils : readFile;
|
||||||
|
import dmd.errorsink : ErrorSinkNull;
|
||||||
|
import dmd.globals : global;
|
||||||
|
import dmd.lexer : Lexer;
|
||||||
|
|
||||||
auto fe = safeAccess(fLit).assignExpression.as!UnaryExpression
|
auto bytes = readFile(fileName) ~ '\0';
|
||||||
.primaryExpression.functionLiteralExpression.unwrap;
|
__gshared ErrorSinkNull errorSinkNull;
|
||||||
|
if (!errorSinkNull)
|
||||||
|
errorSinkNull = new ErrorSinkNull;
|
||||||
|
|
||||||
if (fe is null || fe.parameters !is null || fe.identifier != tok!"" ||
|
scope lexer = new Lexer(null, cast(char*) bytes, 0, bytes.length, 0, 0, errorSinkNull, &global.compileEnv);
|
||||||
fe.specifiedFunctionBody is null || fe.specifiedFunctionBody.blockStatement is null)
|
while (lexer.nextToken() != TOK.endOfFile)
|
||||||
{
|
tokens ~= lexer.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(AST.FuncLiteralDeclaration lambda)
|
||||||
|
{
|
||||||
|
import std.algorithm.iteration : filter;
|
||||||
|
import std.algorithm.searching : canFind, find, until;
|
||||||
|
|
||||||
|
super.visit(lambda);
|
||||||
|
|
||||||
|
if (lambda.fbody.isReturnStatement() is null)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
auto start = &fLit.tokens[0];
|
|
||||||
auto endIncl = &fe.specifiedFunctionBody.tokens[0];
|
|
||||||
assert(endIncl >= start);
|
|
||||||
auto tokens = start[0 .. endIncl - start + 1];
|
|
||||||
auto arrow = tokens.find!(a => a.type == tok!"=>");
|
|
||||||
|
|
||||||
AutoFix[] autofixes;
|
auto tokenRange = tokens.filter!(t => t.loc.fileOffset > lambda.loc.fileOffset)
|
||||||
if (arrow.length)
|
.filter!(t => t.loc.fileOffset < lambda.endloc.fileOffset)
|
||||||
{
|
.find!(t => t.value == TOK.goesTo);
|
||||||
if (fLit.tokens[0] == tok!"(")
|
|
||||||
autofixes ~= AutoFix.replacement(arrow[0], "", "Remove arrow (use function body)");
|
if (!tokenRange.canFind!(t => t.value == TOK.leftCurly))
|
||||||
else
|
return;
|
||||||
autofixes ~= AutoFix.insertionBefore(fLit.tokens[0], "(", "Remove arrow (use function body)")
|
|
||||||
.concat(AutoFix.insertionAfter(fLit.tokens[0], ")"))
|
if (!tokenRange.canFind!(t => t.value == TOK.leftParenthesis))
|
||||||
.concat(AutoFix.replacement(arrow[0], ""));
|
addErrorMessage(cast(ulong) lambda.loc.linnum, cast(ulong) lambda.loc.charnum, KEY, MSG);
|
||||||
}
|
|
||||||
autofixes ~= AutoFix.insertionBefore(*endIncl, "() ", "Add parenthesis (return delegate)");
|
|
||||||
addErrorMessage(tokens, KEY, "This lambda returns a lambda. Add parenthesis to clarify.",
|
|
||||||
autofixes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
enum KEY = "dscanner.confusing.lambda_returns_lambda";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows) {/*because of newline in code*/} else
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
import dscanner.analysis.config : Check, disabledConfig, StaticAnalysisConfig;
|
import dscanner.analysis.config : Check, disabledConfig, StaticAnalysisConfig;
|
||||||
import dscanner.analysis.helpers : assertAnalyzerWarnings, assertAutoFix;
|
import dscanner.analysis.helpers : assertAnalyzerWarningsDMD, assertAutoFix;
|
||||||
import std.stdio : stderr;
|
import std.stdio : stderr;
|
||||||
|
import std.format : format;
|
||||||
|
|
||||||
StaticAnalysisConfig sac = disabledConfig();
|
StaticAnalysisConfig sac = disabledConfig();
|
||||||
sac.lambda_return_check = Check.enabled;
|
sac.lambda_return_check = Check.enabled;
|
||||||
|
auto msg = "This lambda returns a lambda. Add parenthesis to clarify.";
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
int[] b;
|
int[] b;
|
||||||
auto a = b.map!(a => { return a * a + 2; }).array(); /+
|
auto a = b.map!(a => { return a * a + 2; }).array(); // [warn]: %s
|
||||||
^^^^^^ [warn]: This lambda returns a lambda. Add parenthesis to clarify. +/
|
pragma(msg, typeof(a => { return a; })); // [warn]: %s
|
||||||
pragma(msg, typeof(a => { return a; })); /+
|
pragma(msg, typeof((a) => { return a; })); // [warn]: %s
|
||||||
^^^^^^ [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({ return a; }));
|
||||||
pragma(msg, typeof(a => () { return a; }));
|
pragma(msg, typeof(a => () { return a; }));
|
||||||
|
b.map!(a => a * 2);
|
||||||
}
|
}
|
||||||
}c, sac);
|
}c.format(msg, msg, msg), sac);
|
||||||
|
|
||||||
|
|
||||||
|
/+ TODO: Fix AutoFix
|
||||||
assertAutoFix(q{
|
assertAutoFix(q{
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
|
|
@ -106,7 +111,7 @@ unittest
|
||||||
pragma(msg, typeof((a) { return a; })); // fix:0
|
pragma(msg, typeof((a) { return a; })); // fix:0
|
||||||
pragma(msg, typeof((a) => () { return a; })); // fix:1
|
pragma(msg, typeof((a) => () { return a; })); // fix:1
|
||||||
}
|
}
|
||||||
}c, sac);
|
}c, sac);+/
|
||||||
|
|
||||||
stderr.writeln("Unittest for LambdaReturnCheck passed.");
|
stderr.writeln("Unittest for LambdaReturnCheck passed.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -862,10 +862,6 @@ private BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName,
|
||||||
analysisConfig.long_line_check == Check.skipTests && !ut),
|
analysisConfig.long_line_check == Check.skipTests && !ut),
|
||||||
analysisConfig.max_line_length);
|
analysisConfig.max_line_length);
|
||||||
|
|
||||||
if (moduleName.shouldRun!LambdaReturnCheck(analysisConfig))
|
|
||||||
checks ~= new LambdaReturnCheck(args.setSkipTests(
|
|
||||||
analysisConfig.lambda_return_check == Check.skipTests && !ut));
|
|
||||||
|
|
||||||
if (moduleName.shouldRun!AutoFunctionChecker(analysisConfig))
|
if (moduleName.shouldRun!AutoFunctionChecker(analysisConfig))
|
||||||
checks ~= new AutoFunctionChecker(args.setSkipTests(
|
checks ~= new AutoFunctionChecker(args.setSkipTests(
|
||||||
analysisConfig.auto_function_check == Check.skipTests && !ut));
|
analysisConfig.auto_function_check == Check.skipTests && !ut));
|
||||||
|
|
@ -1342,6 +1338,12 @@ MessageSet analyzeDmd(string fileName, ASTCodegen.Module m, const char[] moduleN
|
||||||
config.label_var_same_name_check == Check.skipTests && !ut
|
config.label_var_same_name_check == Check.skipTests && !ut
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (moduleName.shouldRunDmd!(LambdaReturnCheck!ASTCodegen)(config))
|
||||||
|
visitors ~= new LambdaReturnCheck!ASTCodegen(
|
||||||
|
fileName,
|
||||||
|
config.lambda_return_check == Check.skipTests && !ut
|
||||||
|
);
|
||||||
|
|
||||||
foreach (visitor; visitors)
|
foreach (visitor; visitors)
|
||||||
{
|
{
|
||||||
m.accept(visitor);
|
m.accept(visitor);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue