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