Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -568,6 +568,7 @@ def UnusedVariable : DiagGroup<"unused-variable", [UnusedConstVariable]>; def UnusedLocalTypedef : DiagGroup<"unused-local-typedef">; +def UnusedUsing : DiagGroup<"unused-using">; def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">; def UnusedGetterReturnValue : DiagGroup<"unused-getter-return-value">; def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">; @@ -698,6 +699,7 @@ // UnusedMemberFunction, (clean-up llvm before enabling) UnusedPrivateField, UnusedLambdaCapture, UnusedLocalTypedef, UnusedValue, UnusedVariable, + UnusedUsing, UnusedPropertyIvar]>, DiagCategory<"Unused Entity Issue">; @@ -813,6 +815,8 @@ [VectorConversion]>; // -Wvector-conversions = -Wvector-conversion def : DiagGroup<"unused-local-typedefs", [UnusedLocalTypedef]>; // -Wunused-local-typedefs = -Wunused-local-typedef +def : DiagGroup<"unused-usings", [UnusedUsing]>; + // -Wunused-usings = -Wunused-using // A warning group for warnings that we want to have on by default in clang, // but which aren't on by default in GCC. Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -279,6 +279,8 @@ def warn_unused_local_typedef : Warning< "unused %select{typedef|type alias}0 %1">, InGroup, DefaultIgnore; +def warn_unused_using : Warning<"unused using %0">, + InGroup, DefaultIgnore; def warn_unused_property_backing_ivar : Warning<"ivar %0 which backs the property is not " "referenced in this property's accessor">, Index: include/clang/Sema/ExternalSemaSource.h =================================================================== --- include/clang/Sema/ExternalSemaSource.h +++ include/clang/Sema/ExternalSemaSource.h @@ -35,6 +35,7 @@ class Scope; class Sema; class TypedefNameDecl; +class UsingDecl; class ValueDecl; class VarDecl; struct LateParsedTemplate; @@ -145,6 +146,15 @@ virtual void ReadUnusedLocalTypedefNameCandidates( llvm::SmallSetVector &Decls) {} + /// \brief Read the set of potentially unused usings known to the source. + /// + /// The external source should append its own potentially unused usings + /// to the given vector of declarations. Note that this routine may be + /// invoked multiple times; the external source should take care not to + /// introduce the same declarations repeatedly. + virtual void ReadUnusedUsingCandidates( + llvm::SmallSetVector &Decls) {} + /// \brief Read the set of referenced selectors known to the /// external Sema source. /// Index: include/clang/Sema/MultiplexExternalSemaSource.h =================================================================== --- include/clang/Sema/MultiplexExternalSemaSource.h +++ include/clang/Sema/MultiplexExternalSemaSource.h @@ -282,6 +282,15 @@ void ReadUnusedLocalTypedefNameCandidates( llvm::SmallSetVector &Decls) override; + /// \brief Read the set of potentially unused usings known to the source. + /// + /// The external source should append its own potentially unused usings + /// to the given vector of declarations. Note that this routine may be + /// invoked multiple times; the external source should take care not to + /// introduce the same declarations repeatedly. + void ReadUnusedUsingCandidates( + llvm::SmallSetVector &Decls) override; + /// \brief Read the set of referenced selectors known to the /// external Sema source. /// Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -555,6 +555,9 @@ llvm::SmallSetVector UnusedLocalTypedefNameCandidates; + /// \brief Set containing all usings that are likely unused. + llvm::SmallSetVector UnusedUsingCandidates; + /// \brief Delete-expressions to be analyzed at the end of translation unit /// /// This list contains class members, and locations of delete-expressions @@ -1290,6 +1293,7 @@ ModuleLoader &getModuleLoader() const; void emitAndClearUnusedLocalTypedefWarnings(); + void emitAndClearUnusedUsingWarnings(); void ActOnStartOfTranslationUnit(); void ActOnEndOfTranslationUnit(); Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -648,7 +648,10 @@ PP_CONDITIONAL_STACK = 62, /// \brief A table of skipped ranges within the preprocessing record. - PPD_SKIPPED_RANGES = 63 + PPD_SKIPPED_RANGES = 63, + + /// \brief Record code for potentially unused usings. + UNUSED_USING_CANDIDATES = 64 }; /// \brief Record types used within a source manager block. Index: include/clang/Serialization/ASTReader.h =================================================================== --- include/clang/Serialization/ASTReader.h +++ include/clang/Serialization/ASTReader.h @@ -828,6 +828,11 @@ /// Sema tracks these to emit warnings. SmallVector UnusedLocalTypedefNameCandidates; + /// \brief The IDs of all potentially unused using names in the chain. + /// + /// Sema tracks these to emit warnings. + SmallVector UnusedUsingCandidates; + /// \brief Our current depth in #pragma cuda force_host_device begin/end /// macros. unsigned ForceCUDAHostDeviceDepth = 0; @@ -1987,6 +1992,9 @@ void ReadUnusedLocalTypedefNameCandidates( llvm::SmallSetVector &Decls) override; + void ReadUnusedUsingCandidates( + llvm::SmallSetVector &Decls) override; + void ReadReferencedSelectors( SmallVectorImpl> &Sels) override; Index: lib/Sema/MultiplexExternalSemaSource.cpp =================================================================== --- lib/Sema/MultiplexExternalSemaSource.cpp +++ lib/Sema/MultiplexExternalSemaSource.cpp @@ -273,6 +273,12 @@ Sources[i]->ReadUnusedLocalTypedefNameCandidates(Decls); } +void MultiplexExternalSemaSource::ReadUnusedUsingCandidates( + llvm::SmallSetVector &Decls) { + for(size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadUnusedUsingCandidates(Decls); +} + void MultiplexExternalSemaSource::ReadReferencedSelectors( SmallVectorImpl > &Sels) { for(size_t i = 0; i < Sources.size(); ++i) Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -812,6 +812,43 @@ UnusedLocalTypedefNameCandidates.clear(); } +// Emit warnings on unused using references. +void Sema::emitAndClearUnusedUsingWarnings() { + if (ExternalSource) + ExternalSource->ReadUnusedUsingCandidates(UnusedUsingCandidates); + for (const UsingDecl *UD : UnusedUsingCandidates) { + // Skip if the using declaration is in a system header. + SourceLocation loc = UD->getLocation(); + // We only care about the expansion location. + loc = SourceMgr.getExpansionLoc(loc); + FileID file = SourceMgr.getFileID(loc); + if (file.isValid()) { + // Retrieve file information. + bool invalid = false; + const SrcMgr::SLocEntry &sloc = SourceMgr.getSLocEntry(file, &invalid); + if (!invalid && sloc.isFile()) { + const SrcMgr::FileInfo &fileInfo = sloc.getFile(); + if (SrcMgr::isSystem(fileInfo.getFileCharacteristic())) + continue; + } + } + // For the case when a using shadow declaration is being hidden, just + // get the location from the declaration itself. + // struct Base { void foo(int); }; + // struct Derived : Base { + // using Base::foo; + // void foo(int); + // }; + // Check if the referenced definition has been used or referenced. + const NamedDecl *ND = (UD->shadow_size() > 0) + ? UD->shadow_begin()->getTargetDecl() : UD; + if (ND->isUsed() || ND->isReferenced()) + continue; + Diag(UD->getLocation(), diag::warn_unused_using) << UD->getDeclName(); + } + UnusedUsingCandidates.clear(); +} + /// This is called before the very first declaration in the translation unit /// is parsed. Note that the ASTContext may have already injected some /// declarations. @@ -981,6 +1018,7 @@ // Warnings emitted in ActOnEndOfTranslationUnit() should be emitted for // modules when they are built, not every time they are used. emitAndClearUnusedLocalTypedefWarnings(); + emitAndClearUnusedUsingWarnings(); // Modules don't need any of the checking below. if (!PP.isIncrementalProcessingEnabled()) @@ -1101,6 +1139,7 @@ } emitAndClearUnusedLocalTypedefWarnings(); + emitAndClearUnusedUsingWarnings(); } if (!Diags.isIgnored(diag::warn_unused_private_field, SourceLocation())) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1679,6 +1679,9 @@ if (isa(D)) return true; + if (isa(D)) + return true; + // White-list anything that isn't a local variable. if (!isa(D) || isa(D) || isa(D)) return false; @@ -1772,6 +1775,16 @@ return; } + if (auto *USD = dyn_cast(D)) { + // Check if the using shadow target was used or referenced. + const NamedDecl *ND = USD->getTargetDecl(); + if (!(ND->isUsed() || ND->isReferenced())) { + const UsingDecl *UD = USD->getUsingDecl(); + Diag(UD->getLocation(), diag::warn_unused_using) << UD->getDeclName(); + } + return; + } + FixItHint Hint; GenerateFixForUnusedDecl(D, Context, Hint); Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -9167,14 +9167,20 @@ } } - NamedDecl *UD = + NamedDecl *ND = BuildUsingDeclaration(S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc, SS, TargetNameInfo, EllipsisLoc, AttrList, /*IsInstantiation*/false); - if (UD) - PushOnScopeChains(UD, S, /*AddToContext*/ false); + if (ND) { + PushOnScopeChains(ND, S, /*AddToContext*/ false); + if (auto *UD = dyn_cast(ND)) + // In the case of global usings they can be referenced later on, so the + // diagnostics are emitted at end-of-translation-unit. + if (!UD->getParentFunctionOrMethod()) + UnusedUsingCandidates.insert(UD); + } - return UD; + return ND; } /// \brief Determine whether a using declaration considers the given Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -3447,6 +3447,11 @@ getGlobalDeclID(F, Record[I])); break; + case UNUSED_USING_CANDIDATES: + for (unsigned I = 0, N = Record.size(); I != N; ++I) + UnusedUsingCandidates.push_back(getGlobalDeclID(F, Record[I])); + break; + case CUDA_PRAGMA_FORCE_HOST_DEVICE_DEPTH: if (Record.size() != 1) { Error("invalid cuda pragma options record"); @@ -8090,6 +8095,17 @@ UnusedLocalTypedefNameCandidates.clear(); } +void ASTReader::ReadUnusedUsingCandidates( + llvm::SmallSetVector &Decls) { + for (unsigned I = 0, N = UnusedUsingCandidates.size(); I != N; ++I) { + UsingDecl *D = dyn_cast_or_null( + GetDecl(UnusedUsingCandidates[I])); + if (D) + Decls.insert(D); + } + UnusedUsingCandidates.clear(); +} + void ASTReader::ReadReferencedSelectors( SmallVectorImpl> &Sels) { if (ReferencedSelectorsData.empty()) Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -1137,6 +1137,7 @@ RECORD(MSSTRUCT_PRAGMA_OPTIONS); RECORD(POINTERS_TO_MEMBERS_PRAGMA_OPTIONS); RECORD(UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES); + RECORD(UNUSED_USING_CANDIDATES); RECORD(DELETE_EXPRS_TO_ANALYZE); RECORD(CUDA_PRAGMA_FORCE_HOST_DEVICE_DEPTH); RECORD(PP_CONDITIONAL_STACK); @@ -4715,6 +4716,11 @@ for (const TypedefNameDecl *TD : SemaRef.UnusedLocalTypedefNameCandidates) AddDeclRef(TD, UnusedLocalTypedefNameCandidates); + // Build a record containing all of the UnusedUsingCandidates. + RecordData UnusedUsingCandidates; + for (const UsingDecl *UD : SemaRef.UnusedUsingCandidates) + AddDeclRef(UD, UnusedUsingCandidates); + // Build a record containing all of pending implicit instantiations. RecordData PendingInstantiations; for (const auto &I : SemaRef.PendingInstantiations) { @@ -5035,6 +5041,10 @@ Stream.EmitRecord(UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES, UnusedLocalTypedefNameCandidates); + // Write the record containing potentially unused usings. + if (!UnusedUsingCandidates.empty()) + Stream.EmitRecord(UNUSED_USING_CANDIDATES, UnusedUsingCandidates); + // Write the record containing pending implicit instantiations. if (!PendingInstantiations.empty()) Stream.EmitRecord(PENDING_IMPLICIT_INSTANTIATIONS, PendingInstantiations); Index: test/FixIt/fixit.cpp =================================================================== --- test/FixIt/fixit.cpp +++ test/FixIt/fixit.cpp @@ -1,12 +1,12 @@ -// RUN: %clang_cc1 -pedantic -Wall -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++98 %s +// RUN: %clang_cc1 -pedantic -Wall -Wno-unused-using -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++98 %s // RUN: cp %s %t-98 -// RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++98 %t-98 -// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-comment -fcxx-exceptions -x c++ -std=c++98 %t-98 +// RUN: not %clang_cc1 -pedantic -Wall -Wno-unused-using -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++98 %t-98 +// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-unused-using -Wno-comment -fcxx-exceptions -x c++ -std=c++98 %t-98 // RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -x c++ -std=c++11 %s 2>&1 | FileCheck %s -// RUN: %clang_cc1 -pedantic -Wall -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++11 %s +// RUN: %clang_cc1 -pedantic -Wall -Wno-unused-using -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++11 %s // RUN: cp %s %t-11 -// RUN: not %clang_cc1 -pedantic -Wall -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++11 %t-11 -// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-comment -fcxx-exceptions -x c++ -std=c++11 %t-11 +// RUN: not %clang_cc1 -pedantic -Wall -Wno-unused-using -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++11 %t-11 +// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror -Wno-unused-using -Wno-comment -fcxx-exceptions -x c++ -std=c++11 %t-11 /* This is a test of the various code modification hints that are provided as part of warning or extension diagnostics. All of the Index: test/Modules/Inputs/module.map =================================================================== --- test/Modules/Inputs/module.map +++ test/Modules/Inputs/module.map @@ -307,6 +307,10 @@ header "warn-unused-local-typedef.h" } +module warn_unused_using { + header "warn-unused-using.h" +} + module using_decl { module a { header "using-decl-a.h" export * } module b { header "using-decl-b.h" export * } Index: test/Modules/Inputs/warn-unused-using.h =================================================================== --- test/Modules/Inputs/warn-unused-using.h +++ test/Modules/Inputs/warn-unused-using.h @@ -0,0 +1,6 @@ +namespace nsp { + typedef int INT; +} +inline void foo() { + using nsp::INT; +} Index: test/Modules/warn-unused-using.cpp =================================================================== --- test/Modules/warn-unused-using.cpp +++ test/Modules/warn-unused-using.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -Wunused-using -x objective-c++ -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=CHECK_1 +// RUN: %clang_cc1 -Wunused-using -x objective-c++ -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=CHECK_2 --allow-empty + +// For modules, the warning should only fire the first time, when the module is +// built. +// CHECK_1: warning: unused using 'INT' +// CHECK_2-NOT: warning: unused using 'INT' +@import warn_unused_using; Index: test/SemaCXX/coreturn.cpp =================================================================== --- test/SemaCXX/coreturn.cpp +++ test/SemaCXX/coreturn.cpp @@ -2,7 +2,7 @@ #include "Inputs/std-coroutine.h" using std::experimental::suspend_always; -using std::experimental::suspend_never; +using std::experimental::suspend_never; // expected-warning {{unused using 'suspend_never'}} struct awaitable { bool await_ready(); Index: test/SemaCXX/warn-unused-using-serialize.cpp =================================================================== --- test/SemaCXX/warn-unused-using-serialize.cpp +++ test/SemaCXX/warn-unused-using-serialize.cpp @@ -0,0 +1,14 @@ +// RUN: %clang -x c++-header -c -Wunused-using %s -o %t.gch -Werror +// RUN: %clang -DBE_THE_SOURCE -c -Wunused-using -include %t %s -o /dev/null 2>&1 | FileCheck %s +// RUN: %clang -DBE_THE_SOURCE -c -Wunused-using -include %t %s -o /dev/null 2>&1 | FileCheck %s + +#ifndef BE_THE_SOURCE +namespace nsp { + typedef int INT; +} +//inline void foo() { +// The warning should fire every time the pch file is used, not when it's built. +// CHECK: warning: unused using 'INT' + using nsp::INT; +//} +#endif Index: test/SemaCXX/warn-unused-using.cpp =================================================================== --- test/SemaCXX/warn-unused-using.cpp +++ test/SemaCXX/warn-unused-using.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused-using -verify -std=c++1y %s + +namespace nsp { + void Print(); + int var; + typedef int INT; + typedef char CHAR; + typedef float FLOAT; +} + +using nsp::Print; // expected-warning {{unused using 'Print'}} +using nsp::var; // expected-warning {{unused using 'var'}} + +void Foo() { + using nsp::Print; // expected-warning {{unused using 'Print'}} + { + using nsp::var; // expected-warning {{unused using 'var'}} + { + using nsp::INT; // expected-warning {{unused using 'INT'}} + using nsp::FLOAT; // expected-warning {{unused using 'FLOAT'}} + } + } +} + +void Bar() { + using nsp::FLOAT; // no diag + FLOAT f; + f = 2.0; +} + +struct A { + virtual void Foo(double x) const; +}; +struct B : A { + using A::Foo; // expected-warning {{unused using 'Foo'}} + virtual void Foo(double x) const; +}; + +void one() { + B b; + b.Foo(1.0); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-using" +using nsp::CHAR; // no diag +#pragma clang diagnostic pop + +using nsp::CHAR; // expected-warning {{unused using 'CHAR'}}