Merge remote-tracking branch 'upstream/master' into phobos

This commit is contained in:
Sebastian Wilzbach 2018-06-27 05:52:28 +02:00
commit 6d526bbf25
14 changed files with 131 additions and 23 deletions

1
.gitignore vendored
View file

@ -31,6 +31,7 @@ dsc
# Git hash # Git hash
githash.txt githash.txt
githash_.txt
# GDB history # GDB history
.gdb_history .gdb_history

View file

@ -4,7 +4,19 @@ setlocal enabledelayedexpansion
if "%DC%"=="" set DC="dmd" if "%DC%"=="" set DC="dmd"
if "%DC%"=="ldc2" set DC="ldmd2" if "%DC%"=="ldc2" set DC="ldmd2"
set DFLAGS=-O -release -inline -version=StdLoggerDisableWarning :: git might not be installed, so we provide 0.0.0 as a fallback or use
:: the existing githash file if existent
git describe --tags > githash_.txt
for /f %%i in ("githash_.txt") do set githashsize=%%~zi
if %githashsize% == 0 (
if not exist "githash.txt" (
echo v0.0.0 > githash.txt
)
) else (
move /y githash_.txt githash.txt
)
set DFLAGS=-O -release -inline -version=StdLoggerDisableWarning -J.
set TESTFLAGS=-g -w -version=StdLoggerDisableWarning set TESTFLAGS=-g -w -version=StdLoggerDisableWarning
set CORE= set CORE=
set LIBDPARSE= set LIBDPARSE=

@ -1 +1 @@
Subproject commit e018446b028b92c34398032e698c05c0919630ee Subproject commit 3f7eaa1b1e6dd0c5e14d500d0b3d4ba1e1e41138

View file

@ -12,8 +12,8 @@
"StdLoggerDisableWarning" "StdLoggerDisableWarning"
], ],
"dependencies" : { "dependencies" : {
"libdparse" : "~>0.8.6", "libdparse" : "~>0.8.7",
"dsymbol" : "~>0.3.7", "dsymbol" : "~>0.3.10",
"inifiled" : "~>1.3.1", "inifiled" : "~>1.3.1",
"emsi_containers" : "~>0.8.0-alpha.7", "emsi_containers" : "~>0.8.0-alpha.7",
"libddoc" : "~>0.3.0-beta.1", "libddoc" : "~>0.3.0-beta.1",
@ -24,6 +24,6 @@
"bin" "bin"
], ],
"preGenerateCommands" : [ "preGenerateCommands" : [
"rdmd --eval=\"auto dir=environment.get(\\\"DUB_PACKAGE_DIR\\\"); dir.buildPath(\\\"bin\\\").mkdirRecurse; auto gitVer = (\\\"git -C \\\"~dir~\\\" describe --tags\\\").executeShell; (gitVer.status == 0 ? gitVer.output : dir.dirName.baseName.findSplitAfter(environment.get(\\\"DUB_ROOT_PACKAGE\\\")~\\\"-\\\")[1]).ifThrown(\\\"0.0.0\\\").toFile(dir.buildPath(\\\"bin\\\", \\\"dubhash.txt\\\"));\"" "rdmd --eval=\"auto dir=environment.get(\\\"DUB_PACKAGE_DIR\\\"); dir.buildPath(\\\"bin\\\").mkdirRecurse; auto gitVer = (\\\"git -C \\\"~dir~\\\" describe --tags\\\").executeShell; (gitVer.status == 0 ? gitVer.output.strip : \\\"v\\\" ~ dir.dirName.baseName.findSplitAfter(environment.get(\\\"DUB_ROOT_PACKAGE\\\")~\\\"-\\\")[1]).ifThrown(\\\"0.0.0\\\").chain(newline).toFile(dir.buildPath(\\\"bin\\\", \\\"dubhash.txt\\\"));\""
] ]
} }

@ -1 +1 @@
Subproject commit 41c567460dee3fab9db0c0f7775534b1d70f1f27 Subproject commit 086cf06051bb1f33c94891ba6c39a57f164ee296

View file

@ -19,6 +19,7 @@ fi
archiveName="dscanner-$VERSION-$OS-$ARCH_SUFFIX.zip" archiveName="dscanner-$VERSION-$OS-$ARCH_SUFFIX.zip"
echo "Building $archiveName" echo "Building $archiveName"
mkdir -p bin mkdir -p bin
git describe --tags > githash.txt # no git installed under Wine
DC="$DIR/dmd2/windows/bin/dmd.exe" wine cmd /C build.bat DC="$DIR/dmd2/windows/bin/dmd.exe" wine cmd /C build.bat
cd bin cd bin

View file

@ -49,7 +49,7 @@ public:
_returns[$-1] = false; _returns[$-1] = false;
const bool autoFun = decl.storageClasses const bool autoFun = decl.storageClasses
.any!(a => a.token.type == tok!"auto"); .any!(a => a.token.type == tok!"auto" || a.atAttribute !is null);
decl.accept(this); decl.accept(this);
@ -216,6 +216,16 @@ unittest
AutoFunctionChecker.MESSAGE, AutoFunctionChecker.MESSAGE,
), sac); ), sac);
assertAnalyzerWarnings(q{
@property doStuff(){} // [warn]: %s
@safe doStuff(){} // [warn]: %s
@disable doStuff();
@safe void doStuff();
}c.format(
AutoFunctionChecker.MESSAGE,
AutoFunctionChecker.MESSAGE,
), sac);
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
enum _genSave = "return true;"; enum _genSave = "return true;";
auto doStuff(){ mixin(_genSave);} auto doStuff(){ mixin(_genSave);}

View file

@ -31,7 +31,7 @@ final class IfElseSameCheck : BaseAnalyzer
override void visit(const IfStatement ifStatement) override void visit(const IfStatement ifStatement)
{ {
if (ifStatement.thenStatement == ifStatement.elseStatement) if (ifStatement.thenStatement && (ifStatement.thenStatement == ifStatement.elseStatement))
addErrorMessage(ifStatement.line, ifStatement.column, addErrorMessage(ifStatement.line, ifStatement.column,
"dscanner.bugs.if_else_same", "'Else' branch is identical to 'Then' branch."); "dscanner.bugs.if_else_same", "'Else' branch is identical to 'Then' branch.");
ifStatement.accept(this); ifStatement.accept(this);
@ -95,5 +95,13 @@ unittest
person = "bobby"; // not same person = "bobby"; // not same
} }
}c, sac); }c, sac);
assertAnalyzerWarnings(q{
void foo()
{
if (auto stuff = call())
}
}c, sac);
stderr.writeln("Unittest for IfElseSameCheck passed."); stderr.writeln("Unittest for IfElseSameCheck passed.");
} }

View file

@ -62,7 +62,9 @@ final class IncorrectInfiniteRangeCheck : BaseAnalyzer
override void visit(const ReturnStatement rs) override void visit(const ReturnStatement rs)
{ {
if (rs.expression.items.length != 1) if (inStruct == 0 || line == size_t.max) // not within a struct yet
return;
if (!rs.expression || rs.expression.items.length != 1)
return; return;
UnaryExpression unary = cast(UnaryExpression) rs.expression.items[0]; UnaryExpression unary = cast(UnaryExpression) rs.expression.items[0];
if (unary is null) if (unary is null)
@ -82,7 +84,7 @@ private:
uint inStruct; uint inStruct;
enum string KEY = "dscanner.suspicious.incorrect_infinite_range"; enum string KEY = "dscanner.suspicious.incorrect_infinite_range";
enum string MESSAGE = "Use `enum bool empty = false;` to define an infinite range."; enum string MESSAGE = "Use `enum bool empty = false;` to define an infinite range.";
size_t line; size_t line = size_t.max;
size_t column; size_t column;
} }
@ -123,6 +125,34 @@ class C { bool empty() { return false; } } // [warn]: %1$s
}c }c
.format(IncorrectInfiniteRangeCheck.MESSAGE), sac); .format(IncorrectInfiniteRangeCheck.MESSAGE), sac);
}
// test for https://github.com/dlang-community/D-Scanner/issues/656
// unittests are skipped but D-Scanner sources are self checked
version(none) struct Foo
{
void empty()
{
return;
}
}
unittest
{
import std.stdio : stderr;
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
import std.format : format;
StaticAnalysisConfig sac = disabledConfig();
sac.incorrect_infinite_range_check = Check.enabled;
assertAnalyzerWarnings(q{
enum isAllZeroBits = ()
{
if (true)
return true;
else
return false;
}();
}, sac);
stderr.writeln("Unittest for IncorrectInfiniteRangeCheck passed."); stderr.writeln("Unittest for IncorrectInfiniteRangeCheck passed.");
} }

View file

@ -51,13 +51,25 @@ final class ObjectConstCheck : BaseAnalyzer
constBlock = true; constBlock = true;
} }
if (inAggregate && d.functionDeclaration !is null && !constColon && !constBlock bool containsDisable(A)(const A[] attribs)
&& isInteresting(d.functionDeclaration.name.text)
&& !hasConst(d.functionDeclaration.memberFunctionAttributes))
{ {
addErrorMessage(d.functionDeclaration.name.line, import std.algorithm.searching : canFind;
d.functionDeclaration.name.column, "dscanner.suspicious.object_const", return attribs.canFind!(a => a.atAttribute !is null &&
"Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const."); a.atAttribute.identifier.text == "disable");
}
if (const FunctionDeclaration fd = d.functionDeclaration)
{
const isDeclationDisabled = containsDisable(d.attributes) ||
containsDisable(fd.memberFunctionAttributes);
if (inAggregate && !constColon && !constBlock && !isDeclationDisabled
&& isInteresting(fd.name.text) && !hasConst(fd.memberFunctionAttributes))
{
addErrorMessage(d.functionDeclaration.name.line,
d.functionDeclaration.name.column, "dscanner.suspicious.object_const",
"Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const.");
}
} }
d.accept(this); d.accept(this);
@ -130,6 +142,16 @@ unittest
const{ override string toString() { return "foo"; }} // ok const{ override string toString() { return "foo"; }} // ok
} }
class Rat
{
bool opEquals(Object a, Object b) @disable; // ok
}
class Ant
{
@disable bool opEquals(Object a, Object b); // ok
}
// Will warn, because none are const // Will warn, because none are const
class Dog class Dog
{ {

View file

@ -54,6 +54,19 @@ final class OpEqualsWithoutToHashCheck : BaseAnalyzer
if (!declaration || !declaration.functionDeclaration) if (!declaration || !declaration.functionDeclaration)
continue; continue;
bool containsDisable(A)(const A[] attribs)
{
import std.algorithm.searching : canFind;
return attribs.canFind!(a => a.atAttribute !is null &&
a.atAttribute.identifier.text == "disable");
}
const isDeclationDisabled = containsDisable(declaration.attributes) ||
containsDisable(declaration.functionDeclaration.memberFunctionAttributes);
if (isDeclationDisabled)
continue;
// Check if opEquals or toHash // Check if opEquals or toHash
immutable string methodName = declaration.functionDeclaration.name.text; immutable string methodName = declaration.functionDeclaration.name.text;
if (methodName == "opEquals") if (methodName == "opEquals")
@ -146,6 +159,13 @@ unittest
return 0; return 0;
} }
} }
// issue #659, do not warn if one miss and the other is not callable
struct Fox {const nothrow @safe hash_t toHash() @disable;}
struct Bat {@disable const nothrow @safe hash_t toHash();}
struct Rat {const bool opEquals(Object a, Object b) @disable;}
struct Cat {@disable const bool opEquals(Object a, Object b);}
}c, sac); }c, sac);
stderr.writeln("Unittest for OpEqualsWithoutToHashCheck passed."); stderr.writeln("Unittest for OpEqualsWithoutToHashCheck passed.");

View file

@ -57,7 +57,11 @@ public:
override void visit(const Declaration d) override void visit(const Declaration d)
{ {
const oldCheckAtAttribute = checkAtAttribute; const oldCheckAtAttribute = checkAtAttribute;
checkAtAttribute = d.functionDeclaration is null;
checkAtAttribute = d.functionDeclaration is null && d.unittest_ is null &&
d.constructor is null && d.destructor is null &&
d.staticConstructor is null && d.staticDestructor is null &&
d.sharedStaticConstructor is null && d.sharedStaticDestructor is null;
d.accept(this); d.accept(this);
checkAtAttribute = oldCheckAtAttribute; checkAtAttribute = oldCheckAtAttribute;
} }
@ -145,5 +149,10 @@ unittest
alias nothrow @trusted uint F4(); alias nothrow @trusted uint F4();
}c , sac); }c , sac);
assertAnalyzerWarnings(q{
@trusted ~this();
@trusted this();
}c , sac);
stderr.writeln("Unittest for TrustTooMuchCheck passed."); stderr.writeln("Unittest for TrustTooMuchCheck passed.");
} }

View file

@ -39,7 +39,7 @@ private:
} }
else else
{ {
enum msg = `Variable %s initializer is useless because it does not differ from the default value`; enum msg = "Variable `%s` initializer is useless because it does not differ from the default value";
} }
static immutable intDefs = ["0", "0L", "0UL", "0uL", "0U", "0x0", "0b0"]; static immutable intDefs = ["0", "0L", "0UL", "0uL", "0U", "0x0", "0b0"];

View file

@ -8,16 +8,11 @@ module dscanner.dscanner_version;
/** /**
* Human-readable version number * Human-readable version number
*/ */
enum DEFAUULT_DSCANNER_VERSION = "v0.5.5";
version (built_with_dub) version (built_with_dub)
{ {
enum DSCANNER_VERSION = import("dubhash.txt"); enum DSCANNER_VERSION = import("dubhash.txt");
} }
else version (Windows)
{
enum DSCANNER_VERSION = DEFAUULT_DSCANNER_VERSION;
}
else else
{ {
/** /**