12#include "clang/Basic/SourceLocation.h"
13#include "clang/Basic/SourceManager.h"
14#include "clang/Frontend/CompilerInstance.h"
15#include "clang/Lex/DirectoryLookup.h"
16#include "clang/Lex/HeaderSearch.h"
17#include "clang/Lex/PPCallbacks.h"
18#include "clang/Lex/Preprocessor.h"
19#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
20#include "llvm/ADT/SmallVector.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/Support/Path.h"
33 : SM(
CI.getSourceManager()),
41 OptionalFileEntryRef
File,
44 const clang::Module * ,
46 SrcMgr::CharacteristicKind FileKind)
override {
47 auto MainFID = SM.getMainFileID();
59 Inc.Resolved = std::string(
62 Inc.HashOffset = SM.getFileOffset(HashLoc);
64 SM.getLineNumber(SM.getFileID(HashLoc), Inc.HashOffset) - 1;
65 Inc.FileKind = FileKind;
66 Inc.Directive = IncludeTok.getIdentifierInfo()->getPPKeywordID();
69 Inc.HeaderID =
static_cast<unsigned>(HID);
71 if (
auto StdlibHeader = tooling::stdlib::Header::named(Inc.Written)) {
74 if (!llvm::is_contained(IDs, HID))
78 Out->MainFileIncludesBySpelling[Inc.Written].push_back(
84 auto IncludingFileEntry = SM.getFileEntryRefForID(SM.getFileID(HashLoc));
85 if (!IncludingFileEntry) {
86 assert(SM.getBufferName(HashLoc).starts_with(
"<") &&
87 "Expected #include location to be a file or <built-in>");
89 IncludingFileEntry = SM.getFileEntryRefForID(MainFID);
98 SrcMgr::CharacteristicKind FileType,
99 FileID PrevFID)
override {
101 case PPCallbacks::EnterFile:
103 if (BuiltinFile.isInvalid() && SM.isWrittenInBuiltinFile(
Loc)) {
104 BuiltinFile = SM.getFileID(
Loc);
105 InBuiltinFile =
true;
108 case PPCallbacks::ExitFile: {
110 if (PrevFID == BuiltinFile)
111 InBuiltinFile =
false;
114 case PPCallbacks::RenameFile:
115 case PPCallbacks::SystemHeaderPragma:
123 bool inMainFile()
const {
return Level == 1; }
125 const SourceManager &SM;
129 bool InBuiltinFile =
false;
135 return Include.starts_with(
"<") || Include.starts_with(
"\"");
144 llvm::StringRef HintPath) {
149 return U.takeError();
153 return IncludePath.takeError();
154 if (!IncludePath->empty())
155 return HeaderFile{std::move(*IncludePath),
true};
159 return Resolved.takeError();
160 return HeaderFile{std::move(*Resolved),
false};
172 llvm::SmallVector<SymbolInclude, 1> Headers;
173 for (
const auto &Include : Includes)
174 Headers.push_back({Include.IncludeHeader, Include.supportedDirectives()});
179 auto &SM =
CI.getSourceManager();
180 MainFileEntry = SM.getFileEntryForID(SM.getMainFileID());
181 auto Collector = std::make_unique<RecordHeaders>(
CI,
this);
182 CI.getPreprocessor().addPPCallbacks(std::move(Collector));
187 for (
const auto &
Dir :
188 CI.getPreprocessor().getHeaderSearchInfo().search_dir_range()) {
189 if (
Dir.getLookupType() == DirectoryLookup::LT_NormalDir)
191 SM.getFileManager().getCanonicalName(*
Dir.getDirRef()));
196std::optional<IncludeStructure::HeaderID>
199 if (
Entry == MainFileEntry) {
202 auto It = UIDToIndex.find(
Entry->getUniqueID());
203 if (It == UIDToIndex.end())
210 if (&
Entry.getFileEntry() == MainFileEntry) {
211 if (RealPathNames.front().empty())
212 RealPathNames.front() = MainFileEntry->tryGetRealPathName().str();
215 auto R = UIDToIndex.try_emplace(
219 RealPathNames.emplace_back();
221 std::string &RealPathName = RealPathNames[
static_cast<unsigned>(Result)];
222 if (RealPathName.empty())
223 RealPathName =
Entry.getFileEntry().tryGetRealPathName().str();
227llvm::DenseMap<IncludeStructure::HeaderID, unsigned>
230 llvm::DenseMap<HeaderID, unsigned> Result;
231 assert(
static_cast<unsigned>(
Root) < RealPathNames.size());
233 std::vector<IncludeStructure::HeaderID> CurrentLevel;
234 CurrentLevel.push_back(
Root);
235 llvm::DenseSet<IncludeStructure::HeaderID> Seen;
239 std::vector<IncludeStructure::HeaderID> PreviousLevel;
240 for (
unsigned Level = 1; !CurrentLevel.empty(); ++Level) {
241 PreviousLevel.clear();
242 PreviousLevel.swap(CurrentLevel);
243 for (
const auto &
Parent : PreviousLevel) {
245 if (Seen.insert(Child).second) {
246 CurrentLevel.push_back(Child);
247 Result[Child] = Level;
255llvm::SmallVector<const Inclusion *>
257 llvm::SmallVector<const Inclusion *> Includes;
258 for (
auto Idx : MainFileIncludesBySpelling.lookup(Spelling))
264 IncludedHeaders.insert(Inc.
Written);
266 IncludedHeaders.insert(Inc.
Resolved);
273 assert(InsertedHeader.
valid());
274 if (!HeaderSearchInfo && !InsertedHeader.
Verbatim)
276 if (FileName == DeclaringHeader || FileName == InsertedHeader.
File)
278 auto Included = [&](llvm::StringRef Header) {
279 return IncludedHeaders.contains(Header);
281 return !Included(DeclaringHeader) && !Included(InsertedHeader.
File);
284std::optional<std::string>
286 llvm::StringRef IncludingFile)
const {
287 assert(InsertedHeader.
valid());
289 return InsertedHeader.
File;
291 std::string Suggested;
292 if (HeaderSearchInfo) {
293 Suggested = HeaderSearchInfo->suggestPathToFileForDiagnostics(
294 InsertedHeader.
File, BuildDir, IncludingFile, &
IsAngled);
297 StringRef IncludingDir = llvm::sys::path::parent_path(IncludingFile);
298 SmallString<256> RelFile(InsertedHeader.
File);
300 llvm::sys::path::replace_path_prefix(RelFile, IncludingDir,
"./");
301 Suggested = llvm::sys::path::convert_to_slash(
302 llvm::sys::path::remove_leading_dotslash(RelFile));
305 if (llvm::sys::path::is_absolute(Suggested))
308 Suggested =
"<" + Suggested +
">";
310 Suggested =
"\"" + Suggested +
"\"";
314std::optional<TextEdit>
316 tooling::IncludeDirective
Directive)
const {
317 std::optional<TextEdit>
Edit;
319 Inserter.insert(VerbatimHeader.trim(
"\"<>"),
320 VerbatimHeader.starts_with(
"<"),
Directive))
enum clang::clangd::@1062::Bracket::Direction Dir
CompiledFragmentImpl & Out
bool IsAngled
true if this was an include with angle brackets
const MacroDirective * Directive
std::unique_ptr< CompilerInvocation > CI
void addExisting(const Inclusion &Inc)
std::optional< std::string > calculateIncludePath(const HeaderFile &InsertedHeader, llvm::StringRef IncludingFile) const
Determines the preferred way to #include a file, taking into account the search path.
bool shouldInsertInclude(PathRef DeclaringHeader, const HeaderFile &InsertedHeader) const
Checks whether to add an #include of the header into File.
std::optional< TextEdit > insert(llvm::StringRef VerbatimHeader, tooling::IncludeDirective Directive) const
Calculates an edit that inserts VerbatimHeader into code.
llvm::SmallVector< const Inclusion * > mainFileIncludesWithSpelling(llvm::StringRef Spelling) const
static const HeaderID MainFileID
llvm::DenseMap< HeaderID, unsigned > includeDepth(HeaderID Root=MainFileID) const
std::vector< Inclusion > MainFileIncludes
std::vector< std::string > SearchPathsCanonical
llvm::DenseMap< tooling::stdlib::Header, llvm::SmallVector< HeaderID > > StdlibHeaders
HeaderID getOrCreateID(FileEntryRef Entry)
void collect(const CompilerInstance &CI)
std::optional< HeaderID > getID(const FileEntry *Entry) const
llvm::DenseMap< HeaderID, SmallVector< HeaderID > > IncludeChildren
static llvm::Expected< std::string > includeSpelling(const URI &U)
Gets the preferred spelling of this file for #include, if there is one, e.g.
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
bool isLiteralInclude(llvm::StringRef Include)
Returns true if Include is literal include like "path" or <path>.
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
bool operator==(const Inclusion &LHS, const Inclusion &RHS)
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
SourceLocation translatePreamblePatchLocation(SourceLocation Loc, const SourceManager &SM)
Translates locations inside preamble patch to their main-file equivalent using presumed locations.
llvm::Expected< HeaderFile > toHeaderFile(llvm::StringRef Header, llvm::StringRef HintPath)
Creates a HeaderFile from Header which can be either a URI or a literal include.
std::optional< std::string > getCanonicalPath(const FileEntryRef F, FileManager &FileMgr)
Get the canonical path of F.
llvm::SmallVector< SymbolInclude, 1 > getRankedIncludes(const Symbol &Sym)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
TextEdit replacementToEdit(llvm::StringRef Code, const tooling::Replacement &R)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A set of edits generated for a single file.
tok::PPKeywordKind Directive
SrcMgr::CharacteristicKind FileKind
The class presents a C++ symbol, e.g.
llvm::SmallVector< IncludeHeaderWithReferences, 1 > IncludeHeaders
One Symbol can potentially be included via different headers.
A single C++ or preprocessor token.