diff options
author | Reid Kleckner <rnk@google.com> | 2016-04-29 00:37:43 +0000 |
---|---|---|
committer | Reid Kleckner <rnk@google.com> | 2016-04-29 00:37:43 +0000 |
commit | f463a8a424e7e1c141aa7d9b96f1898183d2b680 (patch) | |
tree | 60e74fb87802d817cfeb1df221cc7207360387d3 /clang/lib/Sema/SemaDecl.cpp | |
parent | b87faffdb9bc86c4c08224bdbaa37096edcfda4d (diff) | |
download | bcm5719-llvm-f463a8a424e7e1c141aa7d9b96f1898183d2b680.tar.gz bcm5719-llvm-f463a8a424e7e1c141aa7d9b96f1898183d2b680.zip |
Avoid -Wshadow warnings about constructor parameters named after fields
Usually these parameters are used solely to initialize the field in the
initializer list, and there is no real shadowing confusion.
There is a new warning under -Wshadow called
-Wshadow-field-in-constructor-modified. It attempts to find
modifications of such constructor parameters that probably intended to
modify the field.
It has some false negatives, though, so there is another warning group,
-Wshadow-field-in-constructor, which always warns on this special case.
For users who just want the old behavior and don't care about these fine
grained groups, we have a new warning group called -Wshadow-all that
activates everything.
Fixes PR16088.
Reviewers: rsmith
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D18271
llvm-svn: 267957
Diffstat (limited to 'clang/lib/Sema/SemaDecl.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 80 |
1 files changed, 61 insertions, 19 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 03f235d4399..0c8781c0233 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1609,9 +1609,19 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) { // If this was a forward reference to a label, verify it was defined. if (LabelDecl *LD = dyn_cast<LabelDecl>(D)) CheckPoppedLabel(LD, *this); - - // Remove this name from our lexical scope. + + // Remove this name from our lexical scope, and warn on it if we haven't + // already. IdResolver.RemoveDecl(D); + auto ShadowI = ShadowingDecls.find(D); + if (ShadowI != ShadowingDecls.end()) { + if (const auto *FD = dyn_cast<FieldDecl>(ShadowI->second)) { + Diag(D->getLocation(), diag::warn_ctor_parm_shadows_field) + << D << FD << FD->getParent(); + Diag(FD->getLocation(), diag::note_previous_declaration); + } + ShadowingDecls.erase(ShadowI); + } } } @@ -6382,6 +6392,17 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, return NewVD; } +/// Enum describing the %select options in diag::warn_decl_shadow. +enum ShadowedDeclKind { SDK_Local, SDK_Global, SDK_StaticMember, SDK_Field }; + +/// Determine what kind of declaration we're shadowing. +static ShadowedDeclKind computeShadowedDeclKind(const NamedDecl *ShadowedDecl, + const DeclContext *OldDC) { + if (isa<RecordDecl>(OldDC)) + return isa<FieldDecl>(ShadowedDecl) ? SDK_Field : SDK_StaticMember; + return OldDC->isFileContext() ? SDK_Global : SDK_Local; +} + /// \brief Diagnose variable or built-in function shadowing. Implements /// -Wshadow. /// @@ -6410,12 +6431,23 @@ void Sema::CheckShadow(Scope *S, VarDecl *D, const LookupResult& R) { if (!isa<VarDecl>(ShadowedDecl) && !isa<FieldDecl>(ShadowedDecl)) return; - // Fields are not shadowed by variables in C++ static methods. - if (isa<FieldDecl>(ShadowedDecl)) + if (FieldDecl *FD = dyn_cast<FieldDecl>(ShadowedDecl)) { + // Fields are not shadowed by variables in C++ static methods. if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewDC)) if (MD->isStatic()) return; + // Fields shadowed by constructor parameters are a special case. Usually + // the constructor initializes the field with the parameter. + if (isa<CXXConstructorDecl>(NewDC) && isa<ParmVarDecl>(D)) { + // Remember that this was shadowed so we can either warn about its + // modification or its existence depending on warning settings. + D = D->getCanonicalDecl(); + ShadowingDecls.insert({D, FD}); + return; + } + } + if (VarDecl *shadowedVar = dyn_cast<VarDecl>(ShadowedDecl)) if (shadowedVar->isExternC()) { // For shadowing external vars, make sure that we point to the global @@ -6443,27 +6475,13 @@ void Sema::CheckShadow(Scope *S, VarDecl *D, const LookupResult& R) { // shadowing context, but that's just a false negative. } - // Determine what kind of declaration we're shadowing. - - // The order must be consistent with the %select in the warning message. - enum ShadowedDeclKind { Local, Global, StaticMember, Field }; - ShadowedDeclKind Kind; - if (isa<RecordDecl>(OldDC)) { - if (isa<FieldDecl>(ShadowedDecl)) - Kind = Field; - else - Kind = StaticMember; - } else if (OldDC->isFileContext()) { - Kind = Global; - } else { - Kind = Local; - } DeclarationName Name = R.getLookupName(); // Emit warning and note. if (getSourceManager().isInSystemMacro(R.getNameLoc())) return; + ShadowedDeclKind Kind = computeShadowedDeclKind(ShadowedDecl, OldDC); Diag(R.getNameLoc(), diag::warn_decl_shadow) << Name << Kind << OldDC; Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration); } @@ -6479,6 +6497,30 @@ void Sema::CheckShadow(Scope *S, VarDecl *D) { CheckShadow(S, D, R); } +/// Check if 'E', which is an expression that is about to be modified, refers +/// to a constructor parameter that shadows a field. +void Sema::CheckShadowingDeclModification(Expr *E, SourceLocation Loc) { + // Quickly ignore expressions that can't be shadowing ctor parameters. + if (!getLangOpts().CPlusPlus || ShadowingDecls.empty()) + return; + E = E->IgnoreParenImpCasts(); + auto *DRE = dyn_cast<DeclRefExpr>(E); + if (!DRE) + return; + const NamedDecl *D = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl()); + auto I = ShadowingDecls.find(D); + if (I == ShadowingDecls.end()) + return; + const NamedDecl *ShadowedDecl = I->second; + const DeclContext *OldDC = ShadowedDecl->getDeclContext(); + Diag(Loc, diag::warn_modifying_shadowing_decl) << D << OldDC; + Diag(D->getLocation(), diag::note_var_declared_here) << D; + Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration); + + // Avoid issuing multiple warnings about the same decl. + ShadowingDecls.erase(I); +} + /// Check for conflict between this global or extern "C" declaration and /// previous global or extern "C" declarations. This is only used in C++. template<typename T> |