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/Weak.h" #include "llvm/ADT/MapVector.h" #include @@ -177,6 +178,19 @@ SmallVectorImpl > &Pending) {} + + /// \brief Produces a diagnostic note if the external source contains a + /// complete definition for T. + /// + /// \param Loc the location at which a complete type was required but not + /// provided + /// + /// \param T the QualType that should have been complete at 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 @@ -322,6 +322,8 @@ virtual void ReadPendingInstantiations( SmallVectorImpl >& Pending); + 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 @@ -267,3 +267,12 @@ for(size_t i = 0; i < Sources.size(); ++i) Sources[i]->ReadPendingInstantiations(Pending); } + +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 @@ -5009,6 +5009,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/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -19,4 +19,5 @@ add_subdirectory(AST) add_subdirectory(Tooling) add_subdirectory(Format) + add_subdirectory(Sema) endif() Index: unittests/Makefile =================================================================== --- unittests/Makefile +++ unittests/Makefile @@ -23,7 +23,7 @@ endif ifeq ($(ENABLE_CLANG_REWRITER),1) -PARALLEL_DIRS += ASTMatchers AST Tooling +PARALLEL_DIRS += ASTMatchers AST Tooling Sema endif ifeq ($(ENABLE_CLANG_STATIC_ANALYZER),1) Index: unittests/Sema/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/Sema/CMakeLists.txt @@ -0,0 +1,7 @@ +add_clang_unittest(SemaTests + ExternalSemaSourceTest.cpp + ) + +target_link_libraries(SemaTests + clangAST clangASTMatchers clangTooling + ) Index: unittests/Sema/ExternalSemaSourceTest.cpp =================================================================== --- /dev/null +++ unittests/Sema/ExternalSemaSourceTest.cpp @@ -0,0 +1,100 @@ +//=== unittests/Sema/ExternalSemaSourceTest.cpp - ExternalSemaSource tests ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Sema/ExternalSemaSource.h" +#include "clang/Sema/Sema.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +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; +}; + +class ExternalSemaSourceInstaller : public clang::ASTFrontendAction { +protected: + virtual clang::ASTConsumer * + CreateASTConsumer(clang::CompilerInstance &Compiler, + llvm::StringRef /* dummy */) { + return new clang::ASTConsumer(); + } + + virtual void ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + ASSERT_FALSE(CI.hasSema()); + CI.createSema(getTranslationUnitKind(), NULL); + for (size_t I = 0, E = Sources.size(); I < E; ++I) + CI.getSema().addExternalSource(Sources[I]); + ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats, + CI.getFrontendOpts().SkipFunctionBodies); + } + +public: + void PushSource(clang::ExternalSemaSource *Source) { + Sources.push_back(Source); + } + +private: + std::vector Sources; +}; + +// 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 Index: unittests/Sema/Makefile =================================================================== --- /dev/null +++ unittests/Sema/Makefile @@ -0,0 +1,19 @@ +##===- unittests/Sema/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../.. +TESTNAME = Sema +include $(CLANG_LEVEL)/../../Makefile.config +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option +USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewriteCore.a clangRewriteFrontend.a \ + clangParse.a clangSema.a clangAnalysis.a \ + clangEdit.a clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile