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) 38 && (!hasConst(d.attributes) 39 && !hasConst(d.functionDeclaration.memberFunctionAttributes))) 40 { 41 addErrorMessage(d.functionDeclaration.name.line, 42 d.functionDeclaration.name.column, "dscanner.suspicious.object_const", 43 "Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const."); 44 } 45 d.accept(this); 46 } 47 48 private static bool hasConst(const Attribute[] attributes) 49 { 50 import std.algorithm : any; 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 return attributes.any!(a => a.tokenType == tok!"const" 58 || a.tokenType == tok!"immutable" 59 || 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 StaticAnalysisConfig sac; 76 sac.object_const_check = true; 77 assertAnalyzerWarnings(q{ 78 void testConsts() 79 { 80 // Will be ok because all are declared const/immutable 81 class Cat 82 { 83 const bool opEquals(Object a, Object b) // ok 84 { 85 return true; 86 } 87 88 const int opCmp(Object o) // ok 89 { 90 return 1; 91 } 92 93 const hash_t toHash() // ok 94 { 95 return 0; 96 } 97 98 const string toString() // ok 99 { 100 return "Cat"; 101 } 102 } 103 104 // Will warn, because none are const 105 class Dog 106 { 107 bool opEquals(Object a, Object b) // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 108 { 109 return true; 110 } 111 112 int opCmp(Object o) // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 113 { 114 return 1; 115 } 116 117 hash_t toHash() // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 118 { 119 return 0; 120 } 121 122 string toString() // [warn]: Methods 'opCmp', 'toHash', 'opEquals', 'opCast', and/or 'toString' are non-const. 123 { 124 return "Dog"; 125 } 126 } 127 } 128 }}, sac); 129 130 stderr.writeln("Unittest for ObjectConstCheck passed."); 131 } 132