diff options
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 | ||||
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 37 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprMember.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 9 | ||||
-rw-r--r-- | clang/test/SemaCXX/member-expr.cpp | 38 |
6 files changed, 80 insertions, 15 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5fe82acf16c..2bc2ed4bd56 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/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/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 52ca8d98185..6d712ed633e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/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/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 90841d87458..78dce619eab 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/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<FunctionDecl>(CurContext); // The set of types we've considered so far. llvm::SmallPtrSet<CanQualType,8> CTypes; SmallVector<SourceLocation, 8> 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<CallExpr>(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<CXXOperatorCallExpr>(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/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 29e91e173df..b0e69e6cc70 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/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/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 7ebcd88fe5f..72415518930 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/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/clang/test/SemaCXX/member-expr.cpp b/clang/test/SemaCXX/member-expr.cpp index 338894c92a4..e2462aa48d6 100644 --- a/clang/test/SemaCXX/member-expr.cpp +++ b/clang/test/SemaCXX/member-expr.cpp @@ -79,16 +79,13 @@ namespace test5 { }; void test0(int x) { - x.A::foo<int>(); // expected-error {{'int' is not a structure or union}} } void test1(A *x) { - x.A::foo<int>(); // expected-error {{'test5::A *' is a pointer}} } void test2(A &x) { - x->A::foo<int>(); // expected-error {{'test5::A' is not a pointer}} \ - // expected-note {{did you mean to use '.' instead?}} + x->A::foo<int>(); // 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 <class T> 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<PR15045::bar>' requested here}} + + call_func(f); // expected-note {{in instantiation of function template specialization 'PR15045::call_func<PR15045::foo>' requested here}} } } |