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 dscanner.analysis.duplicate_attribute; 7 8 import std.stdio; 9 import std..string; 10 import dparse.ast; 11 import dparse.lexer; 12 import dscanner.analysis.base; 13 import dscanner.analysis.helpers; 14 import dsymbol.scope_ : Scope; 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, const(Scope)* sc, bool skipTests = false) 25 { 26 super(fileName, sc, skipTests); 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; 38 bool hasSafe; 39 bool hasTrusted; 40 bool hasSystem; 41 bool hasPure; 42 bool hasNoThrow; 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 || !node.functionDeclaration.memberFunctionAttributes) 63 return; 64 65 // Check the functions 66 foreach (memberFunctionAttribute; node.functionDeclaration.memberFunctionAttributes) 67 { 68 size_t line, column; 69 string attributeName = getAttributeName(memberFunctionAttribute, line, column); 70 if (!attributeName || line == 0 || column == 0) 71 return; 72 73 // Check for the attributes 74 checkDuplicateAttribute(attributeName, "property", line, column, hasProperty); 75 checkDuplicateAttribute(attributeName, "safe", line, column, hasSafe); 76 checkDuplicateAttribute(attributeName, "trusted", line, column, hasTrusted); 77 checkDuplicateAttribute(attributeName, "system", line, column, hasSystem); 78 checkDuplicateAttribute(attributeName, "pure", line, column, hasPure); 79 checkDuplicateAttribute(attributeName, "nothrow", line, column, hasNoThrow); 80 } 81 } 82 83 void checkDuplicateAttribute(const string attributeName, 84 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 && attribute.atAttribute && attribute.atAttribute.identifier !is Token.init 105 && attribute.atAttribute.identifier.text 106 && attribute.atAttribute.identifier.text.length) 107 { 108 auto token = attribute.atAttribute.identifier; 109 line = token.line; 110 column = token.column; 111 return token.text; 112 } 113 114 // Get the attribute from the storage class token 115 if (attribute && attribute.attribute.type != tok!"") 116 { 117 line = attribute.attribute.line; 118 column = attribute.attribute.column; 119 return attribute.attribute.type.str; 120 } 121 122 return null; 123 } 124 125 string getAttributeName(const MemberFunctionAttribute memberFunctionAttribute, 126 ref size_t line, ref size_t column) 127 { 128 // Get the name from the tokenType 129 if (memberFunctionAttribute && memberFunctionAttribute.tokenType !is IdType.init 130 && memberFunctionAttribute.tokenType.str 131 && memberFunctionAttribute.tokenType.str.length) 132 { 133 // FIXME: How do we get the line/column number? 134 return memberFunctionAttribute.tokenType.str; 135 } 136 137 // Get the name from the attribute identifier 138 if (memberFunctionAttribute && memberFunctionAttribute.atAttribute 139 && memberFunctionAttribute.atAttribute.identifier !is Token.init 140 && memberFunctionAttribute.atAttribute.identifier.type == tok!"identifier" 141 && memberFunctionAttribute.atAttribute.identifier.text 142 && memberFunctionAttribute.atAttribute.identifier.text.length) 143 { 144 auto iden = memberFunctionAttribute.atAttribute.identifier; 145 line = iden.line; 146 column = iden.column; 147 return iden.text; 148 } 149 150 return null; 151 } 152 } 153 154 unittest 155 { 156 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 157 158 StaticAnalysisConfig sac = disabledConfig(); 159 sac.duplicate_attribute = Check.enabled; 160 assertAnalyzerWarnings(q{ 161 class ExampleAttributes 162 { 163 @property @safe bool xxx() // ok 164 { 165 return false; 166 } 167 168 // Duplicate before 169 @property @property bool aaa() // [warn]: Attribute 'property' is duplicated. 170 { 171 return false; 172 } 173 174 // Duplicate after 175 bool bbb() @safe @safe // [warn]: Attribute 'safe' is duplicated. 176 { 177 return false; 178 } 179 180 // Duplicate before and after 181 @system bool ccc() @system // [warn]: Attribute 'system' is duplicated. 182 { 183 return false; 184 } 185 186 // Duplicate before and after 187 @trusted bool ddd() @trusted // [warn]: Attribute 'trusted' is duplicated. 188 { 189 return false; 190 } 191 } 192 193 class ExamplePureNoThrow 194 { 195 pure nothrow bool aaa() // ok 196 { 197 return false; 198 } 199 200 pure pure bool bbb() // [warn]: Attribute 'pure' is duplicated. 201 { 202 return false; 203 } 204 205 // FIXME: There is no way to get the line/column number of the attribute like this 206 bool ccc() pure pure // FIXME: [warn]: Attribute 'pure' is duplicated. 207 { 208 return false; 209 } 210 211 nothrow nothrow bool ddd() // [warn]: Attribute 'nothrow' is duplicated. 212 { 213 return false; 214 } 215 216 // FIXME: There is no way to get the line/column number of the attribute like this 217 bool eee() nothrow nothrow // FIXME: [warn]: Attribute 'nothrow' is duplicated. 218 { 219 return false; 220 } 221 } 222 }c, sac); 223 224 stderr.writeln("Unittest for DuplicateAttributeCheck passed."); 225 }