Added an option that allows to skip the unittests analysis

allow each check to be individually skipped in the unit tests

useless assertions must always be detected
This commit is contained in:
Basile Burg 2016-05-12 17:14:32 +02:00
parent 09e054f869
commit c101a6e1f7
39 changed files with 370 additions and 234 deletions

View File

@ -43,6 +43,12 @@ analysis and it does not compile the code.
The "--styleCheck" or "-S" option runs some basic static analysis checks against The "--styleCheck" or "-S" option runs some basic static analysis checks against
the given source files. the given source files.
#### Skip style checks in the tests
Static checks in the unit tests can produce irrelevant warnings. For example,
it's legit to declare a variable that's not used if the goal is to verify that
a templatized function can be instantiated by inference of the type of this variable.
To avoid these cases, it's possible to pass the "--skipTests" option.
#### Configuration #### Configuration
By default all checks are enabled. Individual checks can be enabled or disabled By default all checks are enabled. Individual checks can be enabled or disabled
by using a configuration file. Running ```dscanner --defaultConfig``` will by using a configuration file. Running ```dscanner --defaultConfig``` will
@ -50,6 +56,16 @@ generate a default configuration file and print the file's location. The
"--config" option will allow you to specify the path to a configuration file if "--config" option will allow you to specify the path to a configuration file if
you do not want to use the one created by the "--defaultConfig" option. you do not want to use the one created by the "--defaultConfig" option.
For each check, three values are possible:
* `"disabled"`: the check is not performed.
* `"enabled"`: the check is performed.
* `"skip-unittest"`: the check is performed but not in the unit tests.
Any other value deactivates a check.
Note that the "--skipTests" option is the equivalent of changing each
`"enabled"` check by a `"skip-unittest"` check.
#### Implemented checks #### Implemented checks
* Old alias syntax (i.e "alias a b;" should be replaced with "alias b = a;"). * Old alias syntax (i.e "alias a b;" should be replaced with "alias b = a;").
* Implicit concatenation of string literals. * Implicit concatenation of string literals.
@ -85,7 +101,7 @@ you do not want to use the one created by the "--defaultConfig" option.
* Unused labels. * Unused labels.
* Lines longer than 120 characters. * Lines longer than 120 characters.
* Incorrect infinite range definitions. * Incorrect infinite range definitions.
* Some assertions that check conditions that will always be true. * Some assertions that check conditions that will always be true. This check can't be skipped in the tests.
#### Wishlist #### Wishlist

View File

@ -16,9 +16,9 @@ class AliasSyntaxCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName) this(string fileName, bool skipTests = false)
{ {
super(fileName, null); super(fileName, null, skipTests);
} }
override void visit(const AliasDeclaration ad) override void visit(const AliasDeclaration ad)
@ -40,11 +40,11 @@ private:
unittest unittest
{ {
import analysis.helpers : assertAnalyzerWarnings; import analysis.helpers : assertAnalyzerWarnings;
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import std.stdio : stderr; import std.stdio : stderr;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.alias_syntax_check = true; sac.alias_syntax_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
alias int abcde; // [warn]: Prefer the new "'alias' identifier '=' type ';'" syntax to the old "'alias' type identifier ';'" syntax. alias int abcde; // [warn]: Prefer the new "'alias' identifier '=' type ';'" syntax to the old "'alias' type identifier ';'" syntax.
alias abcde = int; alias abcde = int;

View File

@ -20,9 +20,9 @@ class AsmStyleCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const AsmBrExp brExp) override void visit(const AsmBrExp brExp)
@ -39,10 +39,10 @@ class AsmStyleCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.asm_style_check = true; sac.asm_style_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testAsm() void testAsm()
{ {

View File

@ -15,9 +15,9 @@ import analysis.base;
class AutoRefAssignmentCheck : BaseAnalyzer class AutoRefAssignmentCheck : BaseAnalyzer
{ {
/// ///
this(string fileName) this(string fileName, bool skipTests = false)
{ {
super(fileName, null); super(fileName, null, skipTests);
} }
override void visit(const Module m) override void visit(const Module m)
@ -113,11 +113,11 @@ unittest
{ {
import std.stdio : stderr; import std.stdio : stderr;
import std.format : format; import std.format : format;
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import analysis.helpers : assertAnalyzerWarnings; import analysis.helpers : assertAnalyzerWarnings;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.auto_ref_assignment_check = true; sac.auto_ref_assignment_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
int doStuff(T)(auto ref int a) int doStuff(T)(auto ref int a)
{ {

View File

@ -27,10 +27,11 @@ alias MessageSet = RedBlackTree!(Message, comparitor, true);
abstract class BaseAnalyzer : ASTVisitor abstract class BaseAnalyzer : ASTVisitor
{ {
public: public:
this(string fileName, const Scope* sc) this(string fileName, const Scope* sc, bool skipTests = false)
{ {
this.sc = sc; this.sc = sc;
this.fileName = fileName; this.fileName = fileName;
this.skipTests = skipTests;
_messages = new MessageSet; _messages = new MessageSet;
} }
@ -39,9 +40,24 @@ public:
return _messages[].array; return _messages[].array;
} }
alias visit = ASTVisitor.visit;
/**
* Visits a unittest.
*
* When overriden, the protected bool "skipTests" should be handled
* so that the content of the test is not analyzed.
*/
override void visit(const Unittest unittest_)
{
if (!skipTests)
unittest_.accept(this);
}
protected: protected:
bool inAggregate = false; bool inAggregate = false;
bool skipTests;
template visitTemplate(T) template visitTemplate(T)
{ {

View File

@ -30,9 +30,9 @@ class BuiltinPropertyNameCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const FunctionDeclaration fd) override void visit(const FunctionDeclaration fd)
@ -101,10 +101,10 @@ private:
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.builtin_property_names_check = true; sac.builtin_property_names_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
class SomeClass class SomeClass
{ {

View File

@ -17,9 +17,9 @@ class CommaExpressionCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const Expression ex) override void visit(const Expression ex)

View File

@ -10,113 +10,138 @@ import inifiled;
StaticAnalysisConfig defaultStaticAnalysisConfig() StaticAnalysisConfig defaultStaticAnalysisConfig()
{ {
StaticAnalysisConfig config; StaticAnalysisConfig config;
foreach (mem; __traits(allMembers, StaticAnalysisConfig)) config.fillConfig!(Check.enabled);
mixin("config." ~ mem ~ " = true;");
return config; return config;
} }
enum Check: string
{
disabled = "disabled",
enabled = "enabled",
skipTests = "skip-unittest"
}
void fillConfig(string check)(ref StaticAnalysisConfig config)
{
foreach (mem; __traits(allMembers, StaticAnalysisConfig))
{
static if (is(typeof(__traits(getMember, StaticAnalysisConfig, mem))))
static if (is(typeof(__traits(getMember, config, mem)) == string))
__traits(getMember, config, mem) = check;
}
}
unittest
{
StaticAnalysisConfig c;
c.fillConfig!(Check.enabled);
assert(c.enum_array_literal_check == Check.enabled);
fillConfig!(Check.skipTests)(c);
assert(c.alias_syntax_check == Check.skipTests);
}
@INI("Configure which static analysis checks are enabled") @INI("Configure which static analysis checks are enabled")
struct StaticAnalysisConfig struct StaticAnalysisConfig
{ {
@INI("Check variable, class, struct, interface, union, and function names against the Phobos style guide") @INI("Check variable, class, struct, interface, union, and function names against the Phobos style guide")
bool style_check; string style_check = Check.disabled;
@INI("Check for array literals that cause unnecessary allocation") @INI("Check for array literals that cause unnecessary allocation")
bool enum_array_literal_check; string enum_array_literal_check = Check.disabled;
@INI("Check for poor exception handling practices") @INI("Check for poor exception handling practices")
bool exception_check; string exception_check = Check.disabled;
@INI("Check for use of the deprecated 'delete' keyword") @INI("Check for use of the deprecated 'delete' keyword")
bool delete_check; string delete_check = Check.disabled;
@INI("Check for use of the deprecated floating point operators") @INI("Check for use of the deprecated floating point operators")
bool float_operator_check; string float_operator_check = Check.disabled;
@INI("Check number literals for readability") @INI("Check number literals for readability")
bool number_style_check; string number_style_check = Check.disabled;
@INI("Checks that opEquals, opCmp, toHash, and toString are either const, immutable, or inout.") @INI("Checks that opEquals, opCmp, toHash, and toString are either const, immutable, or inout.")
bool object_const_check; string object_const_check = Check.disabled;
@INI("Checks for .. expressions where the left side is larger than the right.") @INI("Checks for .. expressions where the left side is larger than the right.")
bool backwards_range_check; string backwards_range_check = Check.disabled;
@INI("Checks for if statements whose 'then' block is the same as the 'else' block") @INI("Checks for if statements whose 'then' block is the same as the 'else' block")
bool if_else_same_check; string if_else_same_check = Check.disabled;
@INI("Checks for some problems with constructors") @INI("Checks for some problems with constructors")
bool constructor_check; string constructor_check = Check.disabled;
@INI("Checks for unused variables and function parameters") @INI("Checks for unused variables and function parameters")
bool unused_variable_check; string unused_variable_check = Check.disabled;
@INI("Checks for unused labels") @INI("Checks for unused labels")
bool unused_label_check; string unused_label_check = Check.disabled;
@INI("Checks for duplicate attributes") @INI("Checks for duplicate attributes")
bool duplicate_attribute; string duplicate_attribute = Check.disabled;
@INI("Checks that opEquals and toHash are both defined or neither are defined") @INI("Checks that opEquals and toHash are both defined or neither are defined")
bool opequals_tohash_check; string opequals_tohash_check = Check.disabled;
@INI("Checks for subtraction from .length properties") @INI("Checks for subtraction from .length properties")
bool length_subtraction_check; string length_subtraction_check = Check.disabled;
@INI("Checks for methods or properties whose names conflict with built-in properties") @INI("Checks for methods or properties whose names conflict with built-in properties")
bool builtin_property_names_check; string builtin_property_names_check = Check.disabled;
@INI("Checks for confusing code in inline asm statements") @INI("Checks for confusing code in inline asm statements")
bool asm_style_check; string asm_style_check = Check.disabled;
@INI("Checks for confusing logical operator precedence") @INI("Checks for confusing logical operator precedence")
bool logical_precedence_check; string logical_precedence_check = Check.disabled;
@INI("Checks for undocumented public declarations") @INI("Checks for undocumented public declarations")
bool undocumented_declaration_check; string undocumented_declaration_check = Check.disabled;
@INI("Checks for poor placement of function attributes") @INI("Checks for poor placement of function attributes")
bool function_attribute_check; string function_attribute_check = Check.disabled;
@INI("Checks for use of the comma operator") @INI("Checks for use of the comma operator")
bool comma_expression_check; string comma_expression_check = Check.disabled;
@INI("Checks for local imports that are too broad") @INI("Checks for local imports that are too broad")
bool local_import_check; string local_import_check = Check.disabled;
@INI("Checks for variables that could be declared immutable") @INI("Checks for variables that could be declared immutable")
bool could_be_immutable_check = false; // disabled by default for now string could_be_immutable_check = Check.disabled; // disabled by default for now
@INI("Checks for redundant expressions in if statements") @INI("Checks for redundant expressions in if statements")
bool redundant_if_check; string redundant_if_check = Check.disabled;
@INI("Checks for redundant parenthesis") @INI("Checks for redundant parenthesis")
bool redundant_parens_check; string redundant_parens_check = Check.disabled;
@INI("Checks for mismatched argument and parameter names") @INI("Checks for mismatched argument and parameter names")
bool mismatched_args_check; string mismatched_args_check = Check.disabled;
@INI("Checks for labels with the same name as variables") @INI("Checks for labels with the same name as variables")
bool label_var_same_name_check; string label_var_same_name_check = Check.disabled;
@INI("Checks for lines longer than 120 characters") @INI("Checks for lines longer than 120 characters")
bool long_line_check; string long_line_check = Check.disabled;
@INI("Checks for assignment to auto-ref function parameters") @INI("Checks for assignment to auto-ref function parameters")
bool auto_ref_assignment_check; string auto_ref_assignment_check = Check.disabled;
@INI("Checks for incorrect infinite range definitions") @INI("Checks for incorrect infinite range definitions")
bool incorrect_infinite_range_check; string incorrect_infinite_range_check = Check.disabled;
@INI("Checks for asserts that are always true") @INI("Checks for asserts that are always true")
bool useless_assert_check; string useless_assert_check = Check.disabled;
@INI("Check for uses of the old-style alias syntax") @INI("Check for uses of the old-style alias syntax")
bool alias_syntax_check; string alias_syntax_check = Check.disabled;
@INI("Checks for else if that should be else static if") @INI("Checks for else if that should be else static if")
bool static_if_else_check; string static_if_else_check = Check.disabled;
@INI("Check for unclear lambda syntax") @INI("Check for unclear lambda syntax")
bool lambda_return_check; string lambda_return_check = Check.disabled;
} }

View File

@ -11,9 +11,9 @@ class ConstructorCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const ClassDeclaration classDeclaration) override void visit(const ClassDeclaration classDeclaration)
@ -90,10 +90,10 @@ private:
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.constructor_check = true; sac.constructor_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
class Cat // [warn]: This class has a zero-argument constructor as well as a constructor with one default argument. This can be confusing. class Cat // [warn]: This class has a zero-argument constructor as well as a constructor with one default argument. This can be confusing.
{ {

View File

@ -18,9 +18,9 @@ class DeleteCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const DeleteExpression d) override void visit(const DeleteExpression d)
@ -33,11 +33,11 @@ class DeleteCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import analysis.helpers : assertAnalyzerWarnings; import analysis.helpers : assertAnalyzerWarnings;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.delete_check = true; sac.delete_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testDelete() void testDelete()
{ {

View File

@ -21,9 +21,9 @@ class DuplicateAttributeCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const Declaration node) override void visit(const Declaration node)
@ -153,10 +153,10 @@ class DuplicateAttributeCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.duplicate_attribute = true; sac.duplicate_attribute = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
class ExampleAttributes class ExampleAttributes
{ {

View File

@ -19,9 +19,9 @@ class EnumArrayLiteralCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
bool looking = false; bool looking = false;

View File

@ -21,9 +21,9 @@ class FloatOperatorCheck : BaseAnalyzer
enum string KEY = "dscanner.deprecated.floating_point_operators"; enum string KEY = "dscanner.deprecated.floating_point_operators";
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const RelExpression r) override void visit(const RelExpression r)
@ -42,10 +42,10 @@ class FloatOperatorCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.float_operator_check = true; sac.float_operator_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testFish() void testFish()
{ {

View File

@ -25,9 +25,9 @@ class FunctionAttributeCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const InterfaceDeclaration dec) override void visit(const InterfaceDeclaration dec)

View File

@ -13,9 +13,9 @@ import dsymbol.scope_ : Scope;
class IfStatementCheck : BaseAnalyzer class IfStatementCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const IfStatement ifStatement) override void visit(const IfStatement ifStatement)

View File

@ -24,9 +24,9 @@ class IfElseSameCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const IfStatement ifStatement) override void visit(const IfStatement ifStatement)
@ -77,10 +77,10 @@ class IfElseSameCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.if_else_same_check = true; sac.if_else_same_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testSizeT() void testSizeT()
{ {

View File

@ -18,9 +18,9 @@ class IncorrectInfiniteRangeCheck : BaseAnalyzer
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
/// ///
this(string fileName) this(string fileName, bool skipTests = false)
{ {
super(fileName, null); super(fileName, null, skipTests);
} }
override void visit(const StructBody structBody) override void visit(const StructBody structBody)
@ -88,11 +88,11 @@ private:
unittest unittest
{ {
import std.stdio : stderr; import std.stdio : stderr;
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import std.format : format; import std.format : format;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.incorrect_infinite_range_check = true; sac.incorrect_infinite_range_check = Check.enabled;
assertAnalyzerWarnings(q{struct InfiniteRange assertAnalyzerWarnings(q{struct InfiniteRange
{ {
bool empty() // [warn]: %1$s bool empty() // [warn]: %1$s

View File

@ -15,9 +15,9 @@ import analysis.helpers;
*/ */
class LabelVarNameCheck : BaseAnalyzer class LabelVarNameCheck : BaseAnalyzer
{ {
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
mixin ScopedVisit!Module; mixin ScopedVisit!Module;
@ -118,11 +118,11 @@ private:
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import std.stdio : stderr; import std.stdio : stderr;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.label_var_same_name_check = true; sac.label_var_same_name_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
unittest unittest
{ {

View File

@ -13,10 +13,10 @@ class LambdaReturnCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName) this(string fileName, bool skipTests = false)
{ {
super(fileName, null); super(fileName, null, skipTests);
} }
override void visit(const FunctionLiteralExpression fLit) override void visit(const FunctionLiteralExpression fLit)
{ {
@ -47,11 +47,11 @@ private:
unittest unittest
{ {
import analysis.helpers : assertAnalyzerWarnings; import analysis.helpers : assertAnalyzerWarnings;
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import std.stdio : stderr; import std.stdio : stderr;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.lambda_return_check = true; sac.lambda_return_check = Check.enabled;
auto code = ` auto code = `
void main() void main()

View File

@ -20,9 +20,9 @@ class LengthSubtractionCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const AddExpression addExpression) override void visit(const AddExpression addExpression)
@ -58,10 +58,10 @@ class LengthSubtractionCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.length_subtraction_check = true; sac.length_subtraction_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testSizeT() void testSizeT()
{ {

View File

@ -15,9 +15,9 @@ import analysis.base : BaseAnalyzer;
class LineLengthCheck : BaseAnalyzer class LineLengthCheck : BaseAnalyzer
{ {
/// ///
this(string fileName, const(Token)[] tokens) this(string fileName, const(Token)[] tokens, bool skipTests = false)
{ {
super(fileName, null); super(fileName, null, skipTests);
this.tokens = tokens; this.tokens = tokens;
} }

View File

@ -23,9 +23,9 @@ class LocalImportCheck : BaseAnalyzer
/** /**
* Construct with the given file name. * Construct with the given file name.
*/ */
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
mixin visitThing!StructBody; mixin visitThing!StructBody;
@ -84,10 +84,10 @@ private:
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.local_import_check = true; sac.local_import_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testLocalImport() void testLocalImport()
{ {

View File

@ -25,9 +25,9 @@ class LogicPrecedenceCheck : BaseAnalyzer
enum string KEY = "dscanner.confusing.logical_precedence"; enum string KEY = "dscanner.confusing.logical_precedence";
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const OrOrExpression orOr) override void visit(const OrOrExpression orOr)
@ -48,10 +48,10 @@ class LogicPrecedenceCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.logical_precedence_check = true; sac.logical_precedence_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testFish() void testFish()
{ {

View File

@ -11,9 +11,9 @@ import dsymbol.builtin.names;
final class MismatchedArgumentCheck : BaseAnalyzer final class MismatchedArgumentCheck : BaseAnalyzer
{ {
/// ///
this(string fileName, const Scope* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const FunctionCallExpression fce) override void visit(const FunctionCallExpression fce)

View File

@ -24,9 +24,9 @@ public:
/** /**
* Constructs the style checker with the given file name. * Constructs the style checker with the given file name.
*/ */
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const Token t) override void visit(const Token t)
@ -49,10 +49,10 @@ private:
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.number_style_check = true; sac.number_style_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testNumbers() void testNumbers()
{ {

View File

@ -21,9 +21,9 @@ class ObjectConstCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
mixin visitTemplate!ClassDeclaration; mixin visitTemplate!ClassDeclaration;
@ -71,10 +71,10 @@ class ObjectConstCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.object_const_check = true; sac.object_const_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testConsts() void testConsts()
{ {

View File

@ -20,9 +20,9 @@ class OpEqualsWithoutToHashCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const ClassDeclaration node) override void visit(const ClassDeclaration node)
@ -89,10 +89,10 @@ class OpEqualsWithoutToHashCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.opequals_tohash_check = true; sac.opequals_tohash_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
// Success because it has opEquals and toHash // Success because it has opEquals and toHash
class Chimp class Chimp

View File

@ -30,9 +30,9 @@ class PokemonExceptionCheck : BaseAnalyzer
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const LastCatch lc) override void visit(const LastCatch lc)
@ -85,10 +85,10 @@ class PokemonExceptionCheck : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.exception_check = true; sac.exception_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testCatch() void testCatch()
{ {

View File

@ -27,9 +27,9 @@ class BackwardsRangeCheck : BaseAnalyzer
* Params: * Params:
* fileName = the name of the file being analyzed * fileName = the name of the file being analyzed
*/ */
this(string fileName, const(Scope)* sc) this(string fileName, const Scope* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const ForeachStatement foreachStatement) override void visit(const ForeachStatement foreachStatement)
@ -157,10 +157,10 @@ private:
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.backwards_range_check = true; sac.backwards_range_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void testRange() void testRange()
{ {

View File

@ -18,9 +18,9 @@ class RedundantParenCheck : BaseAnalyzer
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
/// ///
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const IfStatement statement) override void visit(const IfStatement statement)

View File

@ -205,8 +205,10 @@ MessageSet analyze(string fileName, const Module m, const StaticAnalysisConfig a
return null; return null;
auto symbolAllocator = scoped!ASTAllocator(); auto symbolAllocator = scoped!ASTAllocator();
version (unittest) enum ut = true; else enum ut = false;
auto first = scoped!FirstPass(m, internString(fileName), symbolAllocator, auto first = scoped!FirstPass(m, internString(fileName), symbolAllocator,
symbolAllocator, true, &moduleCache, null); symbolAllocator, true, &moduleCache, null);
first.run(); first.run();
secondPass(first.rootSymbol, first.moduleScope, moduleCache); secondPass(first.rootSymbol, first.moduleScope, moduleCache);
@ -216,75 +218,142 @@ MessageSet analyze(string fileName, const Module m, const StaticAnalysisConfig a
scope(exit) typeid(Scope).destroy(first.moduleScope); scope(exit) typeid(Scope).destroy(first.moduleScope);
BaseAnalyzer[] checks; BaseAnalyzer[] checks;
if (analysisConfig.asm_style_check) if (analysisConfig.asm_style_check != Check.disabled)
checks ~= new AsmStyleCheck(fileName, moduleScope); checks ~= new AsmStyleCheck(fileName, moduleScope,
if (analysisConfig.backwards_range_check) analysisConfig.asm_style_check == Check.skipTests && !ut);
checks ~= new BackwardsRangeCheck(fileName, moduleScope);
if (analysisConfig.builtin_property_names_check) if (analysisConfig.backwards_range_check != Check.disabled)
checks ~= new BuiltinPropertyNameCheck(fileName, moduleScope); checks ~= new BackwardsRangeCheck(fileName, moduleScope,
if (analysisConfig.comma_expression_check) analysisConfig.backwards_range_check == Check.skipTests && !ut);
checks ~= new CommaExpressionCheck(fileName, moduleScope);
if (analysisConfig.constructor_check) if (analysisConfig.builtin_property_names_check != Check.disabled)
checks ~= new ConstructorCheck(fileName, moduleScope); checks ~= new BuiltinPropertyNameCheck(fileName, moduleScope,
if (analysisConfig.could_be_immutable_check) analysisConfig.builtin_property_names_check == Check.skipTests && !ut);
checks ~= new UnmodifiedFinder(fileName, moduleScope);
if (analysisConfig.delete_check) if (analysisConfig.comma_expression_check != Check.disabled)
checks ~= new DeleteCheck(fileName, moduleScope); checks ~= new CommaExpressionCheck(fileName, moduleScope,
if (analysisConfig.duplicate_attribute) analysisConfig.comma_expression_check == Check.skipTests && !ut);
checks ~= new DuplicateAttributeCheck(fileName, moduleScope);
if (analysisConfig.enum_array_literal_check) if (analysisConfig.constructor_check != Check.disabled)
checks ~= new EnumArrayLiteralCheck(fileName, moduleScope); checks ~= new ConstructorCheck(fileName, moduleScope,
if (analysisConfig.exception_check) analysisConfig.constructor_check == Check.skipTests && !ut);
checks ~= new PokemonExceptionCheck(fileName, moduleScope);
if (analysisConfig.float_operator_check) if (analysisConfig.could_be_immutable_check != Check.disabled)
checks ~= new FloatOperatorCheck(fileName, moduleScope); checks ~= new UnmodifiedFinder(fileName, moduleScope,
if (analysisConfig.function_attribute_check) analysisConfig.could_be_immutable_check == Check.skipTests && !ut);
checks ~= new FunctionAttributeCheck(fileName, moduleScope);
if (analysisConfig.if_else_same_check) if (analysisConfig.delete_check != Check.disabled)
checks ~= new IfElseSameCheck(fileName, moduleScope); checks ~= new DeleteCheck(fileName, moduleScope,
if (analysisConfig.label_var_same_name_check) analysisConfig.delete_check == Check.skipTests && !ut);
checks ~= new LabelVarNameCheck(fileName, moduleScope);
if (analysisConfig.length_subtraction_check) if (analysisConfig.duplicate_attribute != Check.disabled)
checks ~= new LengthSubtractionCheck(fileName, moduleScope); checks ~= new DuplicateAttributeCheck(fileName, moduleScope,
if (analysisConfig.local_import_check) analysisConfig.duplicate_attribute == Check.skipTests && !ut);
checks ~= new LocalImportCheck(fileName, moduleScope);
if (analysisConfig.logical_precedence_check) if (analysisConfig.enum_array_literal_check != Check.disabled)
checks ~= new LogicPrecedenceCheck(fileName, moduleScope); checks ~= new EnumArrayLiteralCheck(fileName, moduleScope,
if (analysisConfig.mismatched_args_check) analysisConfig.enum_array_literal_check == Check.skipTests && !ut);
checks ~= new MismatchedArgumentCheck(fileName, moduleScope);
if (analysisConfig.number_style_check) if (analysisConfig.exception_check != Check.disabled)
checks ~= new NumberStyleCheck(fileName, moduleScope); checks ~= new PokemonExceptionCheck(fileName, moduleScope,
if (analysisConfig.object_const_check) analysisConfig.exception_check == Check.skipTests && !ut);
checks ~= new ObjectConstCheck(fileName, moduleScope);
if (analysisConfig.opequals_tohash_check) if (analysisConfig.float_operator_check != Check.disabled)
checks ~= new OpEqualsWithoutToHashCheck(fileName, moduleScope); checks ~= new FloatOperatorCheck(fileName, moduleScope,
if (analysisConfig.redundant_parens_check) analysisConfig.float_operator_check == Check.skipTests && !ut);
checks ~= new RedundantParenCheck(fileName, moduleScope);
if (analysisConfig.style_check) if (analysisConfig.function_attribute_check != Check.disabled)
checks ~= new StyleChecker(fileName, moduleScope); checks ~= new FunctionAttributeCheck(fileName, moduleScope,
if (analysisConfig.undocumented_declaration_check) analysisConfig.function_attribute_check == Check.skipTests && !ut);
checks ~= new UndocumentedDeclarationCheck(fileName, moduleScope);
if (analysisConfig.unused_label_check) if (analysisConfig.if_else_same_check != Check.disabled)
checks ~= new UnusedLabelCheck(fileName, moduleScope); checks ~= new IfElseSameCheck(fileName, moduleScope,
if (analysisConfig.unused_variable_check) analysisConfig.if_else_same_check == Check.skipTests&& !ut);
checks ~= new UnusedVariableCheck(fileName, moduleScope);
if (analysisConfig.long_line_check) if (analysisConfig.label_var_same_name_check != Check.disabled)
checks ~= new LineLengthCheck(fileName, tokens); checks ~= new LabelVarNameCheck(fileName, moduleScope,
if (analysisConfig.auto_ref_assignment_check) analysisConfig.label_var_same_name_check == Check.skipTests && !ut);
checks ~= new AutoRefAssignmentCheck(fileName);
if (analysisConfig.incorrect_infinite_range_check) if (analysisConfig.length_subtraction_check != Check.disabled)
checks ~= new IncorrectInfiniteRangeCheck(fileName); checks ~= new LengthSubtractionCheck(fileName, moduleScope,
if (analysisConfig.useless_assert_check) analysisConfig.length_subtraction_check == Check.skipTests && !ut);
checks ~= new UselessAssertCheck(fileName);
if (analysisConfig.alias_syntax_check) if (analysisConfig.local_import_check != Check.disabled)
checks ~= new AliasSyntaxCheck(fileName); checks ~= new LocalImportCheck(fileName, moduleScope,
if (analysisConfig.static_if_else_check) analysisConfig.local_import_check == Check.skipTests && !ut);
checks ~= new StaticIfElse(fileName);
if (analysisConfig.lambda_return_check) if (analysisConfig.logical_precedence_check != Check.disabled)
checks ~= new LambdaReturnCheck(fileName); checks ~= new LogicPrecedenceCheck(fileName, moduleScope,
analysisConfig.logical_precedence_check == Check.skipTests && !ut);
if (analysisConfig.mismatched_args_check != Check.disabled)
checks ~= new MismatchedArgumentCheck(fileName, moduleScope,
analysisConfig.mismatched_args_check == Check.skipTests && !ut);
if (analysisConfig.number_style_check != Check.disabled)
checks ~= new NumberStyleCheck(fileName, moduleScope,
analysisConfig.number_style_check == Check.skipTests && !ut);
if (analysisConfig.object_const_check != Check.disabled)
checks ~= new ObjectConstCheck(fileName, moduleScope,
analysisConfig.object_const_check == Check.skipTests && !ut);
if (analysisConfig.opequals_tohash_check != Check.disabled)
checks ~= new OpEqualsWithoutToHashCheck(fileName, moduleScope,
analysisConfig.opequals_tohash_check == Check.skipTests && !ut);
if (analysisConfig.redundant_parens_check != Check.disabled)
checks ~= new RedundantParenCheck(fileName, moduleScope,
analysisConfig.redundant_parens_check == Check.skipTests && !ut);
if (analysisConfig.style_check != Check.disabled)
checks ~= new StyleChecker(fileName, moduleScope,
analysisConfig.style_check == Check.skipTests && !ut);
if (analysisConfig.undocumented_declaration_check != Check.disabled)
checks ~= new UndocumentedDeclarationCheck(fileName, moduleScope,
analysisConfig.undocumented_declaration_check == Check.skipTests && !ut);
if (analysisConfig.unused_label_check != Check.disabled)
checks ~= new UnusedLabelCheck(fileName, moduleScope,
analysisConfig.unused_label_check == Check.skipTests && !ut);
if (analysisConfig.unused_variable_check != Check.disabled)
checks ~= new UnusedVariableCheck(fileName, moduleScope,
analysisConfig.unused_variable_check == Check.skipTests && !ut);
if (analysisConfig.long_line_check != Check.disabled)
checks ~= new LineLengthCheck(fileName, tokens,
analysisConfig.long_line_check == Check.skipTests && !ut);
if (analysisConfig.auto_ref_assignment_check != Check.disabled)
checks ~= new AutoRefAssignmentCheck(fileName,
analysisConfig.auto_ref_assignment_check == Check.skipTests && !ut);
if (analysisConfig.incorrect_infinite_range_check != Check.disabled)
checks ~= new IncorrectInfiniteRangeCheck(fileName,
analysisConfig.incorrect_infinite_range_check == Check.skipTests && !ut);
if (analysisConfig.useless_assert_check != Check.disabled)
checks ~= new UselessAssertCheck(fileName,
analysisConfig.useless_assert_check == Check.skipTests && !ut);
if (analysisConfig.alias_syntax_check != Check.disabled)
checks ~= new AliasSyntaxCheck(fileName,
analysisConfig.alias_syntax_check == Check.skipTests && !ut);
if (analysisConfig.static_if_else_check != Check.disabled)
checks ~= new StaticIfElse(fileName,
analysisConfig.static_if_else_check == Check.skipTests && !ut);
if (analysisConfig.lambda_return_check != Check.disabled)
checks ~= new LambdaReturnCheck(fileName,
analysisConfig.lambda_return_check == Check.skipTests && !ut);
version (none) version (none)
if (analysisConfig.redundant_if_check) if (analysisConfig.redundant_if_check != Check.disabled)
checks ~= new IfStatementCheck(fileName, moduleScope); checks ~= new IfStatementCheck(fileName, moduleScope,
analysisConfig.redundant_if_check == Check.skipTests && !ut);
foreach (check; checks) foreach (check; checks)
{ {
@ -297,3 +366,4 @@ MessageSet analyze(string fileName, const Module m, const StaticAnalysisConfig a
set.insert(message); set.insert(message);
return set; return set;
} }

View File

@ -25,9 +25,9 @@ class StaticIfElse : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string filename) this(string fileName, bool skipTests = false)
{ {
super(filename, null); super(fileName, null, skipTests);
} }
override void visit(const ConditionalStatement cc) override void visit(const ConditionalStatement cc)
@ -66,11 +66,11 @@ class StaticIfElse : BaseAnalyzer
unittest unittest
{ {
import analysis.helpers : assertAnalyzerWarnings; import analysis.helpers : assertAnalyzerWarnings;
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import std.stdio : stderr; import std.stdio : stderr;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.static_if_else_check = true; sac.static_if_else_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
void foo() { void foo() {
static if (false) static if (false)

View File

@ -25,9 +25,9 @@ class StyleChecker : BaseAnalyzer
enum string moduleNameRegex = `^[\p{Ll}_\d]+$`; enum string moduleNameRegex = `^[\p{Ll}_\d]+$`;
enum string KEY = "dscanner.style.phobos_naming_convention"; enum string KEY = "dscanner.style.phobos_naming_convention";
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const ModuleDeclaration dec) override void visit(const ModuleDeclaration dec)
@ -93,10 +93,10 @@ class StyleChecker : BaseAnalyzer
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.style_check = true; sac.style_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
module AMODULE; // [warn]: Module/package name 'AMODULE' does not match style guidelines. module AMODULE; // [warn]: Module/package name 'AMODULE' does not match style guidelines.

View File

@ -21,9 +21,9 @@ class UndocumentedDeclarationCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const Module mod) override void visit(const Module mod)

View File

@ -18,9 +18,9 @@ class UnmodifiedFinder : BaseAnalyzer
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
/// ///
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const Module mod) override void visit(const Module mod)

View File

@ -22,9 +22,9 @@ class UnusedVariableCheck : BaseAnalyzer
* Params: * Params:
* fileName = the name of the file being analyzed * fileName = the name of the file being analyzed
*/ */
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
re = regex("[\\p{Alphabetic}_][\\w_]*"); re = regex("[\\p{Alphabetic}_][\\w_]*");
} }
@ -410,11 +410,11 @@ private:
unittest unittest
{ {
import std.stdio : stderr; import std.stdio : stderr;
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import analysis.helpers : assertAnalyzerWarnings; import analysis.helpers : assertAnalyzerWarnings;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.unused_variable_check = true; sac.unused_variable_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
// Issue 274 // Issue 274

View File

@ -14,9 +14,9 @@ class UnusedLabelCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc) this(string fileName, const(Scope)* sc, bool skipTests = false)
{ {
super(fileName, sc); super(fileName, sc, skipTests);
} }
override void visit(const Module mod) override void visit(const Module mod)
@ -137,11 +137,11 @@ private:
unittest unittest
{ {
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import std.stdio : stderr; import std.stdio : stderr;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.unused_label_check = true; sac.unused_label_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
int testUnusedLabel() int testUnusedLabel()
{ {

View File

@ -19,11 +19,12 @@ class UselessAssertCheck : BaseAnalyzer
{ {
alias visit = BaseAnalyzer.visit; alias visit = BaseAnalyzer.visit;
/// ///
this(string fileName) this(string fileName, bool skipTests = false)
{ {
super(fileName, null); // assertions likely to be in unittest so never skip
} super(fileName, null, false);
}
override void visit(const AssertExpression ae) override void visit(const AssertExpression ae)
{ {
@ -96,11 +97,11 @@ private:
unittest unittest
{ {
import std.stdio : stderr; import std.stdio : stderr;
import analysis.config : StaticAnalysisConfig; import analysis.config : StaticAnalysisConfig, Check;
import std.format : format; import std.format : format;
StaticAnalysisConfig sac; StaticAnalysisConfig sac;
sac.useless_assert_check = true; sac.useless_assert_check = Check.enabled;
assertAnalyzerWarnings(q{ assertAnalyzerWarnings(q{
unittest unittest
{ {

View File

@ -59,6 +59,7 @@ else
bool styleCheck; bool styleCheck;
bool defaultConfig; bool defaultConfig;
bool report; bool report;
bool skipTests;
string symbolName; string symbolName;
string configLocation; string configLocation;
string[] importPaths; string[] importPaths;
@ -89,7 +90,8 @@ else
"I", &importPaths, "I", &importPaths,
"version", &printVersion, "version", &printVersion,
"muffinButton", &muffin, "muffinButton", &muffin,
"explore", &explore); "explore", &explore,
"skipTests", &skipTests);
//dfmt on //dfmt on
} }
catch (ConvException e) catch (ConvException e)
@ -218,6 +220,8 @@ else
string s = configLocation is null ? getConfigurationLocation() : configLocation; string s = configLocation is null ? getConfigurationLocation() : configLocation;
if (s.exists()) if (s.exists())
readINIFile(config, s); readINIFile(config, s);
if (skipTests)
config.fillConfig!(Check.skipTests);
if (report) if (report)
generateReport(expandArgs(args), config, cache, moduleCache); generateReport(expandArgs(args), config, cache, moduleCache);
else else
@ -368,7 +372,7 @@ Options:
If no files are specified, input is read from stdin. %1$s will exit with If no files are specified, input is read from stdin. %1$s will exit with
a status code of zero if no errors are found, 1 otherwise. a status code of zero if no errors are found, 1 otherwise.
--styleCheck <file | directory>..., <file | directory>... --styleCheck|S <file | directory>..., <file | directory>...
Lexes and parses sourceFiles, printing the line and column number of any Lexes and parses sourceFiles, printing the line and column number of any
static analysis check failures stdout. %1$s will exit with a status code static analysis check failures stdout. %1$s will exit with a status code
of zero if no warnings or errors are found, 1 otherwise. of zero if no warnings or errors are found, 1 otherwise.
@ -406,8 +410,12 @@ Options:
$HOME/.config/dscanner/dscanner.ini $HOME/.config/dscanner/dscanner.ini
--defaultConfig --defaultConfig
Generates a default configuration file for the static analysis checks`, Generates a default configuration file for the static analysis checks,
programName);
--skipTests
Does not analyze in the unittests. Only works if --styleCheck.`,
programName);
} }
private void doNothing(string, size_t, size_t, string, bool) private void doNothing(string, size_t, size_t, string, bool)