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.function_attributes;
7 
8 import 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)
29 	{
30 		super(fileName, sc);
31 	}
32 
33 	override void visit(const InterfaceDeclaration dec)
34 	{
35 		auto t = inInterface;
36 		inInterface = true;
37 		dec.accept(this);
38 		inInterface = t;
39 	}
40 
41 	override void visit(const ClassDeclaration dec)
42 	{
43 		auto 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 = false;
63 			bool foundProperty = false;
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"
74 					|| attribute.tokenType == tok!"inout";
75 			}
76 			if (foundProperty && !foundConst)
77 			{
78 				addErrorMessage(dec.name.line, dec.name.column, KEY,
79 					"Zero-parameter '@property' function should be"
80 					~ " marked 'const', 'inout', or 'immutable'.");
81 			}
82 		}
83 		dec.accept(this);
84 	}
85 
86 	override void visit(const Declaration dec)
87 	{
88 		if (dec.attributes.length == 0)
89 			goto end;
90 		foreach (attr; dec.attributes)
91 		{
92 			if (attr.attribute.type == tok!"")
93 				continue;
94 			if (attr.attribute == tok!"abstract" && inInterface)
95 			{
96 				addErrorMessage(attr.attribute.line,
97 					attr.attribute.column, KEY, ABSTRACT_MESSAGE);
98 				continue;
99 			}
100 			if (dec.functionDeclaration !is null
101 				&& (attr.attribute == tok!"const"
102 				|| attr.attribute == tok!"inout"
103 				|| attr.attribute == tok!"immutable"))
104 			{
105 				import std.string : format;
106 				immutable string attrString = str(attr.attribute.type);
107 				addErrorMessage(dec.functionDeclaration.name.line,
108 					dec.functionDeclaration.name.column, KEY,
109 					format("'%s' is not an attribute of the return type."
110 					~ " Place it after the parameter list to clarify.", attrString));
111 			}
112 		}
113 	end:
114 		dec.accept(this);
115 	}
116 
117 private:
118 	bool inInterface;
119 	enum string ABSTRACT_MESSAGE = "'abstract' attribute is redundant in interface declarations";
120 	enum string KEY = "dscanner.confusing.function_attributes";
121 }