167 lines
4.1 KiB
D
167 lines
4.1 KiB
D
// 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 dscanner.analysis.assert_without_msg;
|
|
|
|
import dscanner.analysis.base;
|
|
import dscanner.utils : safeAccess;
|
|
import dsymbol.scope_ : Scope;
|
|
import dparse.lexer;
|
|
import dparse.ast;
|
|
|
|
import std.stdio;
|
|
import std.algorithm;
|
|
|
|
/**
|
|
* Check that all asserts have an explanatory message.
|
|
*/
|
|
final class AssertWithoutMessageCheck : BaseAnalyzer
|
|
{
|
|
enum string KEY = "dscanner.style.assert_without_msg";
|
|
enum string MESSAGE = "An assert should have an explanatory message";
|
|
mixin AnalyzerInfo!"assert_without_msg";
|
|
|
|
///
|
|
this(string fileName, const(Scope)* sc, bool skipTests = false)
|
|
{
|
|
super(fileName, sc, skipTests);
|
|
}
|
|
|
|
override void visit(const AssertExpression expr)
|
|
{
|
|
static if (__traits(hasMember, expr.assertArguments, "messageParts"))
|
|
{
|
|
// libdparse 0.22.0+
|
|
bool hasMessage = expr.assertArguments
|
|
&& expr.assertArguments.messageParts.length > 0;
|
|
}
|
|
else
|
|
bool hasMessage = expr.assertArguments
|
|
&& expr.assertArguments.message !is null;
|
|
|
|
if (!hasMessage)
|
|
addErrorMessage(expr.line, expr.column, KEY, MESSAGE);
|
|
}
|
|
|
|
override void visit(const FunctionCallExpression expr)
|
|
{
|
|
if (!isStdExceptionImported)
|
|
return;
|
|
|
|
if (const IdentifierOrTemplateInstance iot = safeAccess(expr)
|
|
.unaryExpression.primaryExpression.identifierOrTemplateInstance)
|
|
{
|
|
auto ident = iot.identifier;
|
|
if (ident.text == "enforce" && expr.arguments !is null && expr.arguments.namedArgumentList !is null &&
|
|
expr.arguments.namedArgumentList.items.length < 2)
|
|
addErrorMessage(ident.line, ident.column, KEY, MESSAGE);
|
|
}
|
|
}
|
|
|
|
override void visit(const SingleImport sImport)
|
|
{
|
|
static immutable stdException = ["std", "exception"];
|
|
if (sImport.identifierChain.identifiers.map!(a => a.text).equal(stdException))
|
|
isStdExceptionImported = true;
|
|
}
|
|
|
|
// revert the stack after new scopes
|
|
override void visit(const Declaration decl)
|
|
{
|
|
// be careful - ImportDeclarations don't introduce a new scope
|
|
if (decl.importDeclaration is null)
|
|
{
|
|
bool tmp = isStdExceptionImported;
|
|
scope(exit) isStdExceptionImported = tmp;
|
|
decl.accept(this);
|
|
}
|
|
else
|
|
decl.accept(this);
|
|
}
|
|
|
|
mixin ScopedVisit!IfStatement;
|
|
mixin ScopedVisit!BlockStatement;
|
|
|
|
alias visit = BaseAnalyzer.visit;
|
|
|
|
private:
|
|
bool isStdExceptionImported;
|
|
|
|
template ScopedVisit(NodeType)
|
|
{
|
|
override void visit(const NodeType n)
|
|
{
|
|
bool tmp = isStdExceptionImported;
|
|
scope(exit) isStdExceptionImported = tmp;
|
|
n.accept(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.stdio : stderr;
|
|
import std.format : format;
|
|
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
|
|
import dscanner.analysis.helpers : assertAnalyzerWarnings;
|
|
|
|
StaticAnalysisConfig sac = disabledConfig();
|
|
sac.assert_without_msg = Check.enabled;
|
|
|
|
assertAnalyzerWarnings(q{
|
|
unittest {
|
|
assert(0, "foo bar");
|
|
assert(0); // [warn]: %s
|
|
}
|
|
}c.format(
|
|
AssertWithoutMessageCheck.MESSAGE,
|
|
), sac);
|
|
|
|
assertAnalyzerWarnings(q{
|
|
unittest {
|
|
static assert(0, "foo bar");
|
|
static assert(0); // [warn]: %s
|
|
}
|
|
}c.format(
|
|
AssertWithoutMessageCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// check for std.exception.enforce
|
|
assertAnalyzerWarnings(q{
|
|
unittest {
|
|
enforce(0); // std.exception not imported yet - could be a user-defined symbol
|
|
import std.exception;
|
|
enforce(0, "foo bar");
|
|
enforce(0); // [warn]: %s
|
|
}
|
|
}c.format(
|
|
AssertWithoutMessageCheck.MESSAGE,
|
|
), sac);
|
|
|
|
// check for std.exception.enforce
|
|
assertAnalyzerWarnings(q{
|
|
unittest {
|
|
import exception;
|
|
class C {
|
|
import std.exception;
|
|
}
|
|
enforce(0); // std.exception not imported yet - could be a user-defined symbol
|
|
struct S {
|
|
import std.exception;
|
|
}
|
|
enforce(0); // std.exception not imported yet - could be a user-defined symbol
|
|
if (false) {
|
|
import std.exception;
|
|
}
|
|
enforce(0); // std.exception not imported yet - could be a user-defined symbol
|
|
{
|
|
import std.exception;
|
|
}
|
|
enforce(0); // std.exception not imported yet - could be a user-defined symbol
|
|
}
|
|
}c, sac);
|
|
|
|
stderr.writeln("Unittest for AssertWithoutMessageCheck passed.");
|
|
}
|