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.builtin_property_names;
7 
8 import std.stdio;
9 import std.regex;
10 import dparse.ast;
11 import dparse.lexer;
12 import analysis.base;
13 import analysis.helpers;
14 import dsymbol.scope_;
15 
16 /**
17  * The following code should be killed with fire:
18  * ---
19  * class SomeClass
20  * {
21  * 	void init();
22  * 	int init;
23  * 	string mangleof = "LOL";
24  * 	auto init = 10;
25  * 	enum sizeof = 10;
26  * }
27  * ---
28  */
29 class BuiltinPropertyNameCheck : BaseAnalyzer
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 FunctionDeclaration fd)
39 	{
40 		if (depth > 0 && isBuiltinProperty(fd.name.text))
41 		{
42 			addErrorMessage(fd.name.line, fd.name.column, KEY, generateErrorMessage(fd.name.text));
43 		}
44 		fd.accept(this);
45 	}
46 
47 	override void visit(const FunctionBody functionBody)
48 	{
49 		immutable int d = depth;
50 		scope (exit)
51 			depth = d;
52 		depth = 0;
53 		functionBody.accept(this);
54 	}
55 
56 	override void visit(const AutoDeclaration ad)
57 	{
58 		if (depth > 0)
59 			foreach (i; ad.identifiers)
60 			{
61 				if (isBuiltinProperty(i.text))
62 					addErrorMessage(i.line, i.column, KEY, generateErrorMessage(i.text));
63 			}
64 	}
65 
66 	override void visit(const Declarator d)
67 	{
68 		if (depth > 0 && isBuiltinProperty(d.name.text))
69 			addErrorMessage(d.name.line, d.name.column, KEY, generateErrorMessage(d.name.text));
70 	}
71 
72 	override void visit(const StructBody sb)
73 	{
74 		depth++;
75 		sb.accept(this);
76 		depth--;
77 	}
78 
79 private:
80 
81 	enum string KEY = "dscanner.confusing.builtin_property_names";
82 
83 	string generateErrorMessage(string name)
84 	{
85 		import std.string : format;
86 
87 		return format("Avoid naming members '%s'. This can"
88 				~ " confuse code that depends on the '.%s' property of a type.", name, name);
89 	}
90 
91 	bool isBuiltinProperty(string name)
92 	{
93 		import std.algorithm : canFind;
94 
95 		return builtinProperties.canFind(name);
96 	}
97 
98 	enum string[] builtinProperties = ["init", "sizeof", "mangleof", "alignof", "stringof"];
99 	int depth;
100 }
101 
102 unittest
103 {
104 	import analysis.config : StaticAnalysisConfig;
105 
106 	StaticAnalysisConfig sac;
107 	sac.builtin_property_names_check = true;
108 	assertAnalyzerWarnings(q{
109 class SomeClass
110 {
111 	void init(); // [warn]: Avoid naming members 'init'. This can confuse code that depends on the '.init' property of a type.
112 	int init; // [warn]: Avoid naming members 'init'. This can confuse code that depends on the '.init' property of a type.
113 	auto init = 10; // [warn]: Avoid naming members 'init'. This can confuse code that depends on the '.init' property of a type.
114 }
115 	}}, sac);
116 
117 	stderr.writeln("Unittest for NumberStyleCheck passed.");
118 }