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 final 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 	mixin AnalyzerInfo!"exception_check";
31 
32 	alias visit = BaseAnalyzer.visit;
33 
34 	this(string fileName, const(Scope)* sc, bool skipTests = false)
35 	{
36 		super(fileName, sc, skipTests);
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 	override void visit(const Type2 type2)
57 	{
58 		if (ignoreType)
59 			return;
60 
61 		if (type2.type !is null)
62 		{
63 			type2.type.accept(this);
64 			return;
65 		}
66 
67 		if (type2.typeIdentifierPart.typeIdentifierPart !is null)
68 		{
69 			return;
70 		}
71 		const identOrTemplate = type2.typeIdentifierPart.identifierOrTemplateInstance;
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 dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
89 
90 	StaticAnalysisConfig sac = disabledConfig();
91 	sac.exception_check = Check.enabled;
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 }