r187504 - Improve the diagnostic experience, including adding recovery, for

Kaelyn Uhrain rikka at google.com
Wed Jul 31 10:38:24 PDT 2013


Author: rikka
Date: Wed Jul 31 12:38:24 2013
New Revision: 187504

URL: http://llvm.org/viewvc/llvm-project?rev=187504&view=rev
Log:
Improve the diagnostic experience, including adding recovery, for
changing '->' to '.' when there is no operator-> defined for a class.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaExprMember.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/SemaCXX/member-expr.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=187504&r1=187503&r2=187504&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Jul 31 12:38:24 2013
@@ -4233,6 +4233,8 @@ def err_typecheck_member_reference_sugge
   "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<

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=187504&r1=187503&r2=187504&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Jul 31 12:38:24 2013
@@ -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

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=187504&r1=187503&r2=187504&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Wed Jul 31 12:38:24 2013
@@ -5076,15 +5076,44 @@ Sema::ActOnStartCXXMemberReference(Scope
   //   [...] 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
           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();
   }
 

Modified: cfe/trunk/lib/Sema/SemaExprMember.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprMember.cpp?rev=187504&r1=187503&r2=187504&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprMember.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprMember.cpp Wed Jul 31 12:38:24 2013
@@ -1136,10 +1136,8 @@ Sema::LookupMemberExpr(LookupResult &R,
       //   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;

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=187504&r1=187503&r2=187504&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Wed Jul 31 12:38:24 2013
@@ -11463,7 +11463,8 @@ Sema::BuildCallToObjectOfClassType(Scope
 ///  (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,
   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()) {

Modified: cfe/trunk/test/SemaCXX/member-expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/member-expr.cpp?rev=187504&r1=187503&r2=187504&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/member-expr.cpp (original)
+++ cfe/trunk/test/SemaCXX/member-expr.cpp Wed Jul 31 12:38:24 2013
@@ -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}}
   }
 }





More information about the cfe-commits mailing list