1 // Copyright (c) 2014, Matthew Brennan Jones <matthew.brennan.jones@gmail.com>
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.duplicate_attribute;
7 
8 import std.stdio;
9 import std.string;
10 import dparse.ast;
11 import dparse.lexer;
12 import analysis.base;
13 import analysis.helpers;
14 import dsymbol.scope_ : Scope;
15 
16 
17 /**
18  * Checks for duplicate attributes such as @property, @safe,
19  * @trusted, @system, pure, and nothrow
20  */
21 class DuplicateAttributeCheck : BaseAnalyzer
22 {
23 	alias visit = BaseAnalyzer.visit;
24 
25 	this(string fileName, const(Scope)* sc)
26 	{
27 		super(fileName, sc);
28 	}
29 
30 	override void visit(const Declaration node)
31 	{
32 		checkAttributes(node);
33 		node.accept(this);
34 	}
35 
36 	void checkAttributes(const Declaration node)
37 	{
38 		bool hasProperty = false;
39 		bool hasSafe = false;
40 		bool hasTrusted = false;
41 		bool hasSystem = false;
42 		bool hasPure = false;
43 		bool hasNoThrow = false;
44 
45 		// Check the attributes
46 		foreach (attribute; node.attributes)
47 		{
48 			size_t line, column;
49 			string attributeName = getAttributeName(attribute, line, column);
50 			if (!attributeName || line==0 || column==0)
51 				return;
52 
53 			// Check for the attributes
54 			checkDuplicateAttribute(attributeName, "property", line, column, hasProperty);
55 			checkDuplicateAttribute(attributeName, "safe", line, column, hasSafe);
56 			checkDuplicateAttribute(attributeName, "trusted", line, column, hasTrusted);
57 			checkDuplicateAttribute(attributeName, "system", line, column, hasSystem);
58 			checkDuplicateAttribute(attributeName, "pure", line, column, hasPure);
59 			checkDuplicateAttribute(attributeName, "nothrow", line, column, hasNoThrow);
60 		}
61 
62 		// Just return if missing function nodes
63 		if (!node.functionDeclaration
64 			|| !node.functionDeclaration.memberFunctionAttributes)
65 			return;
66 
67 		// Check the functions
68 		foreach (memberFunctionAttribute; node.functionDeclaration.memberFunctionAttributes)
69 		{
70 			size_t line, column;
71 			string attributeName = getAttributeName(memberFunctionAttribute, line, column);
72 			if (!attributeName || line==0 || column==0)
73 				return;
74 
75 			// Check for the attributes
76 			checkDuplicateAttribute(attributeName, "property", line, column, hasProperty);
77 			checkDuplicateAttribute(attributeName, "safe", line, column, hasSafe);
78 			checkDuplicateAttribute(attributeName, "trusted", line, column, hasTrusted);
79 			checkDuplicateAttribute(attributeName, "system", line, column, hasSystem);
80 			checkDuplicateAttribute(attributeName, "pure", line, column, hasPure);
81 			checkDuplicateAttribute(attributeName, "nothrow", line, column, hasNoThrow);
82 		}
83 	}
84 
85 	void checkDuplicateAttribute(const string attributeName, const string attributeDesired, size_t line, size_t column, ref bool hasAttribute)
86 	{
87 		// Just return if not an attribute
88 		if (attributeName != attributeDesired)
89 			return;
90 
91 		// Already has that attribute
92 		if (hasAttribute)
93 		{
94 			string message = "Attribute '%s' is duplicated.".format(attributeName);
95 			addErrorMessage(line, column, "dscanner.unnecessary.duplicate_attribute", message);
96 		}
97 
98 		// Mark it as having that attribute
99 		hasAttribute = true;
100 	}
101 
102 	string getAttributeName(const Attribute attribute, ref size_t line, ref size_t column)
103 	{
104 		// Get the name from the attribute identifier
105 		if (attribute
106 			&& attribute.atAttribute
107 			&& attribute.atAttribute.identifier !is Token.init
108 			&& attribute.atAttribute.identifier.text
109 			&& attribute.atAttribute.identifier.text.length)
110 		{
111 			auto token = attribute.atAttribute.identifier;
112 			line = token.line;
113 			column = token.column;
114 			return token.text;
115 		}
116 
117 		// Get the attribute from the storage class token
118 		if (attribute && attribute.attribute.type != tok!"")
119 		{
120 			line = attribute.attribute.line;
121 			column = attribute.attribute.column;
122 			return attribute.attribute.type.str;
123 		}
124 
125 		return null;
126 	}
127 
128 	string getAttributeName(const MemberFunctionAttribute memberFunctionAttribute, ref size_t line, ref size_t column)
129 	{
130 		// Get the name from the tokenType
131 		if (memberFunctionAttribute
132 			&& memberFunctionAttribute.tokenType !is IdType.init
133 			&& memberFunctionAttribute.tokenType.str
134 			&& memberFunctionAttribute.tokenType.str.length)
135 		{
136 			// FIXME: How do we get the line/column number?
137 			return memberFunctionAttribute.tokenType.str;
138 		}
139 
140 		// Get the name from the attribute identifier
141 		if (memberFunctionAttribute
142 			&& memberFunctionAttribute.atAttribute
143 			&& memberFunctionAttribute.atAttribute.identifier !is Token.init
144 			&& memberFunctionAttribute.atAttribute.identifier.type == tok!"identifier"
145 			&& memberFunctionAttribute.atAttribute.identifier.text
146 			&& memberFunctionAttribute.atAttribute.identifier.text.length)
147 		{
148 			auto iden = memberFunctionAttribute.atAttribute.identifier;
149 			line = iden.line;
150 			column = iden.column;
151 			return iden.text;
152 		}
153 
154 		return null;
155 	}
156 }
157 
158 unittest
159 {
160 	import analysis.config : StaticAnalysisConfig;
161 	StaticAnalysisConfig sac;
162 	sac.duplicate_attribute = true;
163 	assertAnalyzerWarnings(q{
164 		class ExampleAttributes
165 		{
166 			@property @safe bool xxx() // ok
167 			{
168 				return false;
169 			}
170 
171 			// Duplicate before
172 			@property @property bool aaa() // [warn]: Attribute 'property' is duplicated.
173 			{
174 				return false;
175 			}
176 
177 			// Duplicate after
178 			bool bbb() @safe @safe // [warn]: Attribute 'safe' is duplicated.
179 			{
180 				return false;
181 			}
182 
183 			// Duplicate before and after
184 			@system bool ccc() @system // [warn]: Attribute 'system' is duplicated.
185 			{
186 				return false;
187 			}
188 
189 			// Duplicate before and after
190 			@trusted bool ddd() @trusted // [warn]: Attribute 'trusted' is duplicated.
191 			{
192 				return false;
193 			}
194 		}
195 
196 		class ExamplePureNoThrow
197 		{
198 			pure nothrow bool aaa() // ok
199 			{
200 				return false;
201 			}
202 
203 			pure pure bool bbb() // [warn]: Attribute 'pure' is duplicated.
204 			{
205 				return false;
206 			}
207 
208 			// FIXME: There is no way to get the line/column number of the attribute like this
209 			bool ccc() pure pure // FIXME: [warn]: Attribute 'pure' is duplicated.
210 			{
211 				return false;
212 			}
213 
214 			nothrow nothrow bool ddd() // [warn]: Attribute 'nothrow' is duplicated.
215 			{
216 				return false;
217 			}
218 
219 			// FIXME: There is no way to get the line/column number of the attribute like this
220 			bool eee() nothrow nothrow // FIXME: [warn]: Attribute 'nothrow' is duplicated.
221 			{
222 				return false;
223 			}
224 		}
225 	}}, sac);
226 
227 	stderr.writeln("Unittest for DuplicateAttributeCheck passed.");
228 }
229