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 }