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 }