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