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 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 31 alias visit = BaseAnalyzer.visit; 32 33 this(string fileName, const(Scope)* sc, bool skipTests = false) 34 { 35 super(fileName, sc, skipTests); 36 } 37 38 override void visit(const LastCatch lc) 39 { 40 addErrorMessage(lc.line, lc.column, KEY, MESSAGE); 41 lc.accept(this); 42 } 43 44 bool ignoreType = true; 45 46 override void visit(const Catch c) 47 { 48 ignoreType = false; 49 c.type.accept(this); 50 ignoreType = true; 51 52 c.accept(this); 53 } 54 55 override void visit(const Type2 type2) 56 { 57 if (ignoreType) 58 return; 59 60 if (type2.type !is null) 61 { 62 type2.type.accept(this); 63 return; 64 } 65 66 if (type2.typeIdentifierPart.typeIdentifierPart !is null) 67 { 68 return; 69 } 70 const identOrTemplate = type2.typeIdentifierPart.identifierOrTemplateInstance; 71 if (identOrTemplate.templateInstance !is null) 72 { 73 return; 74 } 75 if (identOrTemplate.identifier.text == "Throwable" 76 || identOrTemplate.identifier.text == "Error") 77 { 78 immutable column = identOrTemplate.identifier.column; 79 immutable line = identOrTemplate.identifier.line; 80 addErrorMessage(line, column, KEY, MESSAGE); 81 } 82 } 83 } 84 85 unittest 86 { 87 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 88 89 StaticAnalysisConfig sac = disabledConfig(); 90 sac.exception_check = Check.enabled; 91 assertAnalyzerWarnings(q{ 92 void testCatch() 93 { 94 try 95 { 96 // ... 97 } 98 catch (AssertError err) //ok 99 { 100 101 } 102 catch (Exception err) // ok 103 { 104 105 } 106 catch (shared(Exception) err) // ok 107 { 108 109 } 110 catch (Error err) // [warn]: Catching Error or Throwable is almost always a bad idea. 111 { 112 113 } 114 catch (Throwable err) // [warn]: Catching Error or Throwable is almost always a bad idea. 115 { 116 117 } 118 catch (shared(Error) err) // [warn]: Catching Error or Throwable is almost always a bad idea. 119 { 120 121 } 122 catch // [warn]: Catching Error or Throwable is almost always a bad idea. 123 { 124 125 } 126 } 127 }}, sac); 128 129 stderr.writeln("Unittest for PokemonExceptionCheck passed."); 130 }