mirror of
https://github.com/dlang-community/D-Scanner.git
synced 2025-04-26 05:10:03 +03:00
Merge remote-tracking branch 'upstream/master' into phobos
This commit is contained in:
commit
6d526bbf25
14 changed files with 131 additions and 23 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -31,6 +31,7 @@ dsc
|
||||||
|
|
||||||
# Git hash
|
# Git hash
|
||||||
githash.txt
|
githash.txt
|
||||||
|
githash_.txt
|
||||||
|
|
||||||
# GDB history
|
# GDB history
|
||||||
.gdb_history
|
.gdb_history
|
||||||
|
|
14
build.bat
14
build.bat
|
@ -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=
|
||||||
|
|
2
dsymbol
2
dsymbol
|
@ -1 +1 @@
|
||||||
Subproject commit e018446b028b92c34398032e698c05c0919630ee
|
Subproject commit 3f7eaa1b1e6dd0c5e14d500d0b3d4ba1e1e41138
|
6
dub.json
6
dub.json
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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);}
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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.");
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"];
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue