1 // Copyright Brian Schott (Hackerpilot) 2014. 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.pokemon; 7 8 import std.stdio; 9 import dparse.ast; 10 import dparse.lexer; 11 import dscanner.analysis.base; 12 import dscanner.analysis.helpers; 13 import dsymbol.scope_ : Scope; 14 15 /** 16 * Checks for Pokémon exception handling, i.e. "gotta' catch 'em all". 17 * 18 * --- 19 * try { 20 * choose(pikachu); 21 * } catch (Throwable e) { 22 * ... 23 * } 24 * --- 25 */ 26 final class PokemonExceptionCheck : BaseAnalyzer 27 { 28 enum MESSAGE = "Catching Error or Throwable is almost always a bad idea."; 29 enum string KEY = "dscanner.suspicious.catch_em_all"; 30 mixin AnalyzerInfo!"exception_check"; 31 32 alias visit = BaseAnalyzer.visit; 33 34 this(string fileName, const(Scope)* sc, bool skipTests = false) 35 { 36 super(fileName, sc, skipTests); 37 } 38 39 override void visit(const LastCatch lc) 40 { 41 addErrorMessage(lc.line, lc.column, KEY, MESSAGE); 42 lc.accept(this); 43 } 44 45 bool ignoreType = true; 46 47 override void visit(const Catch c) 48 { 49 ignoreType = false; 50 c.type.accept(this); 51 ignoreType = true; 52 53 c.accept(this); 54 } 55 56 override void visit(const Type2 type2) 57 { 58 if (ignoreType) 59 return; 60 61 if (type2.type !is null) 62 { 63 type2.type.accept(this); 64 return; 65 } 66 67 if (type2.typeIdentifierPart.typeIdentifierPart !is null) 68 { 69 return; 70 } 71 const identOrTemplate = type2.typeIdentifierPart.identifierOrTemplateInstance; 72 if (identOrTemplate.templateInstance !is null) 73 { 74 return; 75 } 76 if (identOrTemplate.identifier.text == "Throwable" 77 || identOrTemplate.identifier.text == "Error") 78 { 79 immutable column = identOrTemplate.identifier.column; 80 immutable line = identOrTemplate.identifier.line; 81 addErrorMessage(line, column, KEY, MESSAGE); 82 } 83 } 84 } 85 86 unittest 87 { 88 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 89 90 StaticAnalysisConfig sac = disabledConfig(); 91 sac.exception_check = Check.enabled; 92 assertAnalyzerWarnings(q{ 93 void testCatch() 94 { 95 try 96 { 97 // ... 98 } 99 catch (AssertError err) //ok 100 { 101 102 } 103 catch (Exception err) // ok 104 { 105 106 } 107 catch (shared(Exception) err) // ok 108 { 109 110 } 111 catch (Error err) // [warn]: Catching Error or Throwable is almost always a bad idea. 112 { 113 114 } 115 catch (Throwable err) // [warn]: Catching Error or Throwable is almost always a bad idea. 116 { 117 118 } 119 catch (shared(Error) err) // [warn]: Catching Error or Throwable is almost always a bad idea. 120 { 121 122 } 123 catch // [warn]: Catching Error or Throwable is almost always a bad idea. 124 { 125 126 } 127 } 128 }}, sac); 129 130 stderr.writeln("Unittest for PokemonExceptionCheck passed."); 131 }