blob: af2445531795079b46a1b7faac4cf518f61fc5d5 [file] [log] [blame]
Eric Liuc5105f92018-02-16 14:15:55 +00001//===--- Headers.cpp - Include headers ---------------------------*- C++-*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "Headers.h"
11#include "Compiler.h"
12#include "Logger.h"
13#include "clang/Frontend/CompilerInstance.h"
14#include "clang/Frontend/CompilerInvocation.h"
15#include "clang/Frontend/FrontendActions.h"
16#include "clang/Lex/HeaderSearch.h"
17#include "clang/Lex/PreprocessorOptions.h"
18#include "clang/Tooling/CompilationDatabase.h"
Eric Liu709bde82018-02-19 18:48:44 +000019#include "llvm/Support/Path.h"
Eric Liuc5105f92018-02-16 14:15:55 +000020
21namespace clang {
22namespace clangd {
23namespace {
24
25class RecordHeaders : public PPCallbacks {
26public:
Eric Liu6c8e8582018-02-26 08:32:13 +000027 RecordHeaders(llvm::StringSet<> &WrittenHeaders,
28 llvm::StringSet<> &ResolvedHeaders)
29 : WrittenHeaders(WrittenHeaders), ResolvedHeaders(ResolvedHeaders) {}
Eric Liuc5105f92018-02-16 14:15:55 +000030
31 void InclusionDirective(SourceLocation /*HashLoc*/,
32 const Token & /*IncludeTok*/,
Eric Liu6c8e8582018-02-26 08:32:13 +000033 llvm::StringRef FileName, bool IsAngled,
Eric Liuc5105f92018-02-16 14:15:55 +000034 CharSourceRange /*FilenameRange*/,
35 const FileEntry *File, llvm::StringRef /*SearchPath*/,
36 llvm::StringRef /*RelativePath*/,
Julie Hockett546943f2018-05-10 19:13:14 +000037 const Module * /*Imported*/,
38 SrcMgr::CharacteristicKind /*FileType*/) override {
Eric Liu6c8e8582018-02-26 08:32:13 +000039 WrittenHeaders.insert(
40 (IsAngled ? "<" + FileName + ">" : "\"" + FileName + "\"").str());
Eric Liuc5105f92018-02-16 14:15:55 +000041 if (File != nullptr && !File->tryGetRealPathName().empty())
Eric Liu6c8e8582018-02-26 08:32:13 +000042 ResolvedHeaders.insert(File->tryGetRealPathName());
Eric Liuc5105f92018-02-16 14:15:55 +000043 }
44
45private:
Eric Liu6c8e8582018-02-26 08:32:13 +000046 llvm::StringSet<> &WrittenHeaders;
47 llvm::StringSet<> &ResolvedHeaders;
Eric Liuc5105f92018-02-16 14:15:55 +000048};
49
50} // namespace
51
Eric Liu6c8e8582018-02-26 08:32:13 +000052bool isLiteralInclude(llvm::StringRef Include) {
53 return Include.startswith("<") || Include.startswith("\"");
54}
55
56bool HeaderFile::valid() const {
57 return (Verbatim && isLiteralInclude(File)) ||
58 (!Verbatim && llvm::sys::path::is_absolute(File));
59}
60
Eric Liuc5105f92018-02-16 14:15:55 +000061/// FIXME(ioeric): we might not want to insert an absolute include path if the
62/// path is not shortened.
63llvm::Expected<std::string>
Eric Liu709bde82018-02-19 18:48:44 +000064calculateIncludePath(llvm::StringRef File, llvm::StringRef Code,
Eric Liu6c8e8582018-02-26 08:32:13 +000065 const HeaderFile &DeclaringHeader,
66 const HeaderFile &InsertedHeader,
Eric Liu709bde82018-02-19 18:48:44 +000067 const tooling::CompileCommand &CompileCommand,
68 IntrusiveRefCntPtr<vfs::FileSystem> FS) {
Eric Liu6c8e8582018-02-26 08:32:13 +000069 assert(llvm::sys::path::is_absolute(File));
70 assert(DeclaringHeader.valid() && InsertedHeader.valid());
71 if (File == DeclaringHeader.File || File == InsertedHeader.File)
Eric Liu709bde82018-02-19 18:48:44 +000072 return "";
73 FS->setCurrentWorkingDirectory(CompileCommand.Directory);
74
Eric Liuc5105f92018-02-16 14:15:55 +000075 // Set up a CompilerInstance and create a preprocessor to collect existing
76 // #include headers in \p Code. Preprocesor also provides HeaderSearch with
77 // which we can calculate the shortest include path for \p Header.
78 std::vector<const char *> Argv;
79 for (const auto &S : CompileCommand.CommandLine)
80 Argv.push_back(S.c_str());
81 IgnoringDiagConsumer IgnoreDiags;
82 auto CI = clang::createInvocationFromCommandLine(
83 Argv,
84 CompilerInstance::createDiagnostics(new DiagnosticOptions(), &IgnoreDiags,
85 false),
86 FS);
87 if (!CI)
88 return llvm::make_error<llvm::StringError>(
89 "Failed to create a compiler instance for " + File,
90 llvm::inconvertibleErrorCode());
91 CI->getFrontendOpts().DisableFree = false;
92 // Parse the main file to get all existing #includes in the file, and then we
93 // can make sure the same header (even with different include path) is not
94 // added more than once.
95 CI->getPreprocessorOpts().SingleFileParseMode = true;
96
Haojian Wub603a5e2018-02-22 13:35:01 +000097 // The diagnostic options must be set before creating a CompilerInstance.
98 CI->getDiagnosticOpts().IgnoreWarnings = true;
Eric Liuc5105f92018-02-16 14:15:55 +000099 auto Clang = prepareCompilerInstance(
100 std::move(CI), /*Preamble=*/nullptr,
101 llvm::MemoryBuffer::getMemBuffer(Code, File),
102 std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags);
Eric Liuc5105f92018-02-16 14:15:55 +0000103
104 if (Clang->getFrontendOpts().Inputs.empty())
105 return llvm::make_error<llvm::StringError>(
106 "Empty frontend action inputs empty for file " + File,
107 llvm::inconvertibleErrorCode());
108 PreprocessOnlyAction Action;
109 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
110 return llvm::make_error<llvm::StringError>(
111 "Failed to begin preprocessor only action for file " + File,
112 llvm::inconvertibleErrorCode());
Eric Liu6c8e8582018-02-26 08:32:13 +0000113 llvm::StringSet<> WrittenHeaders;
114 llvm::StringSet<> ResolvedHeaders;
Eric Liuc5105f92018-02-16 14:15:55 +0000115 Clang->getPreprocessor().addPPCallbacks(
Eric Liu6c8e8582018-02-26 08:32:13 +0000116 llvm::make_unique<RecordHeaders>(WrittenHeaders, ResolvedHeaders));
Eric Liuc5105f92018-02-16 14:15:55 +0000117 if (!Action.Execute())
118 return llvm::make_error<llvm::StringError>(
119 "Failed to execute preprocessor only action for file " + File,
120 llvm::inconvertibleErrorCode());
Eric Liu6c8e8582018-02-26 08:32:13 +0000121 auto Included = [&](llvm::StringRef Header) {
122 return WrittenHeaders.find(Header) != WrittenHeaders.end() ||
123 ResolvedHeaders.find(Header) != ResolvedHeaders.end();
124 };
125 if (Included(DeclaringHeader.File) || Included(InsertedHeader.File))
126 return "";
Eric Liuc5105f92018-02-16 14:15:55 +0000127
128 auto &HeaderSearchInfo = Clang->getPreprocessor().getHeaderSearchInfo();
129 bool IsSystem = false;
Eric Liu6c8e8582018-02-26 08:32:13 +0000130
131 if (InsertedHeader.Verbatim)
132 return InsertedHeader.File;
133
Eric Liuc5105f92018-02-16 14:15:55 +0000134 std::string Suggested = HeaderSearchInfo.suggestPathToFileForDiagnostics(
Eric Liu6c8e8582018-02-26 08:32:13 +0000135 InsertedHeader.File, CompileCommand.Directory, &IsSystem);
Eric Liuc5105f92018-02-16 14:15:55 +0000136 if (IsSystem)
137 Suggested = "<" + Suggested + ">";
138 else
139 Suggested = "\"" + Suggested + "\"";
140
Eric Liu6c8e8582018-02-26 08:32:13 +0000141 log("Suggested #include for " + InsertedHeader.File + " is: " + Suggested);
Eric Liuc5105f92018-02-16 14:15:55 +0000142 return Suggested;
143}
144
145} // namespace clangd
146} // namespace clang