Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -5030,7 +5030,23 @@ "%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, DefaultIgnore; Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -8982,6 +8982,139 @@ return (isa(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(E)) { + NextIsDereference = ME->isArrow(); + const ValueDecl *VD = ME->getMemberDecl(); + if (const FieldDecl *Field = dyn_cast(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(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(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(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(E)) { + if (const DeclContext *DC = S.getFunctionLevelDeclContext()) { + if (const CXXMethodDecl *MD = dyn_cast(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 @@ 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 @@ 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: Index: cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp =================================================================== --- cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp +++ cfe/trunk/test/CXX/temp/temp.decls/temp.mem/p5.cpp @@ -55,8 +55,8 @@ // Partial ordering with conversion function templates. struct X0 { template 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 operator T*() const; // expected-note{{explicit instantiation refers here}} Index: cfe/trunk/test/Sema/anonymous-struct-union.c =================================================================== --- cfe/trunk/test/Sema/anonymous-struct-union.c +++ cfe/trunk/test/Sema/anonymous-struct-union.c @@ -22,6 +22,7 @@ }; void test_unqual_references(struct X x, const struct X xc) { + // expected-note@-1 3{{variable 'xc' declared const here}} x.i = 0; x.f = 0.0; x.f2 = x.f; @@ -29,9 +30,9 @@ 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'}} } Index: cfe/trunk/test/Sema/assign.c =================================================================== --- cfe/trunk/test/Sema/assign.c +++ cfe/trunk/test/Sema/assign.c @@ -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@-1 {{variable 'x' declared const here}} + + x->a = 10; + // expected-error-re@-1 {{cannot assign to variable 'x' with const-qualified type 'const struct (anonymous struct at {{.*}}assign.c:5:19) *'}} } typedef int arr[10]; Index: cfe/trunk/test/Sema/block-misc.c =================================================================== --- cfe/trunk/test/Sema/block-misc.c +++ cfe/trunk/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: cfe/trunk/test/SemaCXX/anonymous-union.cpp =================================================================== --- cfe/trunk/test/SemaCXX/anonymous-union.cpp +++ cfe/trunk/test/SemaCXX/anonymous-union.cpp @@ -39,13 +39,14 @@ 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@-1 2{{variable 'xc' declared const here}} x.i = 0; x.f = 0.0; x.f2 = x.f; @@ -54,8 +55,8 @@ 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'}} } Index: cfe/trunk/test/SemaCXX/captured-statements.cpp =================================================================== --- cfe/trunk/test/SemaCXX/captured-statements.cpp +++ cfe/trunk/test/SemaCXX/captured-statements.cpp @@ -81,11 +81,11 @@ } template -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: cfe/trunk/test/SemaCXX/cxx0x-constexpr-const.cpp =================================================================== --- cfe/trunk/test/SemaCXX/cxx0x-constexpr-const.cpp +++ cfe/trunk/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: cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp =================================================================== --- cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp +++ cfe/trunk/test/SemaCXX/err_typecheck_assign_const.cpp @@ -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 &'}} +} Index: cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp =================================================================== --- cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp +++ cfe/trunk/test/SemaCXX/err_typecheck_assign_const_filecheck.cpp @@ -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: +} Index: cfe/trunk/test/SemaCXX/function-type-qual.cpp =================================================================== --- cfe/trunk/test/SemaCXX/function-type-qual.cpp +++ cfe/trunk/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 non-static data member within const member function 'm2'}} } int x; Index: cfe/trunk/test/SemaObjC/arc.m =================================================================== --- cfe/trunk/test/SemaObjC/arc.m +++ cfe/trunk/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: cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp =================================================================== --- cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp +++ cfe/trunk/test/SemaTemplate/dependent-type-identity.cpp @@ -111,7 +111,9 @@ void A::f(int x) { x = 0; } template - void A::g(const int x) { x = 0; } // expected-error {{not assignable}} + void A::g(const int x) { // expected-note {{declared const here}} + x = 0; // expected-error {{cannot assign to variable 'x'}} + } template void A::h(T) {} // FIXME: Should reject this. Type is different from prior decl if T is an array type.