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 std.d.ast;
10 import std.d.lexer;
11 import analysis.base;
12 import analysis.helpers;
13 
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)
34 	{
35 		super(fileName);
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 
56 	override void visit(const Type2 type2)
57 	{
58 		if (ignoreType) 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.identifiersOrTemplateInstances[0];
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 analysis.config : StaticAnalysisConfig;
88 
89 	StaticAnalysisConfig sac;
90 	sac.exception_check = true;
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 }
131