diff options
| author | Richard Trieu <rtrieu@google.com> | 2015-04-11 01:53:13 +0000 |
|---|---|---|
| committer | Richard Trieu <rtrieu@google.com> | 2015-04-11 01:53:13 +0000 |
| commit | af7d76c7204c0a586fd38fff45ffe0bd284593b0 (patch) | |
| tree | 86b043ae62fd707397d6ad449866bad09a260c47 /clang/lib | |
| parent | d9a21bf93a07994f1564ae3941d95c667cbde448 (diff) | |
| download | bcm5719-llvm-af7d76c7204c0a586fd38fff45ffe0bd284593b0.tar.gz bcm5719-llvm-af7d76c7204c0a586fd38fff45ffe0bd284593b0.zip | |
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
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 144 |
1 files changed, 141 insertions, 3 deletions
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d6e6ab4ec86..e095ee71582 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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)) { @@ -9038,13 +9169,20 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { 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: |

