diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index 6f91595bc0d5..d05dbac3c5fb 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -340,6 +340,12 @@ struct ASTBase this.ident = ident; } + final extern (D) this(const ref Loc loc, Identifier ident) + { + this.loc = loc; + this.ident = ident; + } + void addComment(const(char)* comment) { if (!this.comment) @@ -618,6 +624,12 @@ struct ASTBase this.decl = decl; } + final extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) + { + super(loc, ident); + this.decl = decl; + } + override final inout(AttribDeclaration) isAttribDeclaration() inout { return this; @@ -1403,9 +1415,9 @@ struct ASTBase Condition condition; Dsymbols* elsedecl; - final extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl) + final extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) { - super(decl); + super(loc, null, decl); this.condition = condition; this.elsedecl = elsedecl; } @@ -1434,9 +1446,9 @@ struct ASTBase extern (C++) final class StaticIfDeclaration : ConditionalDeclaration { - extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl) + extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) { - super(condition, decl, elsedecl); + super(loc, condition, decl, elsedecl); } override void accept(Visitor v) diff --git a/src/dmd/attrib.d b/src/dmd/attrib.d index d4f1f7d9965b..b176e9b3a24f 100644 --- a/src/dmd/attrib.d +++ b/src/dmd/attrib.d @@ -940,9 +940,9 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration Condition condition; /// condition deciding whether decl or elsedecl applies Dsymbols* elsedecl; /// array of Dsymbol's for else block - extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl) + extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) { - super(decl); + super(loc, null, decl); //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); this.condition = condition; this.elsedecl = elsedecl; @@ -951,7 +951,7 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration override Dsymbol syntaxCopy(Dsymbol s) { assert(!s); - return new ConditionalDeclaration(condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); + return new ConditionalDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); } override final bool oneMember(Dsymbol* ps, Identifier ident) @@ -1018,16 +1018,16 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration private bool addisdone = false; /// true if members have been added to scope private bool onStack = false; /// true if a call to `include` is currently active - extern (D) this(Condition condition, Dsymbols* decl, Dsymbols* elsedecl) + extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) { - super(condition, decl, elsedecl); + super(loc, condition, decl, elsedecl); //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); } override Dsymbol syntaxCopy(Dsymbol s) { assert(!s); - return new StaticIfDeclaration(condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); + return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); } /**************************************** diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 56832b7c05a8..58df9c49327f 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -500,6 +500,8 @@ final class Parser(AST) : Lexer linkage = linksave; + Loc startloc; + switch (token.value) { case TOK.enum_: @@ -690,6 +692,7 @@ final class Parser(AST) : Lexer s = parseStaticAssert(); else if (next == TOK.if_) { + const Loc loc = token.loc; condition = parseStaticIfCondition(); AST.Dsymbols* athen; if (token.value == TOK.colon) @@ -709,7 +712,7 @@ final class Parser(AST) : Lexer aelse = parseBlock(pLastDecl); checkDanglingElse(elseloc); } - s = new AST.StaticIfDeclaration(condition, athen, aelse); + s = new AST.StaticIfDeclaration(loc, condition, athen, aelse); } else if (next == TOK.import_) { @@ -1134,6 +1137,7 @@ final class Parser(AST) : Lexer break; } case TOK.debug_: + startloc = token.loc; nextToken(); if (token.value == TOK.assign) { @@ -1158,6 +1162,7 @@ final class Parser(AST) : Lexer goto Lcondition; case TOK.version_: + startloc = token.loc; nextToken(); if (token.value == TOK.assign) { @@ -1200,7 +1205,7 @@ final class Parser(AST) : Lexer aelse = parseBlock(pLastDecl); checkDanglingElse(elseloc); } - s = new AST.ConditionalDeclaration(condition, athen, aelse); + s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse); break; } case TOK.semicolon: diff --git a/test/unit/parser/conditionalcompilation_location.d b/test/unit/parser/conditionalcompilation_location.d new file mode 100644 index 000000000000..71f072874220 --- /dev/null +++ b/test/unit/parser/conditionalcompilation_location.d @@ -0,0 +1,95 @@ +module parser.conditionalcompilation_location; + +import dmd.frontend : parseModule; +import support : afterEach, beforeEach; +import dmd.attrib : ConditionalDeclaration, StaticIfDeclaration; +import dmd.globals : Loc; +import dmd.visitor : SemanticTimeTransitiveVisitor; + +@beforeEach +void initializeFrontend() +{ + import dmd.frontend : initDMD; + + initDMD(); +} + +@afterEach +void deinitializeFrontend() +{ + import dmd.frontend : deinitializeDMD; + deinitializeDMD(); +} + +extern (C++) class Visitor : SemanticTimeTransitiveVisitor +{ + alias visit = typeof(super).visit; + Loc l; + + override void visit(ConditionalDeclaration cd) + { + l = cd.loc; + } + + override void visit(StaticIfDeclaration sif) + { + l = sif.loc; + } +} + +immutable struct Test +{ + /* + * The description of the unit test. + * + * This will go into the UDA attached to the `unittest` block. + */ + string description_; + + /* + * The code to parse. + * + */ + string code_; + + string code() + { + return code_; + } + + string description() + { + return description_; + } +} + +enum tests = [ + Test("`version` symbol and condition on the same line", "version(a)"), + Test("`version` symbol and condition different lines", "version\n(a)"), + Test("`version` symbol, condition and parantheses on different lines", "version\n(\na\n)"), + + Test("`debug` symbol and condition on the same line", "debug(a)"), + Test("`debug` symbol and condition different lines", "debug\n(a)"), + Test("`debug` symbol, condition and parantheses on different lines", "debug\n(\na\n)"), + + Test("`static if` and condition on the same line", "static if(a)"), + Test("`static if` and condition different lines", "static if\n(a)"), + Test("`static if`, condition and parantheses on different lines", "static if\n(\na\n)"), + Test("`static` and `if` on different lines", "static\nif\n(a)"), +]; + +static foreach (test; tests) +{ + @(test.description) + unittest + { + auto t = parseModule("test.d", "first_token " ~ test.code); + + scope visitor = new Visitor; + t.module_.accept(visitor); + + assert(visitor.l.linnum == 1); + assert(visitor.l.charnum == 13); + assert(visitor.l.fileOffset == 12); + } +}