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 	mixin AnalyzerInfo!"trust_too_much";
32 
33 	///
34 	this(string fileName, bool skipTests = false)
35 	{
36 		super(fileName, sc, skipTests);
37 	}
38 
39 	override void visit(const AtAttribute d)
40 	{
41 		if (checkAtAttribute && d.identifier.text == "trusted")
42 		{
43 			const Token t = d.identifier;
44 			addErrorMessage(t.line, t.column, KEY, MESSAGE);
45 		}
46 		d.accept(this);
47 	}
48 
49 	// always applied to function body, so OK
50 	override void visit(const MemberFunctionAttribute d)
51 	{
52 		const oldCheckAtAttribute = checkAtAttribute;
53 		checkAtAttribute = false;
54 		d.accept(this);
55 		checkAtAttribute = oldCheckAtAttribute;
56 	}
57 
58 	// handles `@trusted{}` and old style, leading, atAttribute for single funcs
59 	override void visit(const Declaration d)
60 	{
61 		const oldCheckAtAttribute = checkAtAttribute;
62 
63 		checkAtAttribute = 	d.functionDeclaration is null && d.unittest_ is null &&
64 							d.constructor is null && d.destructor is null &&
65 							d.staticConstructor is null && d.staticDestructor is null &&
66 							d.sharedStaticConstructor is null && d.sharedStaticDestructor is null;
67 		d.accept(this);
68 		checkAtAttribute = oldCheckAtAttribute;
69 	}
70 
71 	// issue #588
72 	override void visit(const AliasDeclaration d)
73 	{
74 		const oldCheckAtAttribute = checkAtAttribute;
75 		checkAtAttribute = false;
76 		d.accept(this);
77 		checkAtAttribute = oldCheckAtAttribute;
78 	}
79 }
80 
81 unittest
82 {
83 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
84 	import dscanner.analysis.helpers : assertAnalyzerWarnings;
85 	import std.format : format;
86 
87 	StaticAnalysisConfig sac = disabledConfig();
88 	sac.trust_too_much = Check.enabled;
89 	const msg = TrustTooMuchCheck.MESSAGE;
90 
91 	//--- fail cases ---//
92 
93 	assertAnalyzerWarnings(q{
94 	@trusted: // [warn]: %s
95 		void test();
96 	}}.format(msg), sac);
97 
98 	assertAnalyzerWarnings(q{
99 	@trusted @nogc: // [warn]: %s
100 		void test();
101 	}}.format(msg), sac);
102 
103 	assertAnalyzerWarnings(q{
104 	@trusted { // [warn]: %s
105 		void test();
106 		void test();
107 	}
108 	}}.format(msg), sac);
109 
110 	assertAnalyzerWarnings(q{
111 	@safe {
112 		@trusted @nogc { // [warn]: %s
113 		void test();
114 		void test();
115 	}}
116 	}}.format(msg), sac);
117 
118 	assertAnalyzerWarnings(q{
119 	@nogc @trusted { // [warn]: %s
120 		void test();
121 		void test();
122 	}
123 	}}.format(msg), sac);
124 
125 	assertAnalyzerWarnings(q{
126 	@trusted template foo(){ // [warn]: %s
127 	}
128 	}}.format(msg), sac);
129 
130 	assertAnalyzerWarnings(q{
131 	struct foo{
132 	@trusted:  // [warn]: %s
133 	}
134 	}}.format(msg), sac);
135 	//--- pass cases ---//
136 
137 	assertAnalyzerWarnings(q{
138 	void test() @trusted {}
139 	}}, sac);
140 
141 	assertAnalyzerWarnings(q{
142 	@trusted void test();
143 	}}, sac);
144 
145 	assertAnalyzerWarnings(q{
146 	@nogc template foo(){
147 	}
148 	}} , sac);
149 
150 	assertAnalyzerWarnings(q{
151 	alias nothrow @trusted uint F4();
152 	}} , sac);
153 
154 	assertAnalyzerWarnings(q{
155 	@trusted ~this();
156 	@trusted this();
157 	}} , sac);
158 
159 	stderr.writeln("Unittest for TrustTooMuchCheck passed.");
160 }