From: Kaelyn Uhrain Date: Wed, 31 Jul 2013 17:38:24 +0000 (+0000) Subject: Improve the diagnostic experience, including adding recovery, for X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=baaeb85f07640794f6a4fabb871e33deeab07df2;p=clang Improve the diagnostic experience, including adding recovery, for changing '->' to '.' when there is no operator-> defined for a class. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@187504 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 5fe82acf16..2bc2ed4bd5 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4233,6 +4233,8 @@ def err_typecheck_member_reference_suggestion : Error< "member reference type %0 is %select{a|not a}1 pointer; maybe you meant to use '%select{->|.}1'?">; def note_typecheck_member_reference_suggestion : Note< "did you mean to use '.' instead?">; +def note_member_reference_arrow_from_operator_arrow : Note< + "'->' applied to return value of the operator->() declared here">; def err_typecheck_member_reference_type : Error< "cannot refer to type member %0 in %1 with '%select{.|->}2'">; def err_typecheck_member_reference_unknown : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 52ca8d9818..6d712ed633 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2256,7 +2256,8 @@ public: SourceLocation RParenLoc); ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base, - SourceLocation OpLoc); + SourceLocation OpLoc, + bool *NoArrowOperatorFound = 0); /// CheckCallReturnType - Checks that a call expression's return type is /// complete. Returns true on failure. The location passed in is the location diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 90841d8745..78dce619ea 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -5076,15 +5076,44 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, // [...] When operator->returns, the operator-> is applied to the value // returned, with the original second operand. if (OpKind == tok::arrow) { + bool NoArrowOperatorFound = false; + bool FirstIteration = true; + FunctionDecl *CurFD = dyn_cast(CurContext); // The set of types we've considered so far. llvm::SmallPtrSet CTypes; SmallVector Locations; CTypes.insert(Context.getCanonicalType(BaseType)); while (BaseType->isRecordType()) { - Result = BuildOverloadedArrowExpr(S, Base, OpLoc); - if (Result.isInvalid()) + Result = BuildOverloadedArrowExpr( + S, Base, OpLoc, + // When in a template specialization and on the first loop iteration, + // potentially give the default diagnostic (with the fixit in a + // separate note) instead of having the error reported back to here + // and giving a diagnostic with a fixit attached to the error itself. + (FirstIteration && CurFD && CurFD->isFunctionTemplateSpecialization()) + ? 0 + : &NoArrowOperatorFound); + if (Result.isInvalid()) { + if (NoArrowOperatorFound) { + if (FirstIteration) { + Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << BaseType << 1 << Base->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, "."); + OpKind = tok::period; + break; + } else { + Diag(OpLoc, diag::err_typecheck_member_reference_arrow) + << BaseType << Base->getSourceRange(); + CallExpr *CE = dyn_cast(Base); + if (Decl *CD = (CE ? CE->getCalleeDecl() : 0)) { + Diag(CD->getLocStart(), + diag::note_member_reference_arrow_from_operator_arrow); + } + } + } return ExprError(); + } Base = Result.get(); if (CXXOperatorCallExpr *OpCall = dyn_cast(Base)) Locations.push_back(OpCall->getDirectCallee()->getLocation()); @@ -5096,9 +5125,11 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, Diag(Locations[i], diag::note_declared_at); return ExprError(); } + FirstIteration = false; } - if (BaseType->isPointerType() || BaseType->isObjCObjectPointerType()) + if (OpKind == tok::arrow && + (BaseType->isPointerType() || BaseType->isObjCObjectPointerType())) BaseType = BaseType->getPointeeType(); } diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 29e91e173d..b0e69e6cc7 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -1136,10 +1136,8 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, // foo->bar // This is actually well-formed in C++ if MyRecord has an // overloaded operator->, but that should have been dealt with - // by now. - Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() - << FixItHint::CreateReplacement(OpLoc, "."); + // by now--or a diagnostic message already issued if a problem + // was encountered while looking for the overloaded operator->. IsArrow = false; } else if (BaseType->isFunctionType()) { goto fail; diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 7ebcd88fe5..7241551893 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -11463,7 +11463,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, /// (if one exists), where @c Base is an expression of class type and /// @c Member is the name of the member we're trying to find. ExprResult -Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) { +Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc, + bool *NoArrowOperatorFound) { assert(Base->getType()->isRecordType() && "left-hand side must have class type"); @@ -11509,6 +11510,12 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) { case OR_No_Viable_Function: if (CandidateSet.empty()) { QualType BaseType = Base->getType(); + if (NoArrowOperatorFound) { + // Report this specific error to the caller instead of emitting a + // diagnostic, as requested. + *NoArrowOperatorFound = true; + return ExprError(); + } Diag(OpLoc, diag::err_typecheck_member_reference_arrow) << BaseType << Base->getSourceRange(); if (BaseType->isRecordType() && !BaseType->isPointerType()) { diff --git a/test/SemaCXX/member-expr.cpp b/test/SemaCXX/member-expr.cpp index 338894c92a..e2462aa48d 100644 --- a/test/SemaCXX/member-expr.cpp +++ b/test/SemaCXX/member-expr.cpp @@ -79,16 +79,13 @@ namespace test5 { }; void test0(int x) { - x.A::foo(); // expected-error {{'int' is not a structure or union}} } void test1(A *x) { - x.A::foo(); // expected-error {{'test5::A *' is a pointer}} } void test2(A &x) { - x->A::foo(); // expected-error {{'test5::A' is not a pointer}} \ - // expected-note {{did you mean to use '.' instead?}} + x->A::foo(); // expected-error {{'test5::A' is not a pointer; maybe you meant to use '.'?}} } } @@ -182,7 +179,36 @@ namespace PR15045 { int f() { Cl0 c; - return c->a; // expected-error {{member reference type 'PR15045::Cl0' is not a pointer}} \ - // expected-note {{did you mean to use '.' instead?}} + return c->a; // expected-error {{member reference type 'PR15045::Cl0' is not a pointer; maybe you meant to use '.'?}} + } + + struct bar { + void func(); // expected-note {{'func' declared here}} + }; + + struct foo { + bar operator->(); // expected-note 2 {{'->' applied to return value of the operator->() declared here}} + }; + + template void call_func(T t) { + t->func(); // expected-error-re 2 {{member reference type 'PR15045::bar' is not a pointer$}} \ + // expected-note {{did you mean to use '.' instead?}} + } + + void test_arrow_on_non_pointer_records() { + bar e; + foo f; + + // Show that recovery has happened by also triggering typo correction + e->Func(); // expected-error {{member reference type 'PR15045::bar' is not a pointer; maybe you meant to use '.'?}} \ + // expected-error {{no member named 'Func' in 'PR15045::bar'; did you mean 'func'?}} + + // Make sure a fixit isn't given in the case that the '->' isn't actually + // the problem (the problem is with the return value of an operator->). + f->func(); // expected-error-re {{member reference type 'PR15045::bar' is not a pointer$}} + + call_func(e); // expected-note {{in instantiation of function template specialization 'PR15045::call_func' requested here}} + + call_func(f); // expected-note {{in instantiation of function template specialization 'PR15045::call_func' requested here}} } }