Improve the wording of the error message.  Make the error more informative by 
taking the information from the notes and moving it to the error message.

http://reviews.llvm.org/D4479

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaExpr.cpp
  test/CXX/temp/temp.decls/temp.mem/p5.cpp
  test/Sema/block-misc.c
  test/SemaCXX/anonymous-union.cpp
  test/SemaCXX/captured-statements.cpp
  test/SemaCXX/cxx0x-constexpr-const.cpp
  test/SemaCXX/err_typecheck_assign_const.cpp
  test/SemaCXX/function-type-qual.cpp
  test/SemaObjC/arc.m
  test/SemaTemplate/dependent-type-identity.cpp
Index: test/SemaObjC/arc.m
===================================================================
--- test/SemaObjC/arc.m
+++ test/SemaObjC/arc.m
@@ -293,8 +293,8 @@
     x = 0; // expected-error {{fast enumeration variables can't be modified in ARC by default; declare the variable __strong to allow this}}
   }
 
-  for (const id x in collection) {
-    x = 0; // expected-error {{read-only variable is not assignable}}
+  for (const id x in collection) { // expected-note {{variable 'x' declared const here}}
+    x = 0; // expected-error {{cannot assign to variable 'x' with const qualified type 'const __strong id'}}
   }
 
   for (__strong id x in collection) {
Index: test/Sema/block-misc.c
===================================================================
--- test/Sema/block-misc.c
+++ test/Sema/block-misc.c
@@ -184,8 +184,8 @@
 }
 
 void test18() {
-  void (^const  blockA)(void) = ^{ };
-  blockA = ^{ }; // expected-error {{read-only variable is not assignable}}
+  void (^const  blockA)(void) = ^{ };  // expected-note {{variable 'blockA' declared const here}}
+  blockA = ^{ }; // expected-error {{cannot assign to variable 'blockA' with const qualified type 'void (^const)(void)}}
 }
 
 // rdar://7072507
Index: test/CXX/temp/temp.decls/temp.mem/p5.cpp
===================================================================
--- test/CXX/temp/temp.decls/temp.mem/p5.cpp
+++ test/CXX/temp/temp.decls/temp.mem/p5.cpp
@@ -55,8 +55,8 @@
 // Partial ordering with conversion function templates.
 struct X0 {
   template<typename T> operator T*() {
-    T x = 1;
-    x = 17; // expected-error{{read-only variable is not assignable}}
+    T x = 1; // expected-note{{variable 'x' declared const here}}
+    x = 17; // expected-error{{cannot assign to variable 'x' with const qualified type 'const int'}}
   }
   
   template<typename T> operator T*() const; // expected-note{{explicit instantiation refers here}}
Index: test/SemaCXX/err_typecheck_assign_const.cpp
===================================================================
--- test/SemaCXX/err_typecheck_assign_const.cpp
+++ test/SemaCXX/err_typecheck_assign_const.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+
+const int global = 5;  // expected-note{{variable 'global' declared const here}}
+void test1() {
+  global = 2;  // expected-error{{cannot assign to variable 'global' with const qualified type 'const int'}}
+}
+
+void test2 () {
+  const int local = 5;  // expected-note{{variable 'local' declared const here}}
+  local = 0;  // expected-error{{cannot assign to variable 'local' with const qualified type 'const int'}}
+}
+
+void test2 (const int parameter) {  // expected-note{{variable 'parameter' declared const here}}
+  parameter = 2;  // expected-error{{cannot assign to variable 'parameter' with const qualified type 'const int'}}
+}
+
+class test3 {
+  int field;
+  const int const_field = 1;  // expected-note 2{{field 'const_field' declared const here}}
+  static const int static_const_field = 1;  // expected-note 2{{variable 'static_const_field' declared const here}}
+  void test() {
+    const_field = 4;  // expected-error{{cannot assign to const data member 'const_field' with const qualified type 'const int'}}
+    static_const_field = 4;  // expected-error{{cannot assign to variable 'static_const_field' with const qualified type 'const int'}}
+  }
+  void test_const() const { // expected-note 2{{member function 'test3::test_const' is declared const here}}
+    field = 4;  // expected-error{{cannot assign to data member within const member function 'test_const'}}
+    const_field = 4 ;  // expected-error{{cannot assign to const data member 'const_field' with const qualified type 'const int' in a const member function 'test_const'}}
+    static_const_field = 4;  // expected-error{{cannot assign to variable 'static_const_field' with const qualified type 'const int'}}
+  }
+};
+
+const int &return_const_ref();  // expected-note{{function 'return_const_ref' which returns const qualified type 'const int &' declared here}}
+
+void test4() {
+  return_const_ref() = 10;  // expected-error{{cannot assign to return value since function 'return_const_ref' returns a const value}}
+}
+
+struct S5 {
+  int field;
+  const int const_field = 4;  // expected-note {{field 'const_field' declared const here}}
+};
+
+void test5() {
+  S5 s5;
+  s5.field = 5;
+  s5.const_field = 5;  // expected-error{{cannot assign to const data member 'const_field' with const qualified type 'const int'}}
+}
+
+struct U1 {
+  int a = 5;
+};
+
+struct U2 {
+  U1 u1;
+};
+
+struct U3 {
+  const U2 u2 = U2();  // expected-note{{field 'u2' declared const here}}
+};
+
+struct U4 {
+  U3 u3;
+};
+
+void test6() {
+  U4 u4;
+  u4.u3.u2.u1.a = 5;  // expected-error{{cannot assign to const data member 'u2' with const qualified type 'const U2'}}
+}
Index: test/SemaCXX/cxx0x-constexpr-const.cpp
===================================================================
--- test/SemaCXX/cxx0x-constexpr-const.cpp
+++ test/SemaCXX/cxx0x-constexpr-const.cpp
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
 
-constexpr int x = 1;
+constexpr int x = 1;  // expected-note {{variable 'x' declared const here}}
 constexpr int id(int x) { return x; }
 
 void foo(void) {
-  x = 2; // expected-error {{read-only variable is not assignable}}
+  x = 2; // expected-error {{cannot assign to variable 'x' with const qualified type 'const int'}}
   int (*idp)(int) = id;
 }
 
Index: test/SemaCXX/function-type-qual.cpp
===================================================================
--- test/SemaCXX/function-type-qual.cpp
+++ test/SemaCXX/function-type-qual.cpp
@@ -17,8 +17,8 @@
     x = 0;
   }
 
-  void m2() const {
-    x = 0; // expected-error {{read-only variable is not assignable}}
+  void m2() const { // expected-note {{member function 'C::m2' is declared const here}}
+    x = 0; // expected-error {{cannot assign to data member within const member function 'm2'}}
   }
 
   int x;
Index: test/SemaCXX/anonymous-union.cpp
===================================================================
--- test/SemaCXX/anonymous-union.cpp
+++ test/SemaCXX/anonymous-union.cpp
@@ -39,10 +39,10 @@
   a = 0;
 }
 
-void X::test_unqual_references_const() const {
+void X::test_unqual_references_const() const { // expected-note 2{{member function 'X::test_unqual_references_const' is declared const here}}
   d = 0.0;
-  f2 = 0; // expected-error{{read-only variable is not assignable}}
-  a = 0; // expected-error{{read-only variable is not assignable}}
+  f2 = 0; // expected-error{{cannot assign to data member within const member function 'test_unqual_references_const'}}
+  a = 0; // expected-error{{cannot assign to data member within const member function 'test_unqual_references_const'}}
 }
 
 void test_unqual_references(X x, const X xc) {
Index: test/SemaCXX/captured-statements.cpp
===================================================================
--- test/SemaCXX/captured-statements.cpp
+++ test/SemaCXX/captured-statements.cpp
@@ -81,11 +81,11 @@
 }
 
 template <typename S, typename T>
-S template_capture_var(S x, T y) {
+S template_capture_var(S x, T y) {  // expected-note{{variable 'y' declared const here}}
   #pragma clang _debug captured
   {
     x++;
-    y++;  // expected-error{{read-only variable is not assignable}}
+    y++;  // expected-error{{cannot assign to variable 'y' with const qualified type 'const int'}}
   }
 
   return x;
Index: test/SemaTemplate/dependent-type-identity.cpp
===================================================================
--- test/SemaTemplate/dependent-type-identity.cpp
+++ test/SemaTemplate/dependent-type-identity.cpp
@@ -111,7 +111,9 @@
   void A<T>::f(int x) { x = 0; }
 
   template<typename T>
-  void A<T>::g(const int x) { x = 0; } // expected-error {{not assignable}}
+  void A<T>::g(const int x) {  // expected-note {{declared const here}}
+    x = 0; // expected-error {{cannot assign to variable 'x'}}
+  }
 
   template<typename T>
   void A<T>::h(T) {} // FIXME: Should reject this. Type is different from prior decl if T is an array type.
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -4811,7 +4811,25 @@
 def ext_typecheck_comparison_of_distinct_pointers_nonstandard : ExtWarn<
   "comparison of distinct pointer types (%0 and %1) uses non-standard "
   "composite pointer type %2">, InGroup<CompareDistinctPointerType>;
-def err_typecheck_assign_const : Error<"read-only variable is not assignable">;
+
+def err_typecheck_assign_const : Error<
+  "%select{read-only variable is not assignable|"
+  "cannot assign to return value since function %1 returns a const value|"
+  "cannot assign to variable %1 with const qualified type %2|"
+  "cannot assign to const data member %1 with const qualified type %2|"
+  "cannot assign to data member within const member function %1|"
+  "cannot assign to const data member %1 with const qualified type %2 in a "
+  "const member function %3}0">;
+
+def note_function_returns_const_ref : Note<
+  "function %0 which returns const qualified type %1 declared here">;
+def note_const_variable : Note<
+  "variable %0 declared const here">;
+def note_const_field : Note<
+  "field %0 declared const here">;
+def note_const_method : Note<
+  "member function %q0 is declared const here">;
+
 def err_stmtexpr_file_scope : Error<
   "statement expression not allowed at file scope">;
 def warn_mixed_sign_comparison : Warning<
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -8482,6 +8482,118 @@
   return (isa<BlockDecl>(DC) ? NCCK_Block : NCCK_Lambda);
 }
 
+enum {
+  ConstUnknown,
+  ConstFunction,
+  ConstVariable,
+  ConstField,
+  ConstMethod,
+  ConstFieldAndMethod
+};
+
+// Emit the "read-only variable not assignable" error and print notes to give
+// more information about why the variable is not assignable, such as pointing
+// to the declaration of a const variable, showing that a method is const, or
+// that the funciton is returning a const reference.
+static void DiagnoseConstAssignment(Sema &S, const Expr *E,
+                                    SourceLocation Loc) {
+  PartialDiagnostic PD = S.PDiag(diag::err_typecheck_assign_const)
+                         << E->getSourceRange();
+
+  const FunctionDecl *FD = nullptr;
+  const ValueDecl *VD = nullptr;
+  const MemberExpr *ME = nullptr;
+  const CXXMethodDecl *MD = nullptr;
+
+  E = E->IgnoreParens();
+
+  // If a call expression is found, point to function declaraion.
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+    FD = CE->getDirectCallee();
+  }
+
+  // Point to variable declaration.
+  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+    VD = DRE->getDecl();
+    if (VD && !VD->getType().isConstQualified())
+      VD = nullptr;
+  }
+
+  // Handle class fields and methods.
+  if (const MemberExpr *Member = dyn_cast<MemberExpr>(E)) {
+    const Expr *Base = E;
+    while (Member) {
+      ValueDecl *VD = Member->getMemberDecl();
+      // Class field is const.
+      if (VD->getType().isConstQualified()) {
+        ME = Member;
+        break;
+      }
+      Base = Member->getBase();
+      Member = dyn_cast<MemberExpr>(Base);
+    }
+
+    while (Member) {
+      Base = Member->getBase();
+      Member = dyn_cast<MemberExpr>(Base);
+    }
+
+    // Class method is const.
+    if (isa<CXXThisExpr>(Base)) {
+      if (const DeclContext *DC = S.getFunctionLevelDeclContext()) {
+        MD = dyn_cast<CXXMethodDecl>(DC);
+        if (MD && !MD->isConst()) {
+          MD = nullptr;
+        }
+      }
+    }
+  }
+
+  if (FD) {
+    PD << ConstFunction << FD;
+  } else if (VD) {
+    PD << ConstVariable << VD << VD->getType();
+  } else if (ME && MD) {
+    PD << ConstFieldAndMethod << ME->getMemberDecl() << ME->getType() << MD;
+  } else if (ME) {
+    PD << ConstField << ME->getMemberDecl() << ME->getType();
+  } else if (MD) {
+    PD << ConstMethod << MD;
+  } else {
+    PD << ConstUnknown;
+  }
+
+  S.Diag(Loc, PD);
+
+  if (FD) {
+    S.Diag(FD->getReturnTypeSourceRange().getBegin(),
+           diag::note_function_returns_const_ref)
+        << FD << FD->getReturnType() << FD->getReturnTypeSourceRange();
+  }
+
+  if (VD) {
+    S.Diag(VD->getLocation(), diag::note_const_variable)
+        << VD << VD->getType() << VD->getSourceRange();
+  }
+
+  if (ME) {
+    while (ME) {
+      ValueDecl *VD = ME->getMemberDecl();
+      if (VD->getType().isConstQualified()) {
+        S.Diag(VD->getLocation(), diag::note_const_field)
+            << VD << VD->getType() << VD->getSourceRange();
+      }
+      ME = dyn_cast<MemberExpr>(ME->getBase());
+    }
+  }
+
+  if (MD) {
+    S.Diag(MD->getLocation(), diag::note_const_method) << MD
+                                                       << MD->getSourceRange();
+  }
+}
+
+
 /// CheckForModifiableLvalue - Verify that E is a modifiable lvalue.  If not,
 /// emit an error and return true.  If so, return false.
 static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
@@ -8498,8 +8610,6 @@
   bool NeedType = false;
   switch (IsLV) { // C99 6.5.16p2
   case Expr::MLV_ConstQualified:
-    Diag = diag::err_typecheck_assign_const;
-
     // Use a specialized diagnostic when we're assigning to an object
     // from an enclosing function or block.
     if (NonConstCaptureKind NCCK = isReferenceToNonConstCapture(S, E)) {
@@ -8545,6 +8655,13 @@
       }
     }
 
+    // If none of the special cases above are triggered, then this is a
+    // simple const assignment.
+    if (Diag == 0) {
+      DiagnoseConstAssignment(S, E, Loc);
+      return true;
+    }
+
     break;
   case Expr::MLV_ArrayType:
   case Expr::MLV_ArrayTemporary:
@@ -8589,6 +8706,7 @@
     S.Diag(Loc, Diag) << E->getType() << E->getSourceRange() << Assign;
   else
     S.Diag(Loc, Diag) << E->getSourceRange() << Assign;
+
   return true;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to