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 llvm-svn: 234677
This commit is contained in:
parent
d9a21bf93a
commit
af7d76c720
|
@ -5030,7 +5030,23 @@ def err_typecheck_op_on_nonoverlapping_address_space_pointers : Error<
|
|||
"%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;
|
||||
|
|
|
@ -8982,6 +8982,139 @@ static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) {
|
|||
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(Expr *E, SourceLocation Loc, Sema &S) {
|
|||
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)) {
|
||||
|
@ -9045,6 +9176,13 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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:
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -22,6 +22,7 @@ struct X {
|
|||
};
|
||||
|
||||
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 @@ void test_unqual_references(struct X x, const struct X xc) {
|
|||
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'}}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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@-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 xc) {
|
|||
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'}}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 &'}}
|
||||
}
|
|
@ -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:
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue