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 : BaseAnalyzer; 10 11 import std.stdio; 12 13 /** 14 * Requires unittests to be explicitly annotated with either @safe or @system 15 */ 16 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 21 /// 22 this(string fileName, bool skipTests = false) 23 { 24 super(fileName, null, skipTests); 25 } 26 27 override void visit(const Declaration decl) 28 { 29 if (decl.unittest_ !is null) 30 { 31 bool isSafeOrSystem; 32 if (decl.attributes !is null) 33 foreach (attribute; decl.attributes) 34 { 35 if (attribute.atAttribute !is null) 36 { 37 const token = attribute.atAttribute.identifier.text; 38 if (token == "safe" || token == "system") 39 { 40 isSafeOrSystem = true; 41 break; 42 } 43 } 44 } 45 if (!isSafeOrSystem) 46 addErrorMessage(decl.unittest_.line, decl.unittest_.column, KEY, MESSAGE); 47 } 48 decl.accept(this); 49 } 50 51 alias visit = BaseAnalyzer.visit; 52 53 } 54 55 unittest 56 { 57 import std.stdio : stderr; 58 import std.format : format; 59 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 60 import dscanner.analysis.helpers : assertAnalyzerWarnings; 61 62 StaticAnalysisConfig sac = disabledConfig(); 63 sac.explicitly_annotated_unittests = Check.enabled; 64 65 assertAnalyzerWarnings(q{ 66 @safe unittest {} 67 @system unittest {} 68 pure nothrow @system @nogc unittest {} 69 70 unittest {} // [warn]: %s 71 pure nothrow @nogc unittest {} // [warn]: %s 72 }c.format( 73 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 74 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 75 ), sac); 76 77 // nested 78 assertAnalyzerWarnings(q{ 79 struct Foo 80 { 81 @safe unittest {} 82 @system unittest {} 83 84 unittest {} // [warn]: %s 85 pure nothrow @nogc unittest {} // [warn]: %s 86 } 87 }c.format( 88 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 89 ExplicitlyAnnotatedUnittestCheck.MESSAGE, 90 ), sac); 91 92 stderr.writeln("Unittest for ExplicitlyAnnotatedUnittestCheck passed."); 93 }