D-Scanner/src/dscanner/analysis/explicitly_annotated_unitte...

128 lines
3.0 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.explicitly_annotated_unittests;
import dparse.lexer;
import dparse.ast;
import dscanner.analysis.base;
import std.stdio;
/**
* Requires unittests to be explicitly annotated with either @safe or @system
*/
final class ExplicitlyAnnotatedUnittestCheck : BaseAnalyzer
{
enum string KEY = "dscanner.style.explicitly_annotated_unittest";
enum string MESSAGE = "A unittest should be annotated with at least @safe or @system";
mixin AnalyzerInfo!"explicitly_annotated_unittests";
///
this(string fileName, bool skipTests = false)
{
super(fileName, null, skipTests);
}
override void visit(const Declaration decl)
{
if (decl.unittest_ !is null)
{
bool isSafeOrSystem;
if (decl.attributes !is null)
foreach (attribute; decl.attributes)
{
if (attribute.atAttribute !is null)
{
const token = attribute.atAttribute.identifier.text;
if (token == "safe" || token == "system")
{
isSafeOrSystem = true;
break;
}
}
}
if (!isSafeOrSystem)
{
auto token = decl.unittest_.findTokenForDisplay(tok!"unittest");
addErrorMessage(token, KEY, MESSAGE,
[
AutoFix.insertionBefore(token[0], "@safe ", "Mark unittest @safe"),
AutoFix.insertionBefore(token[0], "@system ", "Mark unittest @system")
]);
}
}
decl.accept(this);
}
alias visit = BaseAnalyzer.visit;
}
unittest
{
import dscanner.analysis.config : Check, disabledConfig, StaticAnalysisConfig;
import dscanner.analysis.helpers : assertAnalyzerWarnings, assertAutoFix;
import std.format : format;
import std.stdio : stderr;
StaticAnalysisConfig sac = disabledConfig();
sac.explicitly_annotated_unittests = Check.enabled;
assertAnalyzerWarnings(q{
@safe unittest {}
@system unittest {}
pure nothrow @system @nogc unittest {}
unittest {} /+
^^^^^^^^ [warn]: %s +/
pure nothrow @nogc unittest {} /+
^^^^^^^^ [warn]: %s +/
}c.format(
ExplicitlyAnnotatedUnittestCheck.MESSAGE,
ExplicitlyAnnotatedUnittestCheck.MESSAGE,
), sac);
// nested
assertAnalyzerWarnings(q{
struct Foo
{
@safe unittest {}
@system unittest {}
unittest {} /+
^^^^^^^^ [warn]: %s +/
pure nothrow @nogc unittest {} /+
^^^^^^^^ [warn]: %s +/
}
}c.format(
ExplicitlyAnnotatedUnittestCheck.MESSAGE,
ExplicitlyAnnotatedUnittestCheck.MESSAGE,
), sac);
// nested
assertAutoFix(q{
unittest {} // fix:0
pure nothrow @nogc unittest {} // fix:0
struct Foo
{
unittest {} // fix:1
pure nothrow @nogc unittest {} // fix:1
}
}c, q{
@safe unittest {} // fix:0
pure nothrow @nogc @safe unittest {} // fix:0
struct Foo
{
@system unittest {} // fix:1
pure nothrow @nogc @system unittest {} // fix:1
}
}c, sac);
stderr.writeln("Unittest for ExplicitlyAnnotatedUnittestCheck passed.");
}