Index: lib/Sema/CMakeLists.txt =================================================================== --- lib/Sema/CMakeLists.txt +++ lib/Sema/CMakeLists.txt @@ -61,4 +61,5 @@ clangBasic clangEdit clangLex + clangFormat ) Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -23,6 +23,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/LangOptions.h" +#include "clang/Format/Format.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/Preprocessor.h" @@ -35,6 +36,7 @@ #include "clang/Sema/SemaInternal.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/TypoCorrection.h" +#include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/TinyPtrVector.h" @@ -4982,6 +4984,26 @@ return (IsSystem ? '<' : '"') + Path + (IsSystem ? '>' : '"'); } +/// Hint where to insert a missing #include into the file. +/// We reuse clang-format's header-insertion logic, always with LLVM-style. +static FixItHint AddInclude(FileID File, SourceManager &SourceMgr, + std::string &IncludeTarget) { + StringRef Code = SourceMgr.getBufferData(File); + auto StartOfFile = SourceMgr.getLocForStartOfFile(File); + tooling::Replacement Unplaced(SourceMgr.getFilename(StartOfFile), UINT_MAX, 0, + "#include " + IncludeTarget); + auto Reps = cleanupAroundReplacements(Code, tooling::Replacements(Unplaced), + format::getLLVMStyle()); + if (Reps) + for (const auto &Placed : *Reps) { + auto Begin = StartOfFile.getLocWithOffset(Placed.getOffset()); + auto End = Begin.getLocWithOffset(Placed.getLength()); + return FixItHint::CreateReplacement(SourceRange(Begin, End), + Placed.getReplacementText()); + } + return FixItHint(); +} + void Sema::diagnoseMissingImport(SourceLocation UseLoc, NamedDecl *Decl, SourceLocation DeclLoc, ArrayRef Modules, @@ -5006,12 +5028,10 @@ PP.getModuleHeaderToIncludeForDiagnostics(UseLoc, DeclLoc)) { // The right way to make the declaration visible is to include a header; // suggest doing so. - // - // FIXME: Find a smart place to suggest inserting a #include, and add - // a FixItHint there. + std::string Include = getIncludeStringForHeader(PP, E); Diag(UseLoc, diag::err_module_unimported_use_header) - << (int)MIK << Decl << Modules[0]->getFullModuleName() - << getIncludeStringForHeader(PP, E); + << (int)MIK << Decl << Modules[0]->getFullModuleName() << Include + << AddInclude(SourceMgr.getFileID(UseLoc), SourceMgr, Include); } else { // FIXME: Add a FixItHint that imports the corresponding module. Diag(UseLoc, diag::err_module_unimported_use) Index: test/Modules/suggest-include.cpp =================================================================== --- test/Modules/suggest-include.cpp +++ test/Modules/suggest-include.cpp @@ -1,7 +1,11 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -I%S/Inputs/suggest-include %s -verify +// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -I%S/Inputs/suggest-include %s -fdiagnostics-parseable-fixits 2>&1 | FileCheck --match-full-lines --implicit-check-not "fix-it:{{.*}}" %s #include "empty.h" // import the module file +// CHECK: fix-it:"{{.*}}":{[[@LINE]]:1-[[@LINE]]:84}:"#include \"usetextual1.h\"\n" +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:1-[[@LINE-1]]:84}:"#include \"usetextual2.h\"\n" +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:84}:"#include \"usetextual3.h\"\n" // expected-note@usetextual1.h:2 {{previous}} // expected-note@textual2.h:1 {{previous}}