1 // Copyright (c) 2014, Matthew Brennan Jones <matthew.brennan.jones@gmail.com> 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.duplicate_attribute; 7 8 import std.stdio; 9 import std.string; 10 import dparse.ast; 11 import dparse.lexer; 12 import analysis.base; 13 import analysis.helpers; 14 import dsymbol.scope_ : Scope; 15 16 17 /** 18 * Checks for duplicate attributes such as @property, @safe, 19 * @trusted, @system, pure, and nothrow 20 */ 21 class DuplicateAttributeCheck : BaseAnalyzer 22 { 23 alias visit = BaseAnalyzer.visit; 24 25 this(string fileName, const(Scope)* sc) 26 { 27 super(fileName, sc); 28 } 29 30 override void visit(const Declaration node) 31 { 32 checkAttributes(node); 33 node.accept(this); 34 } 35 36 void checkAttributes(const Declaration node) 37 { 38 bool hasProperty = false; 39 bool hasSafe = false; 40 bool hasTrusted = false; 41 bool hasSystem = false; 42 bool hasPure = false; 43 bool hasNoThrow = false; 44 45 // Check the attributes 46 foreach (attribute; node.attributes) 47 { 48 size_t line, column; 49 string attributeName = getAttributeName(attribute, line, column); 50 if (!attributeName || line==0 || column==0) 51 return; 52 53 // Check for the attributes 54 checkDuplicateAttribute(attributeName, "property", line, column, hasProperty); 55 checkDuplicateAttribute(attributeName, "safe", line, column, hasSafe); 56 checkDuplicateAttribute(attributeName, "trusted", line, column, hasTrusted); 57 checkDuplicateAttribute(attributeName, "system", line, column, hasSystem); 58 checkDuplicateAttribute(attributeName, "pure", line, column, hasPure); 59 checkDuplicateAttribute(attributeName, "nothrow", line, column, hasNoThrow); 60 } 61 62 // Just return if missing function nodes 63 if (!node.functionDeclaration 64 || !node.functionDeclaration.memberFunctionAttributes) 65 return; 66 67 // Check the functions 68 foreach (memberFunctionAttribute; node.functionDeclaration.memberFunctionAttributes) 69 { 70 size_t line, column; 71 string attributeName = getAttributeName(memberFunctionAttribute, line, column); 72 if (!attributeName || line==0 || column==0) 73 return; 74 75 // Check for the attributes 76 checkDuplicateAttribute(attributeName, "property", line, column, hasProperty); 77 checkDuplicateAttribute(attributeName, "safe", line, column, hasSafe); 78 checkDuplicateAttribute(attributeName, "trusted", line, column, hasTrusted); 79 checkDuplicateAttribute(attributeName, "system", line, column, hasSystem); 80 checkDuplicateAttribute(attributeName, "pure", line, column, hasPure); 81 checkDuplicateAttribute(attributeName, "nothrow", line, column, hasNoThrow); 82 } 83 } 84 85 void checkDuplicateAttribute(const string attributeName, const string attributeDesired, size_t line, size_t column, ref bool hasAttribute) 86 { 87 // Just return if not an attribute 88 if (attributeName != attributeDesired) 89 return; 90 91 // Already has that attribute 92 if (hasAttribute) 93 { 94 string message = "Attribute '%s' is duplicated.".format(attributeName); 95 addErrorMessage(line, column, "dscanner.unnecessary.duplicate_attribute", message); 96 } 97 98 // Mark it as having that attribute 99 hasAttribute = true; 100 } 101 102 string getAttributeName(const Attribute attribute, ref size_t line, ref size_t column) 103 { 104 // Get the name from the attribute identifier 105 if (attribute 106 && attribute.atAttribute 107 && attribute.atAttribute.identifier !is Token.init 108 && attribute.atAttribute.identifier.text 109 && attribute.atAttribute.identifier.text.length) 110 { 111 auto token = attribute.atAttribute.identifier; 112 line = token.line; 113 column = token.column; 114 return token.text; 115 } 116 117 // Get the attribute from the storage class token 118 if (attribute && attribute.attribute.type != tok!"") 119 { 120 line = attribute.attribute.line; 121 column = attribute.attribute.column; 122 return attribute.attribute.type.str; 123 } 124 125 return null; 126 } 127 128 string getAttributeName(const MemberFunctionAttribute memberFunctionAttribute, ref size_t line, ref size_t column) 129 { 130 // Get the name from the tokenType 131 if (memberFunctionAttribute 132 && memberFunctionAttribute.tokenType !is IdType.init 133 && memberFunctionAttribute.tokenType.str 134 && memberFunctionAttribute.tokenType.str.length) 135 { 136 // FIXME: How do we get the line/column number? 137 return memberFunctionAttribute.tokenType.str; 138 } 139 140 // Get the name from the attribute identifier 141 if (memberFunctionAttribute 142 && memberFunctionAttribute.atAttribute 143 && memberFunctionAttribute.atAttribute.identifier !is Token.init 144 && memberFunctionAttribute.atAttribute.identifier.type == tok!"identifier" 145 && memberFunctionAttribute.atAttribute.identifier.text 146 && memberFunctionAttribute.atAttribute.identifier.text.length) 147 { 148 auto iden = memberFunctionAttribute.atAttribute.identifier; 149 line = iden.line; 150 column = iden.column; 151 return iden.text; 152 } 153 154 return null; 155 } 156 } 157 158 unittest 159 { 160 import analysis.config : StaticAnalysisConfig; 161 StaticAnalysisConfig sac; 162 sac.duplicate_attribute = true; 163 assertAnalyzerWarnings(q{ 164 class ExampleAttributes 165 { 166 @property @safe bool xxx() // ok 167 { 168 return false; 169 } 170 171 // Duplicate before 172 @property @property bool aaa() // [warn]: Attribute 'property' is duplicated. 173 { 174 return false; 175 } 176 177 // Duplicate after 178 bool bbb() @safe @safe // [warn]: Attribute 'safe' is duplicated. 179 { 180 return false; 181 } 182 183 // Duplicate before and after 184 @system bool ccc() @system // [warn]: Attribute 'system' is duplicated. 185 { 186 return false; 187 } 188 189 // Duplicate before and after 190 @trusted bool ddd() @trusted // [warn]: Attribute 'trusted' is duplicated. 191 { 192 return false; 193 } 194 } 195 196 class ExamplePureNoThrow 197 { 198 pure nothrow bool aaa() // ok 199 { 200 return false; 201 } 202 203 pure pure bool bbb() // [warn]: Attribute 'pure' is duplicated. 204 { 205 return false; 206 } 207 208 // FIXME: There is no way to get the line/column number of the attribute like this 209 bool ccc() pure pure // FIXME: [warn]: Attribute 'pure' is duplicated. 210 { 211 return false; 212 } 213 214 nothrow nothrow bool ddd() // [warn]: Attribute 'nothrow' is duplicated. 215 { 216 return false; 217 } 218 219 // FIXME: There is no way to get the line/column number of the attribute like this 220 bool eee() nothrow nothrow // FIXME: [warn]: Attribute 'nothrow' is duplicated. 221 { 222 return false; 223 } 224 } 225 }}, sac); 226 227 stderr.writeln("Unittest for DuplicateAttributeCheck passed."); 228 } 229