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 }