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 analysis.pokemon; 7 8 import std.stdio; 9 import dparse.ast; 10 import dparse.lexer; 11 import analysis.base; 12 import 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) 34 { 35 super(fileName, sc); 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.symbol.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1) 67 { 68 return; 69 } 70 auto identOrTemplate = type2.symbol.identifierOrTemplateChain 71 .identifiersOrTemplateInstances[0]; 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 analysis.config : StaticAnalysisConfig; 89 90 StaticAnalysisConfig sac; 91 sac.exception_check = true; 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 }