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 final class DuplicateAttributeCheck : BaseAnalyzer 21 { 22 alias visit = BaseAnalyzer.visit; 23 24 mixin AnalyzerInfo!"duplicate_attribute"; 25 26 this(string fileName, const(Scope)* sc, bool skipTests = false) 27 { 28 super(fileName, sc, skipTests); 29 } 30 31 override void visit(const Declaration node) 32 { 33 checkAttributes(node); 34 node.accept(this); 35 } 36 37 void checkAttributes(const Declaration node) 38 { 39 bool hasProperty; 40 bool hasSafe; 41 bool hasTrusted; 42 bool hasSystem; 43 bool hasPure; 44 bool hasNoThrow; 45 46 // Check the attributes 47 foreach (attribute; node.attributes) 48 { 49 size_t line, column; 50 string attributeName = getAttributeName(attribute, line, column); 51 if (!attributeName || line == 0 || column == 0) 52 return; 53 54 // Check for the attributes 55 checkDuplicateAttribute(attributeName, "property", line, column, hasProperty); 56 checkDuplicateAttribute(attributeName, "safe", line, column, hasSafe); 57 checkDuplicateAttribute(attributeName, "trusted", line, column, hasTrusted); 58 checkDuplicateAttribute(attributeName, "system", line, column, hasSystem); 59 checkDuplicateAttribute(attributeName, "pure", line, column, hasPure); 60 checkDuplicateAttribute(attributeName, "nothrow", line, column, hasNoThrow); 61 } 62 63 // Just return if missing function nodes 64 if (!node.functionDeclaration || !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, 86 const string attributeDesired, size_t line, size_t column, ref bool hasAttribute) 87 { 88 // Just return if not an attribute 89 if (attributeName != attributeDesired) 90 return; 91 92 // Already has that attribute 93 if (hasAttribute) 94 { 95 string message = "Attribute '%s' is duplicated.".format(attributeName); 96 addErrorMessage(line, column, "dscanner.unnecessary.duplicate_attribute", message); 97 } 98 99 // Mark it as having that attribute 100 hasAttribute = true; 101 } 102 103 string getAttributeName(const Attribute attribute, ref size_t line, ref size_t column) 104 { 105 // Get the name from the attribute identifier 106 if (attribute && attribute.atAttribute && 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, 128 ref size_t line, ref size_t column) 129 { 130 // Get the name from the tokenType 131 if (memberFunctionAttribute && 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 && memberFunctionAttribute.atAttribute 141 && memberFunctionAttribute.atAttribute.identifier !is Token.init 142 && memberFunctionAttribute.atAttribute.identifier.type == tok!"identifier" 143 && memberFunctionAttribute.atAttribute.identifier.text 144 && memberFunctionAttribute.atAttribute.identifier.text.length) 145 { 146 auto iden = memberFunctionAttribute.atAttribute.identifier; 147 line = iden.line; 148 column = iden.column; 149 return iden.text; 150 } 151 152 return null; 153 } 154 } 155 156 unittest 157 { 158 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 159 160 StaticAnalysisConfig sac = disabledConfig(); 161 sac.duplicate_attribute = Check.enabled; 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 }