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 }