diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index f5fbf805de17..9cc4edc77d0c 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -2188,16 +2188,19 @@ extern (C++) final class DotIdExp : UnaExp bool noderef; // true if the result of the expression will never be dereferenced bool wantsym; // do not replace Symbol with its initializer during semantic() bool arrow; // ImportC: if -> instead of . + /// Location of the identifier (for accurate error reporting). Loc.initial when not from source. + Loc identLoc; - extern (D) this(Loc loc, Expression e, Identifier ident) @safe + extern (D) this(Loc loc, Expression e, Identifier ident, Loc identLoc = Loc.initial) @safe { super(loc, EXP.dotIdentifier, e); this.ident = ident; + this.identLoc = identLoc; } - static DotIdExp create(Loc loc, Expression e, Identifier ident) @safe + static DotIdExp create(Loc loc, Expression e, Identifier ident, Loc identLoc = Loc.initial) @safe { - return new DotIdExp(loc, e, ident); + return new DotIdExp(loc, e, ident, identLoc); } override void accept(Visitor v) diff --git a/compiler/src/dmd/expression.h b/compiler/src/dmd/expression.h index d3896125bd83..090750215a1a 100644 --- a/compiler/src/dmd/expression.h +++ b/compiler/src/dmd/expression.h @@ -694,8 +694,9 @@ class DotIdExp final : public UnaExp d_bool noderef; // true if the result of the expression will never be dereferenced d_bool wantsym; // do not replace Symbol with its initializer during semantic() d_bool arrow; // ImportC: if -> instead of . + Loc identLoc; // location of the identifier (for accurate error reporting) - static DotIdExp *create(Loc loc, Expression *e, Identifier *ident); + static DotIdExp *create(Loc loc, Expression *e, Identifier *ident, Loc identLoc = Loc()); void accept(Visitor *v) override { v->visit(this); } }; diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 725fc718ad3c..c126d9a2a79b 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -2187,7 +2187,11 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) } if (!s) - return ue.e1.type.getProperty(sc, loc, ident, 0, ue.e1); + { + auto die = ue.isDotIdExp(); + const identLoc = (die && die.identLoc != Loc.initial) ? die.identLoc : Loc.initial; + return ue.e1.type.getProperty(sc, loc, ident, 0, ue.e1, identLoc); + } FuncDeclaration f = s.isFuncDeclaration(); if (f) @@ -15700,7 +15704,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) if (exp.e1.isVarExp() && exp.e1.type.toBasetype().isTypeSArray() && exp.ident == Id.length) { // bypass checkPurity - return exp.e1.type.dotExp(sc, exp.e1, exp.ident, cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref)); + return exp.e1.type.dotExp(sc, exp.e1, exp.ident, cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref), exp.identLoc); } if (!exp.e1.isDotExp()) @@ -16065,7 +16069,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) Expression e = new PtrExp(exp.loc, exp.e1); e = e.expressionSemantic(sc); const newFlag = cast(DotExpFlag) (gag * DotExpFlag.gag | exp.noderef * DotExpFlag.noDeref); - return e.type.dotExp(sc, e, exp.ident, newFlag); + return e.type.dotExp(sc, e, exp.ident, newFlag, exp.identLoc); } else if (exp.ident == Id.__xalignof && exp.e1.isVarExp() && @@ -16092,7 +16096,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag); - Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag); + Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag, exp.identLoc); if (e) { e = e.expressionSemantic(sc); diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index c4439b55b655..9f9ff324eb04 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -2794,7 +2794,8 @@ class DotIdExp final : public UnaExp bool noderef; bool wantsym; bool arrow; - static DotIdExp* create(Loc loc, Expression* e, Identifier* ident); + Loc identLoc; // location of the identifier (for accurate error reporting) + static DotIdExp* create(Loc loc, Expression* e, Identifier* ident, Loc identLoc = Loc()); void accept(Visitor* v) override; }; diff --git a/compiler/src/dmd/parse.d b/compiler/src/dmd/parse.d index c8ca3a1a0f22..3a1fccbf96f1 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -9064,6 +9064,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.identifier) { Identifier id = token.ident; + const identLoc = token.loc; nextToken(); if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_) @@ -9072,7 +9073,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs); } else - e = new AST.DotIdExp(loc, e, id); + e = new AST.DotIdExp(loc, e, id, identLoc); continue; } if (token.value == TOK.new_) diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d index 779e6bfd7f59..9789a590a3e0 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -5014,11 +5014,12 @@ Expression defaultInitLiteral(Type t, Loc loc) * ident = the identifier of the property * flag = if flag & 1, don't report "not a property" error and just return NULL. * src = expression for type `t` or null. + * identLoc = location of the identifier (for accurate error reporting); use when != Loc.initial. * Returns: * expression representing the property, or null if not a property and (flag & 1) */ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int flag, - Expression src = null) + Expression src = null, Loc identLoc = Loc.initial) { Expression visitType(Type mt) { @@ -5094,21 +5095,22 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla if (mt == Type.terror) return ErrorExp.get(); + const errLoc = identLoc != Loc.initial ? identLoc : loc; if (s) { - error(loc, "no property `%s` for type `%s`", ident.toErrMsg(), mt.toErrMsg()); + error(errLoc, "no property `%s` for type `%s`", ident.toErrMsg(), mt.toErrMsg()); errorSupplemental(s.loc, "did you mean `%s`?", ident == s.ident ? s.toPrettyChars() : s.toErrMsg()); } else if (ident == Id.opCall && mt.ty == Tclass) - error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toErrMsg(), mt.toErrMsg(), mt.toPrettyChars()); + error(errLoc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toErrMsg(), mt.toErrMsg(), mt.toPrettyChars()); else if (const n = importHint(ident.toString())) - error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toErrMsg(), mt.toErrMsg(), cast(int)n.length, n.ptr); + error(errLoc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toErrMsg(), mt.toErrMsg(), cast(int)n.length, n.ptr); else { if (src) { - error(loc, "no property `%s` for `%s` of type `%s`", + error(errLoc, "no property `%s` for `%s` of type `%s`", ident.toErrMsg(), src.toErrMsg(), mt.toPrettyChars(true)); auto s2 = scope_.search_correct(ident); // UFCS @@ -5125,7 +5127,7 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla } } else - error(loc, "no property `%s` for type `%s`", ident.toErrMsg(), mt.toPrettyChars(true)); + error(errLoc, "no property `%s` for type `%s`", ident.toErrMsg(), mt.toPrettyChars(true)); if (auto dsym = derefType.toDsymbol(scope_)) { @@ -5425,7 +5427,7 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla } else { - e = mt.toBasetype().getProperty(scope_, loc, ident, flag); + e = mt.toBasetype().getProperty(scope_, loc, ident, flag, null, identLoc); } return e; } @@ -6151,7 +6153,7 @@ void resolve(Type mt, Loc loc, Scope* sc, out Expression pe, out Type pt, out Ds * Returns: * resulting expression with e.ident resolved */ -Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag) +Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag, Loc identLoc = Loc.initial) { enum LOGDOTEXP = false; if (LOGDOTEXP) @@ -6228,7 +6230,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag e = new StringExp(e.loc, e.toString()); } else - e = mt.getProperty(sc, e.loc, ident, flag & DotExpFlag.gag); + e = mt.getProperty(sc, e.loc, ident, flag & DotExpFlag.gag, null, identLoc); Lreturn: if (e) @@ -6288,7 +6290,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag break; default: - e = mt.Type.getProperty(sc, e.loc, ident, flag); + e = mt.Type.getProperty(sc, e.loc, ident, flag, null, identLoc); break; } } @@ -6339,7 +6341,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag break; default: - e = mt.Type.getProperty(sc, e.loc, ident, flag); + e = mt.Type.getProperty(sc, e.loc, ident, flag, null, identLoc); break; } } @@ -6389,11 +6391,11 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ident == Id.nan || ident == Id.infinity || ident == Id.epsilon) { auto vet = mt.basetype.isTypeSArray().next; // vector element type - if (auto ev = getProperty(vet, sc, e.loc, ident, DotExpFlag.gag)) + if (auto ev = getProperty(vet, sc, e.loc, ident, DotExpFlag.gag, null, identLoc)) return ev.castTo(sc, mt); // 'broadcast' ev to the vector elements } - return mt.basetype.dotExp(sc, e.castTo(sc, mt.basetype), ident, flag); + return mt.basetype.dotExp(sc, e.castTo(sc, mt.basetype), ident, flag, identLoc); } Expression visitArray(TypeArray mt) @@ -6541,7 +6543,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } // References just forward things along - return mt.next.dotExp(sc, e, ident, flag); + return mt.next.dotExp(sc, e, ident, flag, identLoc); } Expression visitDelegate(TypeDelegate mt) @@ -6716,7 +6718,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag // https://issues.dlang.org/show_bug.cgi?id=14010 if (!sc.inCfile && ident == Id._mangleof) { - return mt.getProperty(sc, e.loc, ident, flag & 1); + return mt.getProperty(sc, e.loc, ident, flag & 1, null, identLoc); } /* If e.tupleof @@ -6976,7 +6978,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag // https://issues.dlang.org/show_bug.cgi?id=14010 if (ident == Id._mangleof) { - return mt.getProperty(sc, e.loc, ident, flag & 1); + return mt.getProperty(sc, e.loc, ident, flag & 1, null, identLoc); } if (mt.sym.semanticRun < PASS.semanticdone) @@ -6987,24 +6989,25 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag { if (ident == Id._init) { - return mt.getProperty(sc, e.loc, ident, flag & 1); + return mt.getProperty(sc, e.loc, ident, flag & 1, null, identLoc); } /* Allow special enums to not need a member list */ if ((ident == Id.max || ident == Id.min) && (mt.sym.members || !mt.sym.isSpecial())) { - return mt.getProperty(sc, e.loc, ident, flag & 1); + return mt.getProperty(sc, e.loc, ident, flag & 1, null, identLoc); } - Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, DotExpFlag.gag); + Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, DotExpFlag.gag, identLoc); if (!(flag & 1) && !res) { + const errLoc = identLoc != Loc.initial ? identLoc : e.loc; if (auto ns = mt.sym.search_correct(ident)) - error(e.loc, "no property `%s` for type `%s`. Did you mean `%s.%s` ?", ident.toChars(), mt.toChars(), mt.toChars(), + error(errLoc, "no property `%s` for type `%s`. Did you mean `%s.%s` ?", ident.toChars(), mt.toChars(), mt.toChars(), ns.toChars()); else - error(e.loc, "no property `%s` for type `%s`", ident.toChars(), + error(errLoc, "no property `%s` for type `%s`", ident.toChars(), mt.toChars()); errorSupplemental(mt.sym.loc, "%s `%s` defined here", @@ -7029,7 +7032,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag // https://issues.dlang.org/show_bug.cgi?id=12543 if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof) { - return mt.Type.getProperty(sc, e.loc, ident, 0); + return mt.Type.getProperty(sc, e.loc, ident, 0, null, identLoc); } /* If e.tupleof @@ -7090,7 +7093,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag { if (e.op == EXP.type) { - return mt.Type.getProperty(sc, e.loc, ident, 0); + return mt.Type.getProperty(sc, e.loc, ident, 0, null, identLoc); } e = new DotTypeExp(e.loc, e, mt.sym); e = e.expressionSemantic(sc); @@ -7100,7 +7103,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag { if (e.op == EXP.type) { - return mt.Type.getProperty(sc, e.loc, ident, 0); + return mt.Type.getProperty(sc, e.loc, ident, 0, null, identLoc); } if (auto ifbase = cbase.isInterfaceDeclaration()) e = new CastExp(e.loc, e, ifbase.type); diff --git a/compiler/test/fail_compilation/diag21284.d b/compiler/test/fail_compilation/diag21284.d new file mode 100644 index 000000000000..3b1b3acb6180 --- /dev/null +++ b/compiler/test/fail_compilation/diag21284.d @@ -0,0 +1,18 @@ +/* + * Fix for issue 21284: "no property" error should point at the identifier, not the dot. + * The error location should be on the line of the undefined identifier (`three`), not the dot. +TEST_OUTPUT: +--- +fail_compilation/diag21284.d(17): Error: no property `three` for type `E` +fail_compilation/diag21284.d(10): enum `E` defined here +--- +*/ +enum E { one, two } + +void main() +{ + auto x = + E + . + three; +}