Index: include/clang/Sema/ExternalSemaSource.h =================================================================== --- include/clang/Sema/ExternalSemaSource.h +++ include/clang/Sema/ExternalSemaSource.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_SEMA_EXTERNAL_SEMA_SOURCE_H #include "clang/AST/ExternalASTSource.h" +#include "clang/AST/Type.h" #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/MapVector.h" @@ -204,6 +205,20 @@ return TypoCorrection(); } + /// \brief Produces a diagnostic note if the external source contains a + /// complete definition for \p T. + /// + /// \param Loc the location at which a complete type was required but not + /// provided + /// + /// \param T the \c QualType that should have been complete at \p Loc + /// + /// \return true if a diagnostic was produced, false otherwise. + virtual bool MaybeDiagnoseMissingCompleteType(SourceLocation Loc, + QualType T) { + return false; + } + // isa/cast/dyn_cast support static bool classof(const ExternalASTSource *Source) { return Source->SemaSource; Index: include/clang/Sema/MultiplexExternalSemaSource.h =================================================================== --- include/clang/Sema/MultiplexExternalSemaSource.h +++ include/clang/Sema/MultiplexExternalSemaSource.h @@ -340,6 +340,18 @@ bool EnteringContext, const ObjCObjectPointerType *OPT); + /// \brief Produces a diagnostic note if one of the attached sources + /// contains a complete definition for \p T. Queries the sources in list + /// order until the first one claims that a diagnostic was produced. + /// + /// \param Loc the location at which a complete type was required but not + /// provided + /// + /// \param T the \c QualType that should have been complete at \p Loc + /// + /// \return true if a diagnostic was produced, false otherwise. + virtual bool MaybeDiagnoseMissingCompleteType(SourceLocation Loc, QualType T); + // isa/cast/dyn_cast support static bool classof(const MultiplexExternalSemaSource*) { return true; } //static bool classof(const ExternalSemaSource*) { return true; } Index: lib/Sema/MultiplexExternalSemaSource.cpp =================================================================== --- lib/Sema/MultiplexExternalSemaSource.cpp +++ lib/Sema/MultiplexExternalSemaSource.cpp @@ -289,3 +289,12 @@ } return TypoCorrection(); } + +bool MultiplexExternalSemaSource::MaybeDiagnoseMissingCompleteType( + SourceLocation Loc, QualType T) { + for (size_t I = 0, E = Sources.size(); I < E; ++I) { + if (Sources[I]->MaybeDiagnoseMissingCompleteType(Loc, T)) + return true; + } + return false; +} Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -5052,6 +5052,11 @@ if (IFace && !IFace->getDecl()->isInvalidDecl()) Diag(IFace->getDecl()->getLocation(), diag::note_forward_class); + // If we have external information that we can use to suggest a fix, + // produce a note. + if (ExternalSource) + ExternalSource->MaybeDiagnoseMissingCompleteType(Loc, T); + return true; } Index: unittests/Sema/ExternalSemaSourceTest.cpp =================================================================== --- unittests/Sema/ExternalSemaSourceTest.cpp +++ unittests/Sema/ExternalSemaSourceTest.cpp @@ -24,6 +24,21 @@ namespace { +// \brief Counts the number of times MaybeDiagnoseMissingCompleteType +// is called. Returns the result it was provided on creation. +class CompleteTypeDiagnoser : public clang::ExternalSemaSource { +public: + CompleteTypeDiagnoser(bool MockResult) : CallCount(0), Result(MockResult) {} + + virtual bool MaybeDiagnoseMissingCompleteType(SourceLocation L, QualType T) { + ++CallCount; + return Result; + } + + int CallCount; + bool Result; +}; + // \brief Counts the number of err_using_directive_member_suggest diagnostics // correcting from one namespace to another while still passing all diagnostics // along a chain of consumers. @@ -211,4 +226,41 @@ ASSERT_EQ(1, Watcher.SeenCount); } +// We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise +// solve the problem. +TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) { + llvm::OwningPtr Installer( + new ExternalSemaSourceInstaller); + CompleteTypeDiagnoser Diagnoser(false); + Installer->PushSource(&Diagnoser); + std::vector Args(1, "-std=c++11"); + // This code hits the class template specialization/class member of a class + // template specialization checks in Sema::RequireCompleteTypeImpl. + ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( + Installer.take(), + "template struct S { class C { }; }; S::C SCInst;", + Args)); + ASSERT_EQ(0, Diagnoser.CallCount); +} + +// The first ExternalSemaSource where MaybeDiagnoseMissingCompleteType returns +// true should be the last one called. +TEST(ExternalSemaSource, FirstDiagnoserTaken) { + llvm::OwningPtr Installer( + new ExternalSemaSourceInstaller); + CompleteTypeDiagnoser First(false); + CompleteTypeDiagnoser Second(true); + CompleteTypeDiagnoser Third(true); + Installer->PushSource(&First); + Installer->PushSource(&Second); + Installer->PushSource(&Third); + std::vector Args(1, "-std=c++11"); + ASSERT_FALSE(clang::tooling::runToolOnCodeWithArgs( + Installer.take(), "class Incomplete; Incomplete IncompleteInstance;", + Args)); + ASSERT_EQ(1, First.CallCount); + ASSERT_EQ(1, Second.CallCount); + ASSERT_EQ(0, Third.CallCount); +} + } // anonymous namespace