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 }