summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/Headers.cpp
blob: db8029b2ac05d86ea9e21f664c5ed8d02df5d90e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//===--- Headers.cpp - Include headers ---------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Headers.h"
#include "Compiler.h"
#include "Logger.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/Support/Path.h"

namespace clang {
namespace clangd {
namespace {

class RecordHeaders : public PPCallbacks {
public:
  RecordHeaders(std::set<std::string> &Headers) : Headers(Headers) {}

  void InclusionDirective(SourceLocation /*HashLoc*/,
                          const Token & /*IncludeTok*/,
                          llvm::StringRef /*FileName*/, bool /*IsAngled*/,
                          CharSourceRange /*FilenameRange*/,
                          const FileEntry *File, llvm::StringRef /*SearchPath*/,
                          llvm::StringRef /*RelativePath*/,
                          const Module * /*Imported*/) override {
    if (File != nullptr && !File->tryGetRealPathName().empty())
      Headers.insert(File->tryGetRealPathName());
  }

private:
  std::set<std::string> &Headers;
};

} // namespace

/// FIXME(ioeric): we might not want to insert an absolute include path if the
/// path is not shortened.
llvm::Expected<std::string>
calculateIncludePath(llvm::StringRef File, llvm::StringRef Code,
                     llvm::StringRef Header,
                     const tooling::CompileCommand &CompileCommand,
                     IntrusiveRefCntPtr<vfs::FileSystem> FS) {
  assert(llvm::sys::path::is_absolute(File) &&
         llvm::sys::path::is_absolute(Header));

  if (File == Header)
    return "";
  FS->setCurrentWorkingDirectory(CompileCommand.Directory);

  // Set up a CompilerInstance and create a preprocessor to collect existing
  // #include headers in \p Code. Preprocesor also provides HeaderSearch with
  // which we can calculate the shortest include path for \p Header.
  std::vector<const char *> Argv;
  for (const auto &S : CompileCommand.CommandLine)
    Argv.push_back(S.c_str());
  IgnoringDiagConsumer IgnoreDiags;
  auto CI = clang::createInvocationFromCommandLine(
      Argv,
      CompilerInstance::createDiagnostics(new DiagnosticOptions(), &IgnoreDiags,
                                          false),
      FS);
  if (!CI)
    return llvm::make_error<llvm::StringError>(
        "Failed to create a compiler instance for " + File,
        llvm::inconvertibleErrorCode());
  CI->getFrontendOpts().DisableFree = false;
  // Parse the main file to get all existing #includes in the file, and then we
  // can make sure the same header (even with different include path) is not
  // added more than once.
  CI->getPreprocessorOpts().SingleFileParseMode = true;

  auto Clang = prepareCompilerInstance(
      std::move(CI), /*Preamble=*/nullptr,
      llvm::MemoryBuffer::getMemBuffer(Code, File),
      std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags);
  auto &DiagOpts = Clang->getDiagnosticOpts();
  DiagOpts.IgnoreWarnings = true;

  if (Clang->getFrontendOpts().Inputs.empty())
    return llvm::make_error<llvm::StringError>(
        "Empty frontend action inputs empty for file " + File,
        llvm::inconvertibleErrorCode());
  PreprocessOnlyAction Action;
  if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
    return llvm::make_error<llvm::StringError>(
        "Failed to begin preprocessor only action for file " + File,
        llvm::inconvertibleErrorCode());
  std::set<std::string> ExistingHeaders;
  Clang->getPreprocessor().addPPCallbacks(
      llvm::make_unique<RecordHeaders>(ExistingHeaders));
  if (!Action.Execute())
    return llvm::make_error<llvm::StringError>(
        "Failed to execute preprocessor only action for file " + File,
        llvm::inconvertibleErrorCode());
  if (ExistingHeaders.find(Header) != ExistingHeaders.end()) {
    return llvm::make_error<llvm::StringError>(
        Header + " is already included in " + File,
        llvm::inconvertibleErrorCode());
  }

  auto &HeaderSearchInfo = Clang->getPreprocessor().getHeaderSearchInfo();
  bool IsSystem = false;
  std::string Suggested = HeaderSearchInfo.suggestPathToFileForDiagnostics(
      Header, CompileCommand.Directory, &IsSystem);
  if (IsSystem)
    Suggested = "<" + Suggested + ">";
  else
    Suggested = "\"" + Suggested + "\"";

  log("Suggested #include for " + Header + " is: " + Suggested);
  return Suggested;
}

} // namespace clangd
} // namespace clang
OpenPOWER on IntegriCloud