From df92a4d722b73161c372b6811607af8b9386bea1 Mon Sep 17 00:00:00 2001 From: Hackerpilot Date: Wed, 9 Apr 2014 12:17:40 -0700 Subject: [PATCH] Added some checks for default constructors --- analysis/constructors.d | 86 +++++++++++++++++++++++++++++++++++++++++ analysis/run.d | 2 + stdx/d/ast.d | 2 + stdx/d/parser.d | 2 + 4 files changed, 92 insertions(+) create mode 100644 analysis/constructors.d diff --git a/analysis/constructors.d b/analysis/constructors.d new file mode 100644 index 0000000..2c5d9a5 --- /dev/null +++ b/analysis/constructors.d @@ -0,0 +1,86 @@ +module analysis.constructors; + +import stdx.d.ast; +import stdx.d.lexer; +import analysis.base; + +class ConstructorCheck : BaseAnalyzer +{ + alias visit = BaseAnalyzer.visit; + + this(string fileName) + { + super(fileName); + } + + override void visit(const ClassDeclaration classDeclaration) + { + bool oldHasDefault = hasDefaultArgConstructor; + bool oldHasNoArg = hasNoArgConstructor; + hasNoArgConstructor = false; + hasDefaultArgConstructor = false; + State prev = state; + state = State.inClass; + classDeclaration.accept(this); + if (hasNoArgConstructor && hasDefaultArgConstructor) + { + addErrorMessage(classDeclaration.name.line, + classDeclaration.name.column, "This class has a zero-argument" + ~ " constructor as well as a constructor with one default" + ~ " argument. This can be confusing"); + } + hasDefaultArgConstructor = oldHasDefault; + hasNoArgConstructor = oldHasNoArg; + state = prev; + } + + override void visit(const StructDeclaration structDeclaration) + { + State prev = state; + state = State.inStruct; + structDeclaration.accept(this); + state = prev; + } + + override void visit(const Constructor constructor) + { + final switch (state) + { + case State.inStruct: + if (constructor.parameters.parameters.length == 1 + && constructor.parameters.parameters[0].default_ !is null) + { + addErrorMessage(constructor.line, constructor.column, + "This struct constructor can never be called with its " + ~ "default argument."); + } + break; + case State.inClass: + if (constructor.parameters.parameters.length == 1 + && constructor.parameters.parameters[0].default_ !is null) + { + hasDefaultArgConstructor = true; + } + else if (constructor.parameters.parameters.length == 0) + hasNoArgConstructor = true; + break; + case State.ignoring: + break; + } + } + + +private: + + enum State: ubyte + { + ignoring, + inClass, + inStruct + } + + State state; + + bool hasNoArgConstructor; + bool hasDefaultArgConstructor; +} diff --git a/analysis/run.d b/analysis/run.d index 74971e1..56f1fa9 100644 --- a/analysis/run.d +++ b/analysis/run.d @@ -20,6 +20,7 @@ import analysis.fish; import analysis.numbers; import analysis.objectconst; import analysis.range; +import analysis.constructors; void messageFunction(string fileName, size_t line, size_t column, string message, bool isError) @@ -70,6 +71,7 @@ void analyze(File output, string[] fileNames, bool staticAnalyze = true) checks ~= new NumberStyleCheck(fileName); checks ~= new ObjectConstCheck(fileName); checks ~= new BackwardsRangeCheck(fileName); + checks ~= new ConstructorCheck(fileName); foreach (check; checks) { diff --git a/stdx/d/ast.d b/stdx/d/ast.d index bff63c3..44a12c8 100644 --- a/stdx/d/ast.d +++ b/stdx/d/ast.d @@ -955,6 +955,8 @@ public: /** */ MemberFunctionAttribute[] memberFunctionAttributes; /** */ TemplateParameters templateParameters; /** */ size_t location; + /** */ size_t line; + /** */ size_t column; /** */ string comment; } diff --git a/stdx/d/parser.d b/stdx/d/parser.d index 81b35ad..8bac01f 100644 --- a/stdx/d/parser.d +++ b/stdx/d/parser.d @@ -1468,6 +1468,8 @@ class ClassFour(A, B) if (someTest()) : Super {}}c; auto t = expect(tok!"this"); if (t is null) return null; node.location = t.index; + node.line = t.line; + node.column = t.column; auto p = peekPastParens(); bool isTemplate = false; if (p !is null && p.type == tok!"(")