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.function_attributes;
7 
8 import dscanner.analysis.base;
9 import dparse.ast;
10 import dparse.lexer;
11 import std.stdio;
12 import dsymbol.scope_;
13 
14 /**
15  * Prefer
16  * ---
17  * int getStuff() const {}
18  * ---
19  * to
20  * ---
21  * const int getStuff() {}
22  * ---
23  */
24 class FunctionAttributeCheck : BaseAnalyzer
25 {
26 	alias visit = BaseAnalyzer.visit;
27 
28 	this(string fileName, const(Scope)* sc, bool skipTests = false)
29 	{
30 		super(fileName, sc, skipTests);
31 	}
32 
33 	override void visit(const InterfaceDeclaration dec)
34 	{
35 		const t = inInterface;
36 		inInterface = true;
37 		dec.accept(this);
38 		inInterface = t;
39 	}
40 
41 	override void visit(const ClassDeclaration dec)
42 	{
43 		const t = inInterface;
44 		inInterface = false;
45 		dec.accept(this);
46 		inInterface = t;
47 	}
48 
49 	override void visit(const AttributeDeclaration dec)
50 	{
51 		if (inInterface && dec.attribute.attribute == tok!"abstract")
52 		{
53 			addErrorMessage(dec.attribute.attribute.line,
54 					dec.attribute.attribute.column, KEY, ABSTRACT_MESSAGE);
55 		}
56 	}
57 
58 	override void visit(const FunctionDeclaration dec)
59 	{
60 		if (dec.parameters.parameters.length == 0)
61 		{
62 			bool foundConst;
63 			bool foundProperty;
64 			foreach (attribute; dec.attributes)
65 				foundConst = foundConst || attribute.attribute.type == tok!"const"
66 					|| attribute.attribute.type == tok!"immutable"
67 					|| attribute.attribute.type == tok!"inout";
68 			foreach (attribute; dec.memberFunctionAttributes)
69 			{
70 				foundProperty = foundProperty || (attribute.atAttribute !is null
71 						&& attribute.atAttribute.identifier.text == "property");
72 				foundConst = foundConst || attribute.tokenType == tok!"const"
73 					|| attribute.tokenType == tok!"immutable" || attribute.tokenType == tok!"inout";
74 			}
75 			if (foundProperty && !foundConst)
76 			{
77 				addErrorMessage(dec.name.line, dec.name.column, KEY,
78 						"Zero-parameter '@property' function should be"
79 						~ " marked 'const', 'inout', or 'immutable'.");
80 			}
81 		}
82 		dec.accept(this);
83 	}
84 
85 	override void visit(const Declaration dec)
86 	{
87 		if (dec.attributes.length == 0)
88 			goto end;
89 		foreach (attr; dec.attributes)
90 		{
91 			if (attr.attribute.type == tok!"")
92 				continue;
93 			if (attr.attribute == tok!"abstract" && inInterface)
94 			{
95 				addErrorMessage(attr.attribute.line, attr.attribute.column, KEY, ABSTRACT_MESSAGE);
96 				continue;
97 			}
98 			if (dec.functionDeclaration !is null && (attr.attribute == tok!"const"
99 					|| attr.attribute == tok!"inout" || attr.attribute == tok!"immutable"))
100 			{
101 				import std..string : format;
102 
103 				immutable string attrString = str(attr.attribute.type);
104 				addErrorMessage(dec.functionDeclaration.name.line,
105 						dec.functionDeclaration.name.column, KEY, format(
106 							"'%s' is not an attribute of the return type." ~ " Place it after the parameter list to clarify.",
107 							attrString));
108 			}
109 		}
110 	end:
111 		dec.accept(this);
112 	}
113 
114 private:
115 	bool inInterface;
116 	enum string ABSTRACT_MESSAGE = "'abstract' attribute is redundant in interface declarations";
117 	enum string KEY = "dscanner.confusing.function_attributes";
118 }