1 //          Copyright The dlang community - 2018
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dscanner.analysis.trust_too_much;
7 
8 import std.stdio;
9 import dparse.ast;
10 import dparse.lexer;
11 import dscanner.analysis.base;
12 import dsymbol.scope_;
13 
14 /**
15  * Checks that `@trusted` is only applied to a a single function
16  */
17 final class TrustTooMuchCheck : BaseAnalyzer
18 {
19 private:
20 
21 	static immutable MESSAGE = "Trusting a whole scope is a bad idea, " ~
22 		"`@trusted` should only be attached to the functions individually";
23 	static immutable string KEY = "dscanner.trust_too_much";
24 
25 	bool checkAtAttribute = true;
26 
27 public:
28 
29 	alias visit = BaseAnalyzer.visit;
30 
31 	///
32 	this(string fileName, bool skipTests = false)
33 	{
34 		super(fileName, sc, skipTests);
35 	}
36 
37 	override void visit(const AtAttribute d)
38 	{
39 		if (checkAtAttribute && d.identifier.text == "trusted")
40 		{
41 			const Token t = d.identifier;
42 			addErrorMessage(t.line, t.column, KEY, MESSAGE);
43 		}
44 		d.accept(this);
45 	}
46 
47 	// always applied to function body, so OK
48 	override void visit(const MemberFunctionAttribute d)
49 	{
50 		const oldCheckAtAttribute = checkAtAttribute;
51 		checkAtAttribute = false;
52 		d.accept(this);
53 		checkAtAttribute = oldCheckAtAttribute;
54 	}
55 
56 	// handles `@trusted{}` and old style, leading, atAttribute for single funcs
57 	override void visit(const Declaration d)
58 	{
59 		const oldCheckAtAttribute = checkAtAttribute;
60 
61 		checkAtAttribute = 	d.functionDeclaration is null && d.unittest_ is null &&
62 							d.constructor is null && d.destructor is null &&
63 							d.staticConstructor is null && d.staticDestructor is null &&
64 							d.sharedStaticConstructor is null && d.sharedStaticDestructor is null;
65 		d.accept(this);
66 		checkAtAttribute = oldCheckAtAttribute;
67 	}
68 
69 	// issue #588
70 	override void visit(const AliasDeclaration d)
71 	{
72 		const oldCheckAtAttribute = checkAtAttribute;
73 		checkAtAttribute = false;
74 		d.accept(this);
75 		checkAtAttribute = oldCheckAtAttribute;
76 	}
77 }
78 
79 unittest
80 {
81 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
82 	import dscanner.analysis.helpers : assertAnalyzerWarnings;
83 	import std.format : format;
84 
85 	StaticAnalysisConfig sac = disabledConfig();
86 	sac.trust_too_much = Check.enabled;
87 	const msg = TrustTooMuchCheck.MESSAGE;
88 
89 	//--- fail cases ---//
90 
91 	assertAnalyzerWarnings(q{
92 	@trusted: // [warn]: %s
93 		void test();
94 	}}.format(msg), sac);
95 
96 	assertAnalyzerWarnings(q{
97 	@trusted @nogc: // [warn]: %s
98 		void test();
99 	}}.format(msg), sac);
100 
101 	assertAnalyzerWarnings(q{
102 	@trusted { // [warn]: %s
103 		void test();
104 		void test();
105 	}
106 	}}.format(msg), sac);
107 
108 	assertAnalyzerWarnings(q{
109 	@safe {
110 		@trusted @nogc { // [warn]: %s
111 		void test();
112 		void test();
113 	}}
114 	}}.format(msg), sac);
115 
116 	assertAnalyzerWarnings(q{
117 	@nogc @trusted { // [warn]: %s
118 		void test();
119 		void test();
120 	}
121 	}}.format(msg), sac);
122 
123 	assertAnalyzerWarnings(q{
124 	@trusted template foo(){ // [warn]: %s
125 	}
126 	}}.format(msg), sac);
127 
128 	assertAnalyzerWarnings(q{
129 	struct foo{
130 	@trusted:  // [warn]: %s
131 	}
132 	}}.format(msg), sac);
133 	//--- pass cases ---//
134 
135 	assertAnalyzerWarnings(q{
136 	void test() @trusted {}
137 	}}, sac);
138 
139 	assertAnalyzerWarnings(q{
140 	@trusted void test();
141 	}}, sac);
142 
143 	assertAnalyzerWarnings(q{
144 	@nogc template foo(){
145 	}
146 	}} , sac);
147 
148 	assertAnalyzerWarnings(q{
149 	alias nothrow @trusted uint F4();
150 	}} , sac);
151 
152 	assertAnalyzerWarnings(q{
153 	@trusted ~this();
154 	@trusted this();
155 	}} , sac);
156 
157 	stderr.writeln("Unittest for TrustTooMuchCheck passed.");
158 }