1 module analysis.constructors;
2 
3 import std.d.ast;
4 import std.d.lexer;
5 import std.stdio;
6 import analysis.base;
7 import analysis.helpers;
8 
9 
10 class ConstructorCheck : BaseAnalyzer
11 {
12 	alias visit = BaseAnalyzer.visit;
13 
14 	this(string fileName)
15 	{
16 		super(fileName);
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 
77 private:
78 
79 	enum State: ubyte
80 	{
81 		ignoring,
82 		inClass,
83 		inStruct
84 	}
85 
86 	State state;
87 
88 	bool hasNoArgConstructor;
89 	bool hasDefaultArgConstructor;
90 }
91 
92 unittest
93 {
94 	import analysis.config : StaticAnalysisConfig;
95 
96 	StaticAnalysisConfig sac;
97 	sac.constructor_check = true;
98 	assertAnalyzerWarnings(q{
99 		class Cat // [warn]: This class has a zero-argument constructor as well as a constructor with one default argument. This can be confusing.
100 		{
101 			this() {}
102 			this(string name = "kittie") {}
103 		}
104 
105 		struct Dog
106 		{
107 			this() {}
108 			this(string name = "doggie") {} // [warn]: This struct constructor can never be called with its default argument.
109 		}
110 	}}, sac);
111 
112 	stderr.writeln("Unittest for ConstructorCheck passed.");
113 }
114