blob: db8029b2ac05d86ea9e21f664c5ed8d02df5d90e [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:
27 RecordHeaders(std::set<std::string> &Headers) : Headers(Headers) {}
28
29 void InclusionDirective(SourceLocation /*HashLoc*/,
30 const Token & /*IncludeTok*/,
31 llvm::StringRef /*FileName*/, bool /*IsAngled*/,
32 CharSourceRange /*FilenameRange*/,
33 const FileEntry *File, llvm::StringRef /*SearchPath*/,
34 llvm::StringRef /*RelativePath*/,
35 const Module * /*Imported*/) override {
36 if (File != nullptr && !File->tryGetRealPathName().empty())
37 Headers.insert(File->tryGetRealPathName());
38 }
39
40private:
41 std::set<std::string> &Headers;
42};
43
44} // namespace
45
46/// FIXME(ioeric): we might not want to insert an absolute include path if the
47/// path is not shortened.
48llvm::Expected<std::string>
Eric Liu709bde82018-02-19 18:48:44 +000049calculateIncludePath(llvm::StringRef File, llvm::StringRef Code,
50 llvm::StringRef Header,
51 const tooling::CompileCommand &CompileCommand,
52 IntrusiveRefCntPtr<vfs::FileSystem> FS) {
53 assert(llvm::sys::path::is_absolute(File) &&
54 llvm::sys::path::is_absolute(Header));
55
56 if (File == Header)
57 return "";
58 FS->setCurrentWorkingDirectory(CompileCommand.Directory);
59
Eric Liuc5105f92018-02-16 14:15:55 +000060 // Set up a CompilerInstance and create a preprocessor to collect existing
61 // #include headers in \p Code. Preprocesor also provides HeaderSearch with
62 // which we can calculate the shortest include path for \p Header.
63 std::vector<const char *> Argv;
64 for (const auto &S : CompileCommand.CommandLine)
65 Argv.push_back(S.c_str());
66 IgnoringDiagConsumer IgnoreDiags;
67 auto CI = clang::createInvocationFromCommandLine(
68 Argv,
69 CompilerInstance::createDiagnostics(new DiagnosticOptions(), &IgnoreDiags,
70 false),
71 FS);
72 if (!CI)
73 return llvm::make_error<llvm::StringError>(
74 "Failed to create a compiler instance for " + File,
75 llvm::inconvertibleErrorCode());
76 CI->getFrontendOpts().DisableFree = false;
77 // Parse the main file to get all existing #includes in the file, and then we
78 // can make sure the same header (even with different include path) is not
79 // added more than once.
80 CI->getPreprocessorOpts().SingleFileParseMode = true;
81
82 auto Clang = prepareCompilerInstance(
83 std::move(CI), /*Preamble=*/nullptr,
84 llvm::MemoryBuffer::getMemBuffer(Code, File),
85 std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags);
86 auto &DiagOpts = Clang->getDiagnosticOpts();
87 DiagOpts.IgnoreWarnings = true;
88
89 if (Clang->getFrontendOpts().Inputs.empty())
90 return llvm::make_error<llvm::StringError>(
91 "Empty frontend action inputs empty for file " + File,
92 llvm::inconvertibleErrorCode());
93 PreprocessOnlyAction Action;
94 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
95 return llvm::make_error<llvm::StringError>(
96 "Failed to begin preprocessor only action for file " + File,
97 llvm::inconvertibleErrorCode());
98 std::set<std::string> ExistingHeaders;
99 Clang->getPreprocessor().addPPCallbacks(
100 llvm::make_unique<RecordHeaders>(ExistingHeaders));
101 if (!Action.Execute())
102 return llvm::make_error<llvm::StringError>(
103 "Failed to execute preprocessor only action for file " + File,
104 llvm::inconvertibleErrorCode());
105 if (ExistingHeaders.find(Header) != ExistingHeaders.end()) {
106 return llvm::make_error<llvm::StringError>(
107 Header + " is already included in " + File,
108 llvm::inconvertibleErrorCode());
109 }
110
111 auto &HeaderSearchInfo = Clang->getPreprocessor().getHeaderSearchInfo();
112 bool IsSystem = false;
113 std::string Suggested = HeaderSearchInfo.suggestPathToFileForDiagnostics(
114 Header, CompileCommand.Directory, &IsSystem);
115 if (IsSystem)
116 Suggested = "<" + Suggested + ">";
117 else
118 Suggested = "\"" + Suggested + "\"";
119
120 log("Suggested #include for " + Header + " is: " + Suggested);
121 return Suggested;
122}
123
124} // namespace clangd
125} // namespace clang