From 2b90f7637e846ea555dedac14e7f5065d85c5d3b Mon Sep 17 00:00:00 2001 From: Kaelyn Uhrain Date: Wed, 25 Apr 2012 19:49:54 +0000 Subject: [PATCH] Add an error message with fixit hint for changing '.' to '->'. This is mainly for attempting to recover in cases where a class provides a custom operator-> and a '.' was accidentally used instead of '->' when accessing a member of the object returned by the current object's operator->. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@155580 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ include/clang/Sema/Sema.h | 15 +++++++++- lib/Sema/SemaExprMember.cpp | 35 ++++++++++++++++++++-- test/FixIt/fixit.cpp | 23 ++++++++++++++ test/SemaCXX/arrow-operator.cpp | 28 +++++++++++++++++ 5 files changed, 99 insertions(+), 4 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 113a8fc3e5..f9fb2bb4fb 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3641,6 +3641,8 @@ def warn_subscript_is_char : Warning<"array subscript is of type 'char'">, def err_typecheck_incomplete_tag : Error<"incomplete definition of type %0">; def err_no_member : Error<"no member named %0 in %1">; +def err_no_member_overloaded_arrow : Error< + "no member named %0 in %1; did you mean to use '->' instead of '.'?">; def err_member_not_yet_instantiated : Error< "no member %0 in %1; it has not yet been instantiated">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6012c5664e..252ea5b6d0 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2703,6 +2703,18 @@ public: const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs); + // This struct is for use by ActOnMemberAccess to allow + // BuildMemberReferenceExpr to be able to reinvoke ActOnMemberAccess after + // changing the access operator from a '.' to a '->' (to see if that is the + // change needed to fix an error about an unknown member, e.g. when the class + // defines a custom operator->). + struct ActOnMemberAccessExtraArgs { + Scope *S; + UnqualifiedId &Id; + Decl *ObjCImpDecl; + bool HasTrailingLParen; + }; + ExprResult BuildMemberReferenceExpr(Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, const CXXScopeSpec &SS, @@ -2710,7 +2722,8 @@ public: NamedDecl *FirstQualifierInScope, LookupResult &R, const TemplateArgumentListInfo *TemplateArgs, - bool SuppressQualifierCheck = false); + bool SuppressQualifierCheck = false, + ActOnMemberAccessExtraArgs *ExtraArgs = 0); ExprResult PerformMemberExprBaseConversion(Expr *Base, bool IsArrow); ExprResult LookupMemberExpr(LookupResult &R, ExprResult &Base, diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 01d5fc6584..81aba6ac0e 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -813,8 +813,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierInScope, LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs, - bool SuppressQualifierCheck) { + const TemplateArgumentListInfo *TemplateArgs, + bool SuppressQualifierCheck, + ActOnMemberAccessExtraArgs *ExtraArgs) { QualType BaseType = BaseExprType; if (IsArrow) { assert(BaseType->isPointerType()); @@ -835,6 +836,32 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, ? computeDeclContext(SS, false) : BaseType->getAs()->getDecl()); + if (ExtraArgs) { + ExprResult RetryExpr; + if (!IsArrow && BaseExpr) { + SFINAETrap Trap(*this); + ParsedType ObjectType; + bool MayBePseudoDestructor = false; + RetryExpr = ActOnStartCXXMemberReference(getCurScope(), BaseExpr, + OpLoc, tok::arrow, ObjectType, + MayBePseudoDestructor); + if (RetryExpr.isUsable() && !Trap.hasErrorOccurred()) { + CXXScopeSpec TempSS(SS); + RetryExpr = ActOnMemberAccessExpr( + ExtraArgs->S, RetryExpr.get(), OpLoc, tok::arrow, TempSS, + TemplateKWLoc, ExtraArgs->Id, ExtraArgs->ObjCImpDecl, + ExtraArgs->HasTrailingLParen); + } + if (Trap.hasErrorOccurred()) + RetryExpr = ExprError(); + } + if (RetryExpr.isUsable()) { + Diag(OpLoc, diag::err_no_member_overloaded_arrow) + << MemberName << DC << FixItHint::CreateReplacement(OpLoc, "->"); + return RetryExpr; + } + } + Diag(R.getNameLoc(), diag::err_no_member) << MemberName << DC << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); @@ -1506,9 +1533,11 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, return move(Result); } + ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl, HasTrailingLParen}; Result = BuildMemberReferenceExpr(Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, - FirstQualifierInScope, R, TemplateArgs); + FirstQualifierInScope, R, TemplateArgs, + false, &ExtraArgs); } return move(Result); diff --git a/test/FixIt/fixit.cpp b/test/FixIt/fixit.cpp index ac1fb6397f..e6609d8a6c 100644 --- a/test/FixIt/fixit.cpp +++ b/test/FixIt/fixit.cpp @@ -217,3 +217,26 @@ class Foo { }; void Foo::SetBar(Bar bar) { bar_ = bar; } // expected-error {{must use 'enum' tag to refer to type 'Bar' in this scope}} } + +namespace arrow_suggest { + +template +class wrapped_ptr { + public: + wrapped_ptr(T* ptr) : ptr_(ptr) {} + T* operator->() { return ptr_; } + private: + T *ptr_; +}; + +class Worker { + public: + void DoSomething(); +}; + +void test() { + wrapped_ptr worker(new Worker); + worker.DoSomething(); // expected-error {{no member named 'DoSomething' in 'arrow_suggest::wrapped_ptr'; did you mean to use '->' instead of '.'?}} +} + +} // namespace arrow_suggest diff --git a/test/SemaCXX/arrow-operator.cpp b/test/SemaCXX/arrow-operator.cpp index 6535a0a2f2..173ff729fc 100644 --- a/test/SemaCXX/arrow-operator.cpp +++ b/test/SemaCXX/arrow-operator.cpp @@ -36,3 +36,31 @@ void f() Line_Segment(node1->Location()); // expected-error {{not a structure or union}} } } + + +namespace arrow_suggest { + +template +class wrapped_ptr { + public: + wrapped_ptr(T* ptr) : ptr_(ptr) {} + T* operator->() { return ptr_; } + void Check(); // expected-note {{'Check' declared here}} + private: + T *ptr_; +}; + +class Worker { + public: + void DoSomething(); + void Chuck(); +}; + +void test() { + wrapped_ptr worker(new Worker); + worker.DoSomething(); // expected-error {{no member named 'DoSomething' in 'arrow_suggest::wrapped_ptr'; did you mean to use '->' instead of '.'?}} + worker.DoSamething(); // expected-error {{no member named 'DoSamething' in 'arrow_suggest::wrapped_ptr'}} + worker.Chuck(); // expected-error {{no member named 'Chuck' in 'arrow_suggest::wrapped_ptr'; did you mean 'Check'?}} +} + +} // namespace arrow_suggest -- 2.40.0