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 }