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