1 // Distributed under the Boost Software License, Version 1.0. 2 // (See accompanying file LICENSE_1_0.txt or copy at 3 // http://www.boost.org/LICENSE_1_0.txt) 4 5 module dscanner.analysis.explicitly_annotated_unittests; 6 7 import dparse.lexer; 8 import dparse.ast; 9 import dscanner.analysis.base; 10 11 import std.stdio; 12 13 /** 14 * Requires unittests to be explicitly annotated with either @safe or @system 15 */ 16 final class ExplicitlyAnnotatedUnittestCheck : BaseAnalyzer 17 { 18 enum string KEY = "dscanner.style.explicitly_annotated_unittest"; 19 enum string MESSAGE = "A unittest should be annotated with at least @safe or @system"; 20 mixin AnalyzerInfo!"explicitly_annotated_unittests"; 21 22 /// 23 this(string fileName, bool skipTests = false) 24 { 25 super(fileName, null, skipTests); 26 } 27 28 override void visit(const Declaration decl) 29 { 30 if (decl.unittest_ !is null) 31 { 32 bool isSafeOrSystem; 33 if (decl.attributes !is null) 34 foreach (attribute; decl.attributes) 35 { 36 if (attribute.atAttribute !is null) 37 { 38 const token = attribute.atAttribute.identifier.text; 39 if (token == "safe" || token == "system") 40 { 41 isSafeOrSystem = true; 42 break; 43 } 44 } 45 } 46 if (!isSafeOrSystem) 47 addErrorMessage(decl.unittest_.line, decl.unittest_.column, KEY, MESSAGE); 48 } 49 decl.accept(this); 50 } 51 52 alias visit = BaseAnalyzer.visit; 53 54 } 55 56 unittest 57 { 58 import std.stdio : stderr; 59 import std.format : format; 60 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 61 import dscanner.analysis.helpers : assertAnalyzerWarnings; 62 63 StaticAnalysisConfig sac = disabledConfig(); 64 sac.explicitly_annotated_unittests = Check.enabled; 65 66 assertAnalyzerWarnings(q{ 67 @safe unittest {} 68 @system unittest {} 69 pure nothrow @system @nogc unittest {} 70 71 unittest {} // [warn]: %s 72 pure nothrow @nogc unittest {} // [warn]: %s 73 }}.format( 74 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 75 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 76 ), sac); 77 78 // nested 79 assertAnalyzerWarnings(q{ 80 struct Foo 81 { 82 @safe unittest {} 83 @system unittest {} 84 85 unittest {} // [warn]: %s 86 pure nothrow @nogc unittest {} // [warn]: %s 87 } 88 }}.format( 89 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 90 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 91 ), sac); 92 93 stderr.writeln("Unittest for ExplicitlyAnnotatedUnittestCheck passed."); 94 }