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 /** 17 * Checks for Pokémon exception handling, i.e. "gotta' catch 'em all". 18 * 19 * --- 20 * try { 21 * choose(pikachu); 22 * } catch (Throwable e) { 23 * ... 24 * } 25 * --- 26 */ 27 class PokemonExceptionCheck : BaseAnalyzer 28 { 29 enum MESSAGE = "Catching Error or Throwable is almost always a bad idea."; 30 enum string KEY = "dscanner.suspicious.catch_em_all"; 31 32 alias visit = BaseAnalyzer.visit; 33 34 this(string fileName, const(Scope)* sc) 35 { 36 super(fileName, sc); 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 57 override void visit(const Type2 type2) 58 { 59 if (ignoreType) return; 60 61 if (type2.type !is null) 62 { 63 type2.type.accept(this); 64 return; 65 } 66 67 if (type2.symbol.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1) 68 { 69 return; 70 } 71 auto identOrTemplate = type2.symbol.identifierOrTemplateChain.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 } 132