blob: 3d82fe5ee6bbb6f87bbdb8c01e0ece95fe1690a3 [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*/,
37 const Module * /*Imported*/) override {
Eric Liu6c8e8582018-02-26 08:32:13 +000038 WrittenHeaders.insert(
39 (IsAngled ? "<" + FileName + ">" : "\"" + FileName + "\"").str());
Eric Liuc5105f92018-02-16 14:15:55 +000040 if (File != nullptr && !File->tryGetRealPathName().empty())
Eric Liu6c8e8582018-02-26 08:32:13 +000041 ResolvedHeaders.insert(File->tryGetRealPathName());
Eric Liuc5105f92018-02-16 14:15:55 +000042 }
43
44private:
Eric Liu6c8e8582018-02-26 08:32:13 +000045 llvm::StringSet<> &WrittenHeaders;
46 llvm::StringSet<> &ResolvedHeaders;
Eric Liuc5105f92018-02-16 14:15:55 +000047};
48
49} // namespace
50
Eric Liu6c8e8582018-02-26 08:32:13 +000051bool isLiteralInclude(llvm::StringRef Include) {
52 return Include.startswith("<") || Include.startswith("\"");
53}
54
55bool HeaderFile::valid() const {
56 return (Verbatim && isLiteralInclude(File)) ||
57 (!Verbatim && llvm::sys::path::is_absolute(File));
58}
59
Eric Liuc5105f92018-02-16 14:15:55 +000060/// FIXME(ioeric): we might not want to insert an absolute include path if the
61/// path is not shortened.
62llvm::Expected<std::string>
Eric Liu709bde82018-02-19 18:48:44 +000063calculateIncludePath(llvm::StringRef File, llvm::StringRef Code,
Eric Liu6c8e8582018-02-26 08:32:13 +000064 const HeaderFile &DeclaringHeader,
65 const HeaderFile &InsertedHeader,
Eric Liu709bde82018-02-19 18:48:44 +000066 const tooling::CompileCommand &CompileCommand,
67 IntrusiveRefCntPtr<vfs::FileSystem> FS) {
Eric Liu6c8e8582018-02-26 08:32:13 +000068 assert(llvm::sys::path::is_absolute(File));
69 assert(DeclaringHeader.valid() && InsertedHeader.valid());
70 if (File == DeclaringHeader.File || File == InsertedHeader.File)
Eric Liu709bde82018-02-19 18:48:44 +000071 return "";
72 FS->setCurrentWorkingDirectory(CompileCommand.Directory);
73
Eric Liuc5105f92018-02-16 14:15:55 +000074 // Set up a CompilerInstance and create a preprocessor to collect existing
75 // #include headers in \p Code. Preprocesor also provides HeaderSearch with
76 // which we can calculate the shortest include path for \p Header.
77 std::vector<const char *> Argv;
78 for (const auto &S : CompileCommand.CommandLine)
79 Argv.push_back(S.c_str());
80 IgnoringDiagConsumer IgnoreDiags;
81 auto CI = clang::createInvocationFromCommandLine(
82 Argv,
83 CompilerInstance::createDiagnostics(new DiagnosticOptions(), &IgnoreDiags,
84 false),
85 FS);
86 if (!CI)
87 return llvm::make_error<llvm::StringError>(
88 "Failed to create a compiler instance for " + File,
89 llvm::inconvertibleErrorCode());
90 CI->getFrontendOpts().DisableFree = false;
91 // Parse the main file to get all existing #includes in the file, and then we
92 // can make sure the same header (even with different include path) is not
93 // added more than once.
94 CI->getPreprocessorOpts().SingleFileParseMode = true;
95
Haojian Wub603a5e2018-02-22 13:35:01 +000096 // The diagnostic options must be set before creating a CompilerInstance.
97 CI->getDiagnosticOpts().IgnoreWarnings = true;
Eric Liuc5105f92018-02-16 14:15:55 +000098 auto Clang = prepareCompilerInstance(
99 std::move(CI), /*Preamble=*/nullptr,
100 llvm::MemoryBuffer::getMemBuffer(Code, File),
101 std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags);
Eric Liuc5105f92018-02-16 14:15:55 +0000102
103 if (Clang->getFrontendOpts().Inputs.empty())
104 return llvm::make_error<llvm::StringError>(
105 "Empty frontend action inputs empty for file " + File,
106 llvm::inconvertibleErrorCode());
107 PreprocessOnlyAction Action;
108 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
109 return llvm::make_error<llvm::StringError>(
110 "Failed to begin preprocessor only action for file " + File,
111 llvm::inconvertibleErrorCode());
Eric Liu6c8e8582018-02-26 08:32:13 +0000112 llvm::StringSet<> WrittenHeaders;
113 llvm::StringSet<> ResolvedHeaders;
Eric Liuc5105f92018-02-16 14:15:55 +0000114 Clang->getPreprocessor().addPPCallbacks(
Eric Liu6c8e8582018-02-26 08:32:13 +0000115 llvm::make_unique<RecordHeaders>(WrittenHeaders, ResolvedHeaders));
Eric Liuc5105f92018-02-16 14:15:55 +0000116 if (!Action.Execute())
117 return llvm::make_error<llvm::StringError>(
118 "Failed to execute preprocessor only action for file " + File,
119 llvm::inconvertibleErrorCode());
Eric Liu6c8e8582018-02-26 08:32:13 +0000120 auto Included = [&](llvm::StringRef Header) {
121 return WrittenHeaders.find(Header) != WrittenHeaders.end() ||
122 ResolvedHeaders.find(Header) != ResolvedHeaders.end();
123 };
124 if (Included(DeclaringHeader.File) || Included(InsertedHeader.File))
125 return "";
Eric Liuc5105f92018-02-16 14:15:55 +0000126
127 auto &HeaderSearchInfo = Clang->getPreprocessor().getHeaderSearchInfo();
128 bool IsSystem = false;
Eric Liu6c8e8582018-02-26 08:32:13 +0000129
130 if (InsertedHeader.Verbatim)
131 return InsertedHeader.File;
132
Eric Liuc5105f92018-02-16 14:15:55 +0000133 std::string Suggested = HeaderSearchInfo.suggestPathToFileForDiagnostics(
Eric Liu6c8e8582018-02-26 08:32:13 +0000134 InsertedHeader.File, CompileCommand.Directory, &IsSystem);
Eric Liuc5105f92018-02-16 14:15:55 +0000135 if (IsSystem)
136 Suggested = "<" + Suggested + ">";
137 else
138 Suggested = "\"" + Suggested + "\"";
139
Eric Liu6c8e8582018-02-26 08:32:13 +0000140 log("Suggested #include for " + InsertedHeader.File + " is: " + Suggested);
Eric Liuc5105f92018-02-16 14:15:55 +0000141 return Suggested;
142}
143
144} // namespace clangd
145} // namespace clang