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