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 }