r234677 - Improve the error message for assigning to read-only variables.

Richard Trieu rtrieu at google.com
Fri Apr 10 18:53:13 PDT 2015


Author: rtrieu
Date: Fri Apr 10 20:53:13 2015
New Revision: 234677

URL: http://llvm.org/viewvc/llvm-project?rev=234677&view=rev
Log:
Improve the error message for assigning to read-only variables.

Previously, many error messages would simply be "read-only variable is not
assignable"  This change provides more information about why the variable is
not assignable, as well as note to where the const is located.

Differential Revision: http://reviews.llvm.org/D4479


Added:
    cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp
    cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp
    cfe/trunk/test/Sema/anonymous-struct-union.c
    cfe/trunk/test/Sema/assign.c
    cfe/trunk/test/Sema/block-misc.c
    cfe/trunk/test/SemaCXX/anonymous-union.cpp
    cfe/trunk/test/SemaCXX/captured-statements.cpp
    cfe/trunk/test/SemaCXX/cxx0x-constexpr-const.cpp
    cfe/trunk/test/SemaCXX/function-type-qual.cpp
    cfe/trunk/test/SemaObjC/arc.m
    cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Apr 10 20:53:13 2015
@@ -5030,7 +5030,23 @@ def err_typecheck_op_on_nonoverlapping_a
   "%select{comparison between %diff{ ($ and $)|}0,1"
   "|arithmetic operation with operands of type %diff{ ($ and $)|}0,1}2"
   " which are pointers to non-overlapping address spaces">;
-def err_typecheck_assign_const : Error<"read-only variable is not assignable">;
+
+def err_typecheck_assign_const : Error<
+  "%select{"
+  "cannot assign to return value because function %1 returns a const value|"
+  "cannot assign to variable %1 with const-qualified type %2|"
+  "cannot assign to %select{non-|}1static data member %2 "
+  "with const-qualified type %3|"
+  "cannot assign to non-static data member within const member function %1|"
+  "read-only variable is not assignable}0">;
+
+def note_typecheck_assign_const : Note<
+  "%select{"
+  "function %1 which returns const-qualified type %2 declared here|"
+  "variable %1 declared const here|"
+  "%select{non-|}1static data member %2 declared const here|"
+  "member function %q1 is declared const here}0">;
+
 def warn_mixed_sign_comparison : Warning<
   "comparison of integers of different signs: %0 and %1">,
   InGroup<SignCompare>, DefaultIgnore;

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri Apr 10 20:53:13 2015
@@ -8982,6 +8982,139 @@ static NonConstCaptureKind isReferenceTo
   return (isa<BlockDecl>(DC) ? NCCK_Block : NCCK_Lambda);
 }
 
+static bool IsTypeModifiable(QualType Ty, bool IsDereference) {
+  Ty = Ty.getNonReferenceType();
+  if (IsDereference && Ty->isPointerType())
+    Ty = Ty->getPointeeType();
+  return !Ty.isConstQualified();
+}
+
+/// 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 function is returning a const reference.
+static void DiagnoseConstAssignment(Sema &S, const Expr *E,
+                                    SourceLocation Loc) {
+  // Update err_typecheck_assign_const and note_typecheck_assign_const
+  // when this enum is changed.
+  enum {
+    ConstFunction,
+    ConstVariable,
+    ConstMember,
+    ConstMethod,
+    ConstUnknown,  // Keep as last element
+  };
+
+  SourceRange ExprRange = E->getSourceRange();
+
+  // Only emit one error on the first const found.  All other consts will emit
+  // a note to the error.
+  bool DiagnosticEmitted = false;
+
+  // Track if the current expression is the result of a derefence, and if the
+  // next checked expression is the result of a derefence.
+  bool IsDereference = false;
+  bool NextIsDereference = false;
+
+  // Loop to process MemberExpr chains.
+  while (true) {
+    IsDereference = NextIsDereference;
+    NextIsDereference = false;
+
+    E = E->IgnoreParenImpCasts();
+    if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
+      NextIsDereference = ME->isArrow();
+      const ValueDecl *VD = ME->getMemberDecl();
+      if (const FieldDecl *Field = dyn_cast<FieldDecl>(VD)) {
+        // Mutable fields can be modified even if the class is const.
+        if (Field->isMutable()) {
+          assert(DiagnosticEmitted && "Expected diagnostic not emitted.");
+          break;
+        }
+
+        if (!IsTypeModifiable(Field->getType(), IsDereference)) {
+          if (!DiagnosticEmitted) {
+            S.Diag(Loc, diag::err_typecheck_assign_const)
+                << ExprRange << ConstMember << false /*static*/ << Field
+                << Field->getType();
+            DiagnosticEmitted = true;
+          }
+          S.Diag(VD->getLocation(), diag::note_typecheck_assign_const)
+              << ConstMember << false /*static*/ << Field << Field->getType()
+              << Field->getSourceRange();
+        }
+        E = ME->getBase();
+        continue;
+      } else if (const VarDecl *VDecl = dyn_cast<VarDecl>(VD)) {
+        if (VDecl->getType().isConstQualified()) {
+          if (!DiagnosticEmitted) {
+            S.Diag(Loc, diag::err_typecheck_assign_const)
+                << ExprRange << ConstMember << true /*static*/ << VDecl
+                << VDecl->getType();
+            DiagnosticEmitted = true;
+          }
+          S.Diag(VD->getLocation(), diag::note_typecheck_assign_const)
+              << ConstMember << true /*static*/ << VDecl << VDecl->getType()
+              << VDecl->getSourceRange();
+        }
+        // Static fields do not inherit constness from parents.
+        break;
+      }
+      break;
+    } // End MemberExpr
+    break;
+  }
+
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+    // Function calls
+    const FunctionDecl *FD = CE->getDirectCallee();
+    if (!IsTypeModifiable(FD->getReturnType(), IsDereference)) {
+      if (!DiagnosticEmitted) {
+        S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange
+                                                      << ConstFunction << FD;
+        DiagnosticEmitted = true;
+      }
+      S.Diag(FD->getReturnTypeSourceRange().getBegin(),
+             diag::note_typecheck_assign_const)
+          << ConstFunction << FD << FD->getReturnType()
+          << FD->getReturnTypeSourceRange();
+    }
+  } else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+    // Point to variable declaration.
+    if (const ValueDecl *VD = DRE->getDecl()) {
+      if (!IsTypeModifiable(VD->getType(), IsDereference)) {
+        if (!DiagnosticEmitted) {
+          S.Diag(Loc, diag::err_typecheck_assign_const)
+              << ExprRange << ConstVariable << VD << VD->getType();
+          DiagnosticEmitted = true;
+        }
+        S.Diag(VD->getLocation(), diag::note_typecheck_assign_const)
+            << ConstVariable << VD << VD->getType() << VD->getSourceRange();
+      }
+    }
+  } else if (isa<CXXThisExpr>(E)) {
+    if (const DeclContext *DC = S.getFunctionLevelDeclContext()) {
+      if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC)) {
+        if (MD->isConst()) {
+          if (!DiagnosticEmitted) {
+            S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange
+                                                          << ConstMethod << MD;
+            DiagnosticEmitted = true;
+          }
+          S.Diag(MD->getLocation(), diag::note_typecheck_assign_const)
+              << ConstMethod << MD << MD->getSourceRange();
+        }
+      }
+    }
+  }
+
+  if (DiagnosticEmitted)
+    return;
+
+  // Can't determine a more specific message, so display the generic error.
+  S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange << ConstUnknown;
+}
+
 /// 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) {
@@ -8998,8 +9131,6 @@ static bool CheckForModifiableLvalue(Exp
   bool NeedType = false;
   switch (IsLV) { // C99 6.5.16p2
   case Expr::MLV_ConstQualified:
-    DiagID = 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)) {
@@ -9038,13 +9169,20 @@ static bool CheckForModifiableLvalue(Exp
           if (Loc != OrigLoc)
             Assign = SourceRange(OrigLoc, OrigLoc);
           S.Diag(Loc, DiagID) << E->getSourceRange() << Assign;
-          // We need to preserve the AST regardless, so migration tool 
+          // We need to preserve the AST regardless, so migration tool
           // can do its job.
           return false;
         }
       }
     }
 
+    // If none of the special cases above are triggered, then this is a
+    // simple const assignment.
+    if (DiagID == 0) {
+      DiagnoseConstAssignment(S, E, Loc);
+      return true;
+    }
+
     break;
   case Expr::MLV_ArrayType:
   case Expr::MLV_ArrayTemporary:

Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp Fri Apr 10 20:53:13 2015
@@ -55,8 +55,8 @@ template double Foo::As2();
 // 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}}

Modified: cfe/trunk/test/Sema/anonymous-struct-union.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/anonymous-struct-union.c?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/Sema/anonymous-struct-union.c (original)
+++ cfe/trunk/test/Sema/anonymous-struct-union.c Fri Apr 10 20:53:13 2015
@@ -22,6 +22,7 @@ struct X {
 };
 
 void test_unqual_references(struct X x, const struct X xc) {
+  // expected-note at -1 3{{variable 'xc' declared const here}}
   x.i = 0;
   x.f = 0.0;
   x.f2 = x.f;
@@ -29,9 +30,9 @@ void test_unqual_references(struct X x,
   x.f3 = 0; // expected-error{{no member named 'f3'}}
   x.a = 0;
 
-  xc.d = 0.0; // expected-error{{read-only variable is not assignable}}
-  xc.f = 0; // expected-error{{read-only variable is not assignable}}
-  xc.a = 0; // expected-error{{read-only variable is not assignable}}
+  xc.d = 0.0; // expected-error{{cannot assign to variable 'xc' with const-qualified type 'const struct X'}}
+  xc.f = 0; // expected-error{{cannot assign to variable 'xc' with const-qualified type 'const struct X'}}
+  xc.a = 0; // expected-error{{cannot assign to variable 'xc' with const-qualified type 'const struct X'}}
 }
 
 

Modified: cfe/trunk/test/Sema/assign.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/assign.c?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/Sema/assign.c (original)
+++ cfe/trunk/test/Sema/assign.c Fri Apr 10 20:53:13 2015
@@ -3,7 +3,10 @@
 void *test1(void) { return 0; }
 
 void test2 (const struct {int a;} *x) {
-  x->a = 10; // expected-error {{read-only variable is not assignable}}
+  // expected-note at -1 {{variable 'x' declared const here}}
+
+  x->a = 10;
+  // expected-error-re at -1 {{cannot assign to variable 'x' with const-qualified type 'const struct (anonymous struct at {{.*}}assign.c:5:19) *'}}
 }
 
 typedef int arr[10];

Modified: cfe/trunk/test/Sema/block-misc.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/block-misc.c?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/Sema/block-misc.c (original)
+++ cfe/trunk/test/Sema/block-misc.c Fri Apr 10 20:53:13 2015
@@ -184,8 +184,8 @@ void test17() {
 }
 
 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

Modified: cfe/trunk/test/SemaCXX/anonymous-union.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/anonymous-union.cpp?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/anonymous-union.cpp (original)
+++ cfe/trunk/test/SemaCXX/anonymous-union.cpp Fri Apr 10 20:53:13 2015
@@ -39,13 +39,14 @@ void X::test_unqual_references() {
   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 non-static data member within const member function 'test_unqual_references_const'}}
+  a = 0; // expected-error{{cannot assign to non-static data member within const member function 'test_unqual_references_const'}}
 }
 
 void test_unqual_references(X x, const X xc) {
+  // expected-note at -1 2{{variable 'xc' declared const here}}
   x.i = 0;
   x.f = 0.0;
   x.f2 = x.f;
@@ -54,8 +55,8 @@ void test_unqual_references(X x, const X
   x.a = 0;
 
   xc.d = 0.0;
-  xc.f = 0; // expected-error{{read-only variable is not assignable}}
-  xc.a = 0; // expected-error{{read-only variable is not assignable}}
+  xc.f = 0; // expected-error{{cannot assign to variable 'xc' with const-qualified type 'const X'}}
+  xc.a = 0; // expected-error{{cannot assign to variable 'xc' with const-qualified type 'const X'}}
 }
 
 

Modified: cfe/trunk/test/SemaCXX/captured-statements.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/captured-statements.cpp?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/captured-statements.cpp (original)
+++ cfe/trunk/test/SemaCXX/captured-statements.cpp Fri Apr 10 20:53:13 2015
@@ -81,11 +81,11 @@ void test_capture_var() {
 }
 
 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;

Modified: cfe/trunk/test/SemaCXX/cxx0x-constexpr-const.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-constexpr-const.cpp?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx0x-constexpr-const.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx0x-constexpr-const.cpp Fri Apr 10 20:53:13 2015
@@ -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;
 }
 

Added: cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp?rev=234677&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp (added)
+++ cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp Fri Apr 10 20:53:13 2015
@@ -0,0 +1,124 @@
+// 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{{non-static data member '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 non-static 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 non-static data member within const member function 'test_const'}}
+    const_field = 4 ;  // expected-error{{cannot assign to non-static 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'}}
+  }
+};
+
+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 because function 'return_const_ref' returns a const value}}
+}
+
+struct S5 {
+  int field;
+  const int const_field = 4;  // expected-note {{non-static data member 'const_field' declared const here}}
+};
+
+void test5() {
+  S5 s5;
+  s5.field = 5;
+  s5.const_field = 5;  // expected-error{{cannot assign to non-static 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{{non-static data member 'u2' declared const here}}
+};
+
+struct U4 {
+  U3 u3;
+};
+
+void test6() {
+  U4 u4;
+  u4.u3.u2.u1.a = 5;  // expected-error{{cannot assign to non-static data member 'u2' with const-qualified type 'const U2'}}
+}
+
+struct A {
+  int z;
+};
+struct B {
+  A a;
+};
+struct C {
+  B b;
+  C();
+};
+const C &getc(); // expected-note{{function 'getc' which returns const-qualified type 'const C &' declared here}}
+void test7() {
+  const C c;    // expected-note{{variable 'c' declared const here}}
+  c.b.a.z = 5;  // expected-error{{cannot assign to variable 'c' with const-qualified type 'const C'}}
+
+  getc().b.a.z = 5;  // expected-error{{cannot assign to return value because function 'getc' returns a const value}}
+}
+
+struct D { const int n; };  // expected-note 2{{non-static data member 'n' declared const here}}
+struct E { D *const d = 0; };
+void test8() {
+  extern D *const d;
+  d->n = 0;  // expected-error{{cannot assign to non-static data member 'n' with const-qualified type 'const int'}}
+
+  E e;
+  e.d->n = 0;  // expected-error{{cannot assign to non-static data member 'n' with const-qualified type 'const int'}}
+}
+
+struct F { int n; };
+struct G { const F *f; };  // expected-note{{non-static data member 'f' declared const here}}
+void test10() {
+  const F *f;  // expected-note{{variable 'f' declared const here}}
+  f->n = 0;    // expected-error{{cannot assign to variable 'f' with const-qualified type 'const F *'}}
+
+  G g;
+  g.f->n = 0;  // expected-error{{cannot assign to non-static data member 'f' with const-qualified type 'const F *'}}
+}
+
+void test11(
+    const int x,  // expected-note{{variable 'x' declared const here}}
+    const int& y  // expected-note{{variable 'y' declared const here}}
+    ) {
+  x = 5;  // expected-error{{cannot assign to variable 'x' with const-qualified type 'const int'}}
+  y = 5;  // expected-error{{cannot assign to variable 'y' with const-qualified type 'const int &'}}
+}
+
+struct H {
+  const int a = 0;   // expected-note{{non-static data member 'a' declared const here}}
+  const int &b = a;  // expected-note{{non-static data member 'b' declared const here}}
+};
+
+void test12(H h) {
+  h.a = 1;  // expected-error {{cannot assign to non-static data member 'a' with const-qualified type 'const int'}}
+  h.b = 2;  // expected-error {{cannot assign to non-static data member 'b' with const-qualified type 'const int &'}}
+}

Added: cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp?rev=234677&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp (added)
+++ cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp Fri Apr 10 20:53:13 2015
@@ -0,0 +1,252 @@
+// RUN: not %clang_cc1 -fsyntax-only -std=c++11 %s 2>&1 | FileCheck %s
+
+struct E {
+  int num;
+  const int Cnum = 0;
+  mutable int Mnum;
+  static int Snum;
+  const static int CSnum;
+};
+
+struct D {
+  E e;
+  const E Ce;
+  mutable E Me;
+  static E Se;
+  const static E CSe;
+  E &getE() const;
+  const E &getCE() const;
+};
+
+struct C {
+  D d;
+  const D Cd;
+  mutable D Md;
+  static D Sd;
+  const static D CSd;
+  D &getD() const;
+  const D &getCD() const;
+};
+
+struct B {
+  C c;
+  const C Cc;
+  mutable C Mc;
+  static C Sc;
+  const static C CSc;
+  C &getC() const;
+  static C &getSC();
+  const C &getCC() const;
+  static const C &getSCC();
+};
+
+struct A {
+  B b;
+  const B Cb;
+  mutable B Mb;
+  static B Sb;
+  const static B CSb;
+  B &getB() const;
+  static B &getSB();
+  const B &getCB() const;
+  static const B &getSCB();
+};
+
+A& getA();
+
+// Valid assignment
+void test1(A a, const A Ca) {
+  a.b.c.d.e.num = 5;
+  a.b.c.d.e.Mnum = 5;
+  Ca.b.c.d.e.Mnum = 5;
+  a.b.c.d.e.Snum = 5;
+  Ca.b.c.d.e.Snum = 5;
+  Ca.b.c.Md.e.num = 5;
+  Ca.Mb.Cc.d.e.Mnum = 5;
+  Ca.Mb.getC().d.e.num = 5;
+  Ca.getSB().c.d.e.num = 5;
+  a.getSCB().c.d.Me.num = 5;
+  Ca.Cb.Cc.Cd.Ce.Snum = 5;
+  // CHECK-NOT: error:
+  // CHECK-NOT: note:
+}
+
+// One note
+void test2(A a, const A Ca) {
+  Ca.b.c.d.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Ca'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Ca'
+  // CHECK-NOT: note:
+
+  a.Cb.c.d.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cb'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cb'
+  // CHECK-NOT: note:
+
+  a.b.c.Cd.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+
+  a.b.c.d.e.CSnum = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'CSnum'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'CSnum'
+  // CHECK-NOT: note:
+
+  a.b.c.d.e.Cnum = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cnum'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cnum'
+  // CHECK-NOT: note:
+
+  a.getCB().c.d.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'getCB'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'getCB'
+  // CHECK-NOT: note:
+
+  a.getSCB().c.d.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'getSCB'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'getSCB'
+  // CHECK-NOT: note:
+}
+
+// Two notes
+void test3(A a, const A Ca) {
+
+  a.getSCB().Cc.d.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cc'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cc'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'getSCB'
+  // CHECK-NOT: note:
+
+  Ca.b.c.Cd.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Ca'
+  // CHECK-NOT: note:
+
+  a.getCB().c.Cd.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'getCB'
+  // CHECK-NOT: note:
+
+  a.b.getCC().d.e.Cnum = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cnum'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cnum'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'getCC'
+  // CHECK-NOT: note:
+
+  a.b.c.Cd.Ce.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+
+  a.b.CSc.Cd.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'CSc'
+  // CHECK-NOT: note:
+
+  a.CSb.c.Cd.e.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Cd'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'CSb'
+  // CHECK-NOT: note:
+}
+
+// No errors
+void test4(const A Ca) {
+  // Mutable cases
+  Ca.Mb.c.d.e.num = 5;
+  Ca.CSb.Mc.d.e.num = 5;
+  Ca.getCB().Mc.d.e.num = 5;
+  Ca.getSCB().Mc.d.e.num = 5;
+
+  // Returning non-const reference
+  Ca.getB().c.d.e.num = 5;
+  Ca.CSb.getC().d.e.num = 5;
+  Ca.getCB().getC().d.e.num = 5;
+  Ca.getSCB().getC().d.e.num = 5;
+
+  // Returning non-const reference
+  Ca.getSB().c.d.e.num = 5;
+  Ca.CSb.getSC().d.e.num = 5;
+  Ca.getCB().getSC().d.e.num = 5;
+  Ca.getSCB().getSC().d.e.num = 5;
+
+  // Static member
+  Ca.Sb.c.d.e.num = 5;
+  Ca.CSb.Sc.d.e.num = 5;
+  Ca.getCB().Sc.d.e.num = 5;
+  Ca.getSCB().Sc.d.e.num = 5;
+
+  // CHECK-NOT: error:
+  // CHECK-NOT: note:
+}
+
+// Only display notes for relavent cases.
+void test5(const A Ca) {
+  Ca.Mb.c.d.Ce.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+
+  Ca.getB().c.d.Ce.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+
+  Ca.getSB().c.d.Ce.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+
+  Ca.Sb.c.d.Ce.num = 5;
+  // CHECK-NOT: error:
+  // CHECK: error:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+  // CHECK: note:{{.*}} 'Ce'
+  // CHECK-NOT: note:
+}

Modified: cfe/trunk/test/SemaCXX/function-type-qual.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/function-type-qual.cpp?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/function-type-qual.cpp (original)
+++ cfe/trunk/test/SemaCXX/function-type-qual.cpp Fri Apr 10 20:53:13 2015
@@ -17,8 +17,8 @@ class C {
     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 non-static data member within const member function 'm2'}}
   }
 
   int x;

Modified: cfe/trunk/test/SemaObjC/arc.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc.m?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/arc.m (original)
+++ cfe/trunk/test/SemaObjC/arc.m Fri Apr 10 20:53:13 2015
@@ -293,8 +293,8 @@ void test12(id collection) {
     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) {

Modified: cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp?rev=234677&r1=234676&r2=234677&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp (original)
+++ cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp Fri Apr 10 20:53:13 2015
@@ -111,7 +111,9 @@ namespace PR18275 {
   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.





More information about the cfe-commits mailing list