Index: clang-tools-extra/trunk/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/CMakeLists.txt +++ clang-tools-extra/trunk/CMakeLists.txt @@ -6,6 +6,7 @@ endif() add_subdirectory(clang-query) +add_subdirectory(include-fixer) add_subdirectory(pp-trace) add_subdirectory(tool-template) Index: clang-tools-extra/trunk/include-fixer/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/include-fixer/CMakeLists.txt +++ clang-tools-extra/trunk/include-fixer/CMakeLists.txt @@ -0,0 +1,20 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +add_clang_library(clangIncludeFixer + IncludeFixer.cpp + InMemoryXrefsDB.cpp + + LINK_LIBS + clangAST + clangBasic + clangFrontend + clangLex + clangParse + clangSema + clangTooling + clangToolingCore + ) + +add_subdirectory(tool) Index: clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h =================================================================== --- clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h +++ clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.h @@ -0,0 +1,36 @@ +//===-- InMemoryXrefsDB.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYXREFSDB_H +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYXREFSDB_H + +#include "XrefsDB.h" +#include +#include +#include + +namespace clang { +namespace include_fixer { + +/// Xref database with fixed content. +class InMemoryXrefsDB : public XrefsDB { +public: + InMemoryXrefsDB(std::map> LookupTable) + : LookupTable(std::move(LookupTable)) {} + + std::vector search(llvm::StringRef Identifier) override; + +private: + std::map> LookupTable; +}; + +} // namespace include_fixer +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYXREFSDB_H Index: clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp =================================================================== --- clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp +++ clang-tools-extra/trunk/include-fixer/InMemoryXrefsDB.cpp @@ -0,0 +1,23 @@ +//===-- InMemoryXrefsDB.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InMemoryXrefsDB.h" + +namespace clang { +namespace include_fixer { + +std::vector InMemoryXrefsDB::search(llvm::StringRef Identifier) { + auto I = LookupTable.find(Identifier); + if (I != LookupTable.end()) + return I->second; + return {}; +} + +} // namespace include_fixer +} // namespace clang Index: clang-tools-extra/trunk/include-fixer/IncludeFixer.h =================================================================== --- clang-tools-extra/trunk/include-fixer/IncludeFixer.h +++ clang-tools-extra/trunk/include-fixer/IncludeFixer.h @@ -0,0 +1,46 @@ +//===-- IncludeFixer.h - Include inserter -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H + +#include "XrefsDB.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Tooling.h" + +namespace clang { +namespace include_fixer { + +class IncludeFixerActionFactory : public clang::tooling::ToolAction { +public: + /// \param Xrefs A source for matching symbols to header files. + /// \param Replacements Storage for the output of the fixer. + IncludeFixerActionFactory( + XrefsDB &Xrefs, std::vector &Replacements); + ~IncludeFixerActionFactory(); + + bool + runInvocation(clang::CompilerInvocation *Invocation, + clang::FileManager *Files, + std::shared_ptr PCHContainerOps, + clang::DiagnosticConsumer *Diagnostics) override; + +private: + /// The client to use to find cross-references. + XrefsDB &Xrefs; + + /// Replacements are written here. + std::vector &Replacements; +}; + +} // namespace include_fixer +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H Index: clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp =================================================================== --- clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp +++ clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp @@ -0,0 +1,309 @@ +//===-- IncludeFixer.cpp - Include inserter based on sema callbacks -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IncludeFixer.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Sema/ExternalSemaSource.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "include-fixer" + +using namespace clang; + +namespace clang { +namespace include_fixer { +namespace { + +class Action; + +class PreprocessorHooks : public clang::PPCallbacks { +public: + explicit PreprocessorHooks(Action *EnclosingPass) + : EnclosingPass(EnclosingPass), TrackedFile(nullptr) {} + + void FileChanged(clang::SourceLocation loc, + clang::PPCallbacks::FileChangeReason reason, + clang::SrcMgr::CharacteristicKind file_type, + clang::FileID prev_fid) override; + + void InclusionDirective(clang::SourceLocation HashLocation, + const clang::Token &IncludeToken, + llvm::StringRef FileName, bool IsAngled, + clang::CharSourceRange FileNameRange, + const clang::FileEntry *IncludeFile, + llvm::StringRef SearchPath, + llvm::StringRef relative_path, + const clang::Module *imported) override; + +private: + /// The current Action. + Action *EnclosingPass; + + /// The current FileEntry. + const clang::FileEntry *TrackedFile; +}; + +/// Manages the parse, gathers include suggestions. +class Action : public clang::ASTFrontendAction, + public clang::ExternalSemaSource { +public: + explicit Action(XrefsDB &Xrefs) : Xrefs(Xrefs) {} + + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &Compiler, + StringRef InFile) override { + Filename = InFile; + Compiler.getPreprocessor().addPPCallbacks( + llvm::make_unique(this)); + return llvm::make_unique(); + } + + void ExecuteAction() override { + clang::CompilerInstance *Compiler = &getCompilerInstance(); + assert(!Compiler->hasSema() && "CI already has Sema"); + + // Set up our hooks into sema and parse the AST. + if (hasCodeCompletionSupport() && + !Compiler->getFrontendOpts().CodeCompletionAt.FileName.empty()) + Compiler->createCodeCompletionConsumer(); + + clang::CodeCompleteConsumer *CompletionConsumer = nullptr; + if (Compiler->hasCodeCompletionConsumer()) + CompletionConsumer = &Compiler->getCodeCompletionConsumer(); + + Compiler->createSema(getTranslationUnitKind(), CompletionConsumer); + Compiler->getSema().addExternalSource(this); + + clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats, + Compiler->getFrontendOpts().SkipFunctionBodies); + } + + /// Callback for incomplete types. If we encounter a forward declaration we + /// have the fully qualified name ready. Just query that. + bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc, + clang::QualType T) override { + clang::ASTContext &context = getCompilerInstance().getASTContext(); + query(T.getUnqualifiedType().getAsString(context.getPrintingPolicy())); + return false; + } + + /// Callback for unknown identifiers. Try to piece together as much + /// qualification as we can get and do a query. + clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, + int LookupKind, Scope *S, CXXScopeSpec *SS, + CorrectionCandidateCallback &CCC, + DeclContext *MemberContext, + bool EnteringContext, + const ObjCObjectPointerType *OPT) override { + // We don't want to look up inner parts of nested name specifies. Looking up + // the header where a namespace is defined in is rarely useful. + if (LookupKind == clang::Sema::LookupNestedNameSpecifierName) { + DEBUG(llvm::dbgs() << "ignoring " << Typo.getAsString() << "\n"); + return clang::TypoCorrection(); + } + + /// If we have a scope specification, use that to get more precise results. + std::string QueryString; + if (SS && SS->getRange().isValid()) { + auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(), + Typo.getLoc()); + QueryString = + Lexer::getSourceText(Range, getCompilerInstance().getSourceManager(), + getCompilerInstance().getLangOpts()); + } else { + QueryString = Typo.getAsString(); + } + + return query(QueryString); + } + + StringRef filename() const { return Filename; } + + /// Called for each include file we discover is in the file. + /// \param SourceManager the active SourceManager + /// \param canonical_path the canonical path to the include file + /// \param uttered_path the path as it appeared in the program + /// \param IsAngled whether angle brackets were used + /// \param HashLocation the source location of the include's \# + /// \param EndLocation the source location following the include + void NextInclude(clang::SourceManager *SourceManager, + llvm::StringRef canonical_path, llvm::StringRef uttered_path, + bool IsAngled, clang::SourceLocation HashLocation, + clang::SourceLocation EndLocation) { + unsigned Offset = SourceManager->getFileOffset(HashLocation); + if (FirstIncludeOffset == -1U) + FirstIncludeOffset = Offset; + } + + /// Generate replacements for the suggested includes. + /// \return true if changes will be made, false otherwise. + bool Rewrite(clang::SourceManager &SourceManager, + std::vector &replacements) { + for (const auto &ToTry : Untried) { + DEBUG(llvm::dbgs() << "Adding include " << ToTry << "\n"); + std::string ToAdd = "#include " + ToTry + "\n"; + + if (FirstIncludeOffset == -1U) + FirstIncludeOffset = 0; + + replacements.push_back(clang::tooling::Replacement( + SourceManager, FileBegin.getLocWithOffset(FirstIncludeOffset), 0, + ToAdd)); + + // We currently abort after the first inserted include. The more + // includes we have the less safe this becomes due to error recovery + // changing the results. + // FIXME: Handle multiple includes at once. + return true; + } + return false; + } + + /// Gets the location at the very top of the file. + clang::SourceLocation file_begin() const { return FileBegin; } + + /// Sets the location at the very top of the file. + void setFileBegin(clang::SourceLocation Location) { FileBegin = Location; } + + /// Add an include to the set of includes to try. + /// \param include_path The include path to try. + void TryInclude(const std::string &query, const std::string &include_path) { + Untried.insert(include_path); + } + +private: + /// Query the database for a given identifier. + clang::TypoCorrection query(StringRef Query) { + assert(!Query.empty() && "Empty query!"); + + // Save database lookups by not looking up identifiers multiple times. + if (!SeenQueries.insert(Query).second) + return clang::TypoCorrection(); + + DEBUG(llvm::dbgs() << "Looking up " << Query << " ... "); + + std::string error_text; + auto SearchReply = Xrefs.search(Query); + DEBUG(llvm::dbgs() << SearchReply.size() << " replies\n"); + if (SearchReply.empty()) + return clang::TypoCorrection(); + + // Add those files to the set of includes to try out. + // FIXME: Rank the results and pick the best one instead of the first one. + TryInclude(Query, SearchReply[0]); + + // FIXME: We should just return the name we got as input here and prevent + // clang from trying to correct the typo by itself. That may change the + // identifier to something that's not wanted by the user. + return clang::TypoCorrection(); + } + + /// The client to use to find cross-references. + XrefsDB &Xrefs; + + // Remeber things we looked up to avoid querying things twice. + llvm::StringSet<> SeenQueries; + + /// The absolute path to the file being processed. + std::string Filename; + + /// The location of the beginning of the tracked file. + clang::SourceLocation FileBegin; + + /// The offset of the last include in the original source file. This will + /// be used as the insertion point for new include directives. + unsigned FirstIncludeOffset = -1U; + + /// Includes we have left to try. + std::set Untried; +}; + +void PreprocessorHooks::FileChanged(clang::SourceLocation Loc, + clang::PPCallbacks::FileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, + clang::FileID PrevFID) { + // Remember where the main file starts. + if (Reason == clang::PPCallbacks::EnterFile) { + clang::SourceManager *SourceManager = + &EnclosingPass->getCompilerInstance().getSourceManager(); + clang::FileID loc_id = SourceManager->getFileID(Loc); + if (const clang::FileEntry *FileEntry = + SourceManager->getFileEntryForID(loc_id)) { + if (FileEntry->getName() == EnclosingPass->filename()) { + EnclosingPass->setFileBegin(Loc); + TrackedFile = FileEntry; + } + } + } +} + +void PreprocessorHooks::InclusionDirective( + clang::SourceLocation HashLocation, const clang::Token &IncludeToken, + llvm::StringRef FileName, bool IsAngled, + clang::CharSourceRange FileNameRange, const clang::FileEntry *IncludeFile, + llvm::StringRef SearchPath, llvm::StringRef relative_path, + const clang::Module *imported) { + // Remember include locations so we can insert our new include at the end of + // the include block. + clang::SourceManager *SourceManager = + &EnclosingPass->getCompilerInstance().getSourceManager(); + auto IDPosition = SourceManager->getDecomposedExpansionLoc(HashLocation); + const FileEntry *SourceFile = + SourceManager->getFileEntryForID(IDPosition.first); + if (!IncludeFile || TrackedFile != SourceFile) + return; + EnclosingPass->NextInclude(SourceManager, IncludeFile->getName(), FileName, + IsAngled, HashLocation, FileNameRange.getEnd()); +} + +} // namespace + +IncludeFixerActionFactory::IncludeFixerActionFactory( + XrefsDB &Xrefs, std::vector &Replacements) + : Xrefs(Xrefs), Replacements(Replacements) {} + +IncludeFixerActionFactory::~IncludeFixerActionFactory() = default; + +bool IncludeFixerActionFactory::runInvocation( + clang::CompilerInvocation *Invocation, clang::FileManager *Files, + std::shared_ptr PCHContainerOps, + clang::DiagnosticConsumer *Diagnostics) { + assert(Invocation->getFrontendOpts().Inputs.size() == 1); + + // Set up Clang. + clang::CompilerInstance Compiler(PCHContainerOps); + Compiler.setInvocation(Invocation); + Compiler.setFileManager(Files); + + // Create the compiler's actual diagnostics engine. We want to drop all + // diagnostics here. + Compiler.createDiagnostics(new clang::IgnoringDiagConsumer, + /*ShouldOwnClient=*/true); + Compiler.createSourceManager(*Files); + + // Run the parser, gather missing includes. + auto ScopedToolAction = llvm::make_unique(Xrefs); + Compiler.ExecuteAction(*ScopedToolAction); + + // Generate replacements. + ScopedToolAction->Rewrite(Compiler.getSourceManager(), Replacements); + + // Technically this should only return true if we're sure that we have a + // parseable file. We don't know that though. + return true; +} + +} // namespace include_fixer +} // namespace clang Index: clang-tools-extra/trunk/include-fixer/XrefsDB.h =================================================================== --- clang-tools-extra/trunk/include-fixer/XrefsDB.h +++ clang-tools-extra/trunk/include-fixer/XrefsDB.h @@ -0,0 +1,38 @@ +//===-- XrefsDB.h - Interface for symbol-header matching --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_XREFSDB_H +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_XREFSDB_H + +#include "llvm/ADT/StringRef.h" +#include + +namespace clang { +namespace include_fixer { + +/// This class provides an interface for finding the header files corresponding +/// to an indentifier in the source code. +class XrefsDB { +public: + virtual ~XrefsDB() = default; + + /// Search for header files to be included for an identifier. + /// \param Identifier The identifier being searched for. May or may not be + /// fully qualified. + /// \returns A list of inclusion candidates, in a format ready for being + /// pasted after an #include token. + // FIXME: Expose the type name so we can also insert using declarations (or + // fix the usage) + virtual std::vector search(llvm::StringRef Identifier) = 0; +}; + +} // namespace include_fixer +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_XREFSDB_H Index: clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt +++ clang-tools-extra/trunk/include-fixer/tool/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) + +add_clang_executable(clang-include-fixer ClangIncludeFixer.cpp) +target_link_libraries(clang-include-fixer + clangBasic + clangFrontend + clangIncludeFixer + clangRewrite + clangTooling + clangToolingCore + ) Index: clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp =================================================================== --- clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp +++ clang-tools-extra/trunk/include-fixer/tool/ClangIncludeFixer.cpp @@ -0,0 +1,50 @@ +//===-- ClangIncludeFixer.cpp - Standalone include fixer ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InMemoryXrefsDB.h" +#include "IncludeFixer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/CommandLine.h" +using namespace clang; + +static llvm::cl::OptionCategory tool_options("Tool options"); + +int main(int argc, char **argv) { + clang::tooling::CommonOptionsParser options(argc, (const char **)argv, + tool_options); + clang::tooling::ClangTool tool(options.getCompilations(), + options.getSourcePathList()); + // Set up the data source. + std::map> XrefsMap = { + {"std::string", {""}}}; + auto XrefsDB = + llvm::make_unique(std::move(XrefsMap)); + + // Now run our tool. + std::vector Replacements; + include_fixer::IncludeFixerActionFactory Factory(*XrefsDB, Replacements); + + tool.run(&Factory); // Always succeeds. + + // Set up a new source manager for applying the resulting replacements. + llvm::IntrusiveRefCntPtr DiagOpts( + new clang::DiagnosticOptions); + clang::DiagnosticsEngine Diagnostics(new clang::DiagnosticIDs, &*DiagOpts); + clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::outs(), &*DiagOpts); + clang::SourceManager source_manager(Diagnostics, tool.getFiles()); + Diagnostics.setClient(&DiagnosticPrinter, false); + + // Write replacements to disk. + clang::Rewriter Rewrites(source_manager, clang::LangOptions()); + clang::tooling::applyAllReplacements(Replacements, Rewrites); + return Rewrites.overwriteChangedFiles(); +} Index: clang-tools-extra/trunk/unittests/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/unittests/CMakeLists.txt +++ clang-tools-extra/trunk/unittests/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(clang-rename) add_subdirectory(clang-query) add_subdirectory(clang-tidy) +add_subdirectory(include-fixer) Index: clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt +++ clang-tools-extra/trunk/unittests/include-fixer/CMakeLists.txt @@ -0,0 +1,22 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +get_filename_component(INCLUDE_FIXER_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../include-fixer REALPATH) +include_directories( + ${INCLUDE_FIXER_SOURCE_DIR} + ) + +add_extra_unittest(IncludeFixerTests + IncludeFixerTest.cpp + ) + +target_link_libraries(IncludeFixerTests + clangBasic + clangFrontend + clangIncludeFixer + clangRewrite + clangTooling + clangToolingCore + ) Index: clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp =================================================================== --- clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp +++ clang-tools-extra/trunk/unittests/include-fixer/IncludeFixerTest.cpp @@ -0,0 +1,87 @@ +//===-- IncludeFixerTest.cpp - Include fixer unit tests -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../../../../unittests/Tooling/RewriterTestContext.h" +#include "InMemoryXrefsDB.h" +#include "IncludeFixer.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" +using namespace clang; + +namespace clang { +namespace include_fixer { +namespace { + +static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code, + StringRef FileName) { + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions(), InMemoryFileSystem)); + tooling::ToolInvocation Invocation( + {std::string("include_fixer"), std::string("-fsyntax-only"), + FileName.str()}, + ToolAction, Files.get(), std::make_shared()); + + InMemoryFileSystem->addFile(FileName, 0, + llvm::MemoryBuffer::getMemBuffer(Code)); + + InMemoryFileSystem->addFile("foo.h", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + InMemoryFileSystem->addFile("bar.h", 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + return Invocation.run(); +} + +static std::string runIncludeFixer(StringRef Code) { + std::map> XrefsMap = { + {"std::string", {""}}, {"std::string::size_type", {""}}}; + auto XrefsDB = + llvm::make_unique(std::move(XrefsMap)); + std::vector Replacements; + IncludeFixerActionFactory Factory(*XrefsDB, Replacements); + runOnCode(&Factory, Code, "input.cc"); + clang::RewriterTestContext Context; + clang::FileID ID = Context.createInMemoryFile("input.cc", Code); + clang::tooling::applyAllReplacements(Replacements, Context.Rewrite); + return Context.getRewrittenText(ID); +} + +TEST(IncludeFixer, Typo) { + EXPECT_EQ("#include \nstd::string foo;\n", + runIncludeFixer("std::string foo;\n")); + + EXPECT_EQ( + "// comment\n#include \n#include \"foo.h\"\nstd::string foo;\n" + "#include \"bar.h\"\n", + runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n" + "#include \"bar.h\"\n")); + + EXPECT_EQ("#include \n#include \"foo.h\"\nstd::string foo;\n", + runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n")); + + EXPECT_EQ( + "#include \n#include \"foo.h\"\nstd::string::size_type foo;\n", + runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n")); + + // The fixed xrefs db doesn't know how to handle string without std::. + EXPECT_EQ("string foo;\n", runIncludeFixer("string foo;\n")); +} + +TEST(IncludeFixer, IncompleteType) { + EXPECT_EQ( + "#include \n#include \"foo.h\"\n" + "namespace std {\nclass string;\n}\nstring foo;\n", + runIncludeFixer("#include \"foo.h\"\n" + "namespace std {\nclass string;\n}\nstring foo;\n")); +} + +} // namespace +} // namespace include_fixer +} // namespace clang