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 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 		checkAtAttribute = d.functionDeclaration is null;
61 		d.accept(this);
62 		checkAtAttribute = oldCheckAtAttribute;
63 	}
64 
65 	// issue #588
66 	override void visit(const AliasDeclaration d)
67 	{
68 		const oldCheckAtAttribute = checkAtAttribute;
69 		checkAtAttribute = false;
70 		d.accept(this);
71 		checkAtAttribute = oldCheckAtAttribute;
72 	}
73 }
74 
75 unittest
76 {
77 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
78 	import dscanner.analysis.helpers : assertAnalyzerWarnings;
79 	import std.format : format;
80 
81 	StaticAnalysisConfig sac = disabledConfig();
82 	sac.trust_too_much = Check.enabled;
83 	const msg = TrustTooMuchCheck.MESSAGE;
84 
85 	//--- fail cases ---//
86 
87 	assertAnalyzerWarnings(q{
88 	@trusted: // [warn]: %s
89 		void test();
90 	}c.format(msg), sac);
91 
92 	assertAnalyzerWarnings(q{
93 	@trusted @nogc: // [warn]: %s
94 		void test();
95 	}c.format(msg), sac);
96 
97 	assertAnalyzerWarnings(q{
98 	@trusted { // [warn]: %s
99 		void test();
100 		void test();
101 	}
102 	}c.format(msg), sac);
103 
104 	assertAnalyzerWarnings(q{
105 	@safe {
106 		@trusted @nogc { // [warn]: %s
107 		void test();
108 		void test();
109 	}}
110 	}c.format(msg), sac);
111 
112 	assertAnalyzerWarnings(q{
113 	@nogc @trusted { // [warn]: %s
114 		void test();
115 		void test();
116 	}
117 	}c.format(msg), sac);
118 
119 	assertAnalyzerWarnings(q{
120 	@trusted template foo(){ // [warn]: %s
121 	}
122 	}c.format(msg), sac);
123 
124 	assertAnalyzerWarnings(q{
125 	struct foo{
126 	@trusted:  // [warn]: %s
127 	}
128 	}c.format(msg), sac);
129 	//--- pass cases ---//
130 
131 	assertAnalyzerWarnings(q{
132 	void test() @trusted {}
133 	}c, sac);
134 
135 	assertAnalyzerWarnings(q{
136 	@trusted void test();
137 	}c, sac);
138 
139 	assertAnalyzerWarnings(q{
140 	@nogc template foo(){
141 	}
142 	}c , sac);
143 
144 	assertAnalyzerWarnings(q{
145 	alias nothrow @trusted uint F4();
146 	}c , sac);
147 
148 	stderr.writeln("Unittest for TrustTooMuchCheck passed.");
149 }