1 module analysis.constructors; 2 3 import dparse.ast; 4 import dparse.lexer; 5 import std.stdio; 6 import analysis.base; 7 import analysis.helpers; 8 import dsymbol.scope_ : Scope; 9 10 11 class ConstructorCheck : BaseAnalyzer 12 { 13 alias visit = BaseAnalyzer.visit; 14 15 this(string fileName, const(Scope)* sc) 16 { 17 super(fileName, sc); 18 } 19 20 override void visit(const ClassDeclaration classDeclaration) 21 { 22 immutable bool oldHasDefault = hasDefaultArgConstructor; 23 immutable bool oldHasNoArg = hasNoArgConstructor; 24 hasNoArgConstructor = false; 25 hasDefaultArgConstructor = false; 26 immutable State prev = state; 27 state = State.inClass; 28 classDeclaration.accept(this); 29 if (hasNoArgConstructor && hasDefaultArgConstructor) 30 { 31 addErrorMessage(classDeclaration.name.line, 32 classDeclaration.name.column, "dscanner.confusing.constructor_args", 33 "This class has a zero-argument constructor as well as a" 34 ~ " constructor with one default argument. This can be confusing."); 35 } 36 hasDefaultArgConstructor = oldHasDefault; 37 hasNoArgConstructor = oldHasNoArg; 38 state = prev; 39 } 40 41 override void visit(const StructDeclaration structDeclaration) 42 { 43 immutable State prev = state; 44 state = State.inStruct; 45 structDeclaration.accept(this); 46 state = prev; 47 } 48 49 override void visit(const Constructor constructor) 50 { 51 final switch (state) 52 { 53 case State.inStruct: 54 if (constructor.parameters.parameters.length == 1 55 && constructor.parameters.parameters[0].default_ !is null) 56 { 57 addErrorMessage(constructor.line, constructor.column, 58 "dscanner.confusing.struct_constructor_default_args", 59 "This struct constructor can never be called with its " 60 ~ "default argument."); 61 } 62 break; 63 case State.inClass: 64 if (constructor.parameters.parameters.length == 1 65 && constructor.parameters.parameters[0].default_ !is null) 66 { 67 hasDefaultArgConstructor = true; 68 } 69 else if (constructor.parameters.parameters.length == 0) 70 hasNoArgConstructor = true; 71 break; 72 case State.ignoring: 73 break; 74 } 75 } 76 77 78 private: 79 80 enum State: ubyte 81 { 82 ignoring, 83 inClass, 84 inStruct 85 } 86 87 State state; 88 89 bool hasNoArgConstructor; 90 bool hasDefaultArgConstructor; 91 } 92 93 unittest 94 { 95 import analysis.config : StaticAnalysisConfig; 96 97 StaticAnalysisConfig sac; 98 sac.constructor_check = true; 99 assertAnalyzerWarnings(q{ 100 class Cat // [warn]: This class has a zero-argument constructor as well as a constructor with one default argument. This can be confusing. 101 { 102 this() {} 103 this(string name = "kittie") {} 104 } 105 106 struct Dog 107 { 108 this() {} 109 this(string name = "doggie") {} // [warn]: This struct constructor can never be called with its default argument. 110 } 111 }}, sac); 112 113 stderr.writeln("Unittest for ConstructorCheck passed."); 114 } 115