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 analysis.objectconst; 7 8 import std.stdio; 9 import std.regex; 10 import dparse.ast; 11 import dparse.lexer; 12 import analysis.base; 13 import analysis.helpers; 14 import dsymbol.scope_ : Scope; 15 16 /** 17 * Checks that opEquals, opCmp, toHash, 'opCast', and toString are either const, 18 * immutable, or inout. 19 */ 20 class ObjectConstCheck : BaseAnalyzer 21 { 22 alias visit = BaseAnalyzer.visit; 23 24 this(string fileName, const(Scope)* sc) 25 { 26 super(fileName, sc); 27 } 28 29 mixin visitTemplate!ClassDeclaration; 30 mixin visitTemplate!InterfaceDeclaration; 31 mixin visitTemplate!UnionDeclaration; 32 mixin visitTemplate!StructDeclaration; 33 34 override void visit(const Declaration d) 35 { 36 if (inAggregate && d.functionDeclaration !is null 37 && isInteresting(d.functionDeclaration.name.text) && (!hasConst(d.attributes) 38 && !hasConst(d.functionDeclaration.memberFunctionAttributes))) 39 { 40 addErrorMessage(d.functionDeclaration.name.line, 41 d.functionDeclaration.name.column, "dscanner.suspicious.object_const", 42 "Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const."); 43 } 44 d.accept(this); 45 } 46 47 private static bool hasConst(const Attribute[] attributes) 48 { 49 import std.algorithm : any; 50 51 return attributes.any!(a => a.attribute == tok!"const"); 52 } 53 54 private static bool hasConst(const MemberFunctionAttribute[] attributes) 55 { 56 import std.algorithm : any; 57 58 return attributes.any!(a => a.tokenType == tok!"const" 59 || a.tokenType == tok!"immutable" || a.tokenType == tok!"inout"); 60 } 61 62 private static bool isInteresting(string name) 63 { 64 return name == "opCmp" || name == "toHash" || name == "opEquals" 65 || name == "toString" || name == "opCast"; 66 } 67 68 private bool looking = false; 69 70 } 71 72 unittest 73 { 74 import analysis.config : StaticAnalysisConfig; 75 76 StaticAnalysisConfig sac; 77 sac.object_const_check = true; 78 assertAnalyzerWarnings(q{ 79 void testConsts() 80 { 81 // Will be ok because all are declared const/immutable 82 class Cat 83 { 84 const bool opEquals(Object a, Object b) // ok 85 { 86 return true; 87 } 88 89 const int opCmp(Object o) // ok 90 { 91 return 1; 92 } 93 94 const hash_t toHash() // ok 95 { 96 return 0; 97 } 98 99 const string toString() // ok 100 { 101 return "Cat"; 102 } 103 } 104 105 // Will warn, because none are const 106 class Dog 107 { 108 bool opEquals(Object a, Object b) // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 109 { 110 return true; 111 } 112 113 int opCmp(Object o) // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 114 { 115 return 1; 116 } 117 118 hash_t toHash() // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 119 { 120 return 0; 121 } 122 123 string toString() // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 124 { 125 return "Dog"; 126 } 127 } 128 } 129 }}, sac); 130 131 stderr.writeln("Unittest for ObjectConstCheck passed."); 132 }