diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index e5793fa58b04..203ab1eed547 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -73,7 +73,7 @@ def : DiagGroup<"redundant-decls">; def ReturnType : DiagGroup<"return-type">; def SemiBeforeMethodBody : DiagGroup<"semicolon-before-method-body">; def : DiagGroup<"sequence-point">; -def : DiagGroup<"shadow">; +def Shadow : DiagGroup<"shadow">; def : DiagGroup<"shorten-64-to-32">; def SignCompare : DiagGroup<"sign-compare">; def : DiagGroup<"synth">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 9a9b7cc85f6a..b2b4306bfa87 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -107,7 +107,16 @@ def warn_use_out_of_scope_declaration : Warning< "use of out-of-scope declaration of %0">; def err_inline_non_function : Error< "'inline' can only appear on functions">; - + +def warn_decl_shadow : + Warning<"declaration shadows a %select{" + "local variable|" + "variable in %2|" + "static data member of %2|" + "field of %2|" + "global built-in function}1">, + InGroup, DefaultIgnore; + // C++ using declarations def err_using_requires_qualname : Error< "using declaration requires a qualified name">; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index f568d1cdd45e..6c9a45ef6c44 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -430,7 +430,10 @@ std::string NamedDecl::getQualifiedNameAsString(const PrintingPolicy &P) const { for (; I!=End; ++I) QualName += *I + "::"; - QualName += getNameAsString(); + if (getDeclName()) + QualName += getNameAsString(); + else + QualName += ""; return QualName; } diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index c6b63673f57a..a18d5f11af39 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -778,6 +778,7 @@ public: const LookupResult &Previous, Scope *S); void DiagnoseFunctionSpecifiers(Declarator& D); + void DiagnoseShadow(NamedDecl* D, const LookupResult& R); NamedDecl* ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, QualType R, TypeSourceInfo *TInfo, LookupResult &Previous, bool &Redeclaration); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 776f0ddf1a61..5db817192f68 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2403,6 +2403,9 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, NewVD->addAttr(::new (Context) AsmLabelAttr(Context, SE->getString())); } + // Diagnose shadowed variables before filtering for scope. + DiagnoseShadow(NewVD, Previous); + // Don't consider existing declarations that are in a different // scope and are out-of-semantic-context declarations (if the new // declaration has linkage). @@ -2454,6 +2457,65 @@ Sema::ActOnVariableDeclarator(Scope* S, Declarator& D, DeclContext* DC, return NewVD; } +/// \brief Diagnose variable or built-in function shadowing. +/// +/// This method is called as soon as a NamedDecl materializes to check +/// if it shadows another local or global variable, or a built-in function. +/// +/// For performance reasons, the lookup results are reused from the calling +/// context. +/// +/// \param D variable decl to diagnose. Must be a variable. +/// \param R cached previous lookup of \p D. +/// +void Sema::DiagnoseShadow(NamedDecl* D, const LookupResult& R) { + assert(D->getKind() == Decl::Var && "Expecting variable."); + + // Return if warning is ignored. + if (Diags.getDiagnosticLevel(diag::warn_decl_shadow) == Diagnostic::Ignored) + return; + + // Return if not local decl. + if (!D->getDeclContext()->isFunctionOrMethod()) + return; + + DeclarationName Name = D->getDeclName(); + + // Return if lookup has no result. + if (R.getResultKind() != LookupResult::Found) { + // Emit warning for built-in shadowing. + if (Name.getAsIdentifierInfo() && + Name.getAsIdentifierInfo()->getBuiltinID()) + Diag(D->getLocation(), diag::warn_decl_shadow) + << Name + << 4 // global builtin + << Context.getTranslationUnitDecl(); + return; + } + + // Return if not variable decl. + NamedDecl* ShadowedDecl = R.getFoundDecl(); + if (!isa(ShadowedDecl) && !isa(ShadowedDecl)) + return; + + // Determine kind of declaration. + DeclContext *DC = ShadowedDecl->getDeclContext(); + unsigned Kind; + if (isa(DC)) { + if (isa(ShadowedDecl)) + Kind = 3; // field + else + Kind = 2; // static data member + } else if (DC->isFileContext()) + Kind = 1; // global + else + Kind = 0; // local + + // Emit warning and note. + Diag(D->getLocation(), diag::warn_decl_shadow) << Name << Kind << DC; + Diag(ShadowedDecl->getLocation(), diag::note_previous_declaration); +} + /// \brief Perform semantic checking on a newly-created variable /// declaration. ///