1 | //===-- Mapper.cpp - ClangDoc Mapper ----------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "Mapper.h" |
10 | #include "BitcodeWriter.h" |
11 | #include "Serialize.h" |
12 | #include "clang/AST/Comment.h" |
13 | #include "clang/Index/USRGeneration.h" |
14 | #include "llvm/ADT/StringExtras.h" |
15 | #include "llvm/Support/Error.h" |
16 | |
17 | namespace clang { |
18 | namespace doc { |
19 | |
20 | void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) { |
21 | TraverseDecl(Context.getTranslationUnitDecl()); |
22 | } |
23 | |
24 | template <typename T> bool MapASTVisitor::mapDecl(const T *D) { |
25 | // If we're looking a decl not in user files, skip this decl. |
26 | if (D->getASTContext().getSourceManager().isInSystemHeader(D->getLocation())) |
27 | return true; |
28 | |
29 | // Skip function-internal decls. |
30 | if (D->getParentFunctionOrMethod()) |
31 | return true; |
32 | |
33 | llvm::SmallString<128> USR; |
34 | // If there is an error generating a USR for the decl, skip this decl. |
35 | if (index::generateUSRForDecl(D, Buf&: USR)) |
36 | return true; |
37 | bool IsFileInRootDir; |
38 | llvm::SmallString<128> File = |
39 | getFile(D, Context: D->getASTContext(), RootDir: CDCtx.SourceRoot, IsFileInRootDir); |
40 | auto I = serialize::emitInfo(D, getComment(D, Context: D->getASTContext()), |
41 | getLine(D, Context: D->getASTContext()), File, |
42 | IsFileInRootDir, CDCtx.PublicOnly); |
43 | |
44 | // A null in place of I indicates that the serializer is skipping this decl |
45 | // for some reason (e.g. we're only reporting public decls). |
46 | if (I.first) |
47 | CDCtx.ECtx->reportResult(Key: llvm::toHex(llvm::toStringRef(I.first->USR)), |
48 | Value: serialize::serialize(I&: I.first)); |
49 | if (I.second) |
50 | CDCtx.ECtx->reportResult(Key: llvm::toHex(llvm::toStringRef(I.second->USR)), |
51 | Value: serialize::serialize(I&: I.second)); |
52 | return true; |
53 | } |
54 | |
55 | bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl *D) { |
56 | return mapDecl(D); |
57 | } |
58 | |
59 | bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); } |
60 | |
61 | bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); } |
62 | |
63 | bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl *D) { |
64 | return mapDecl(D); |
65 | } |
66 | |
67 | bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) { |
68 | // Don't visit CXXMethodDecls twice |
69 | if (isa<CXXMethodDecl>(Val: D)) |
70 | return true; |
71 | return mapDecl(D); |
72 | } |
73 | |
74 | bool MapASTVisitor::VisitTypedefDecl(const TypedefDecl *D) { |
75 | return mapDecl(D); |
76 | } |
77 | |
78 | bool MapASTVisitor::VisitTypeAliasDecl(const TypeAliasDecl *D) { |
79 | return mapDecl(D); |
80 | } |
81 | |
82 | comments::FullComment * |
83 | MapASTVisitor::(const NamedDecl *D, const ASTContext &Context) const { |
84 | RawComment * = Context.getRawCommentForDeclNoCache(D); |
85 | // FIXME: Move setAttached to the initial comment parsing. |
86 | if (Comment) { |
87 | Comment->setAttached(); |
88 | return Comment->parse(Context, nullptr, D); |
89 | } |
90 | return nullptr; |
91 | } |
92 | |
93 | int MapASTVisitor::getLine(const NamedDecl *D, |
94 | const ASTContext &Context) const { |
95 | return Context.getSourceManager().getPresumedLoc(Loc: D->getBeginLoc()).getLine(); |
96 | } |
97 | |
98 | llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D, |
99 | const ASTContext &Context, |
100 | llvm::StringRef RootDir, |
101 | bool &IsFileInRootDir) const { |
102 | llvm::SmallString<128> File(Context.getSourceManager() |
103 | .getPresumedLoc(Loc: D->getBeginLoc()) |
104 | .getFilename()); |
105 | IsFileInRootDir = false; |
106 | if (RootDir.empty() || !File.starts_with(Prefix: RootDir)) |
107 | return File; |
108 | IsFileInRootDir = true; |
109 | llvm::SmallString<128> Prefix(RootDir); |
110 | // replace_path_prefix removes the exact prefix provided. The result of |
111 | // calling that function on ("A/B/C.c", "A/B", "") would be "/C.c", which |
112 | // starts with a / that is not needed. This is why we fix Prefix so it always |
113 | // ends with a / and the result has the desired format. |
114 | if (!llvm::sys::path::is_separator(value: Prefix.back())) |
115 | Prefix += llvm::sys::path::get_separator(); |
116 | llvm::sys::path::replace_path_prefix(Path&: File, OldPrefix: Prefix, NewPrefix: "" ); |
117 | return File; |
118 | } |
119 | |
120 | } // namespace doc |
121 | } // namespace clang |
122 | |