xref: /llvm-project/clang-tools-extra/clang-doc/Mapper.cpp (revision 1b7631a699e6af7f497548a1ceb5be0570c60ed0)
1e975a473SJulie Hockett //===-- Mapper.cpp - ClangDoc Mapper ----------------------------*- C++ -*-===//
2e975a473SJulie Hockett //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e975a473SJulie Hockett //
7e975a473SJulie Hockett //===----------------------------------------------------------------------===//
8e975a473SJulie Hockett 
9e975a473SJulie Hockett #include "Mapper.h"
10e975a473SJulie Hockett #include "BitcodeWriter.h"
11e975a473SJulie Hockett #include "Serialize.h"
12e975a473SJulie Hockett #include "clang/AST/Comment.h"
13e975a473SJulie Hockett #include "clang/Index/USRGeneration.h"
14e975a473SJulie Hockett #include "llvm/ADT/StringExtras.h"
15*1b7631a6SPeterChou1 #include "llvm/ADT/StringSet.h"
16*1b7631a6SPeterChou1 #include "llvm/Support/Mutex.h"
17e975a473SJulie Hockett 
18e975a473SJulie Hockett namespace clang {
19e975a473SJulie Hockett namespace doc {
20e975a473SJulie Hockett 
21*1b7631a6SPeterChou1 static llvm::StringSet<> USRVisited;
22*1b7631a6SPeterChou1 static llvm::sys::Mutex USRVisitedGuard;
23*1b7631a6SPeterChou1 
24*1b7631a6SPeterChou1 template <typename T> bool isTypedefAnonRecord(const T *D) {
25*1b7631a6SPeterChou1   if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
26*1b7631a6SPeterChou1     return C->getTypedefNameForAnonDecl();
27*1b7631a6SPeterChou1   }
28*1b7631a6SPeterChou1   return false;
29*1b7631a6SPeterChou1 }
30*1b7631a6SPeterChou1 
31e975a473SJulie Hockett void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
32e975a473SJulie Hockett   TraverseDecl(Context.getTranslationUnitDecl());
33e975a473SJulie Hockett }
34e975a473SJulie Hockett 
35*1b7631a6SPeterChou1 template <typename T>
36*1b7631a6SPeterChou1 bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
37e975a473SJulie Hockett   // If we're looking a decl not in user files, skip this decl.
38e975a473SJulie Hockett   if (D->getASTContext().getSourceManager().isInSystemHeader(D->getLocation()))
39e975a473SJulie Hockett     return true;
40e975a473SJulie Hockett 
414955422fSJulie Hockett   // Skip function-internal decls.
42c84155ffSJulie Hockett   if (D->getParentFunctionOrMethod())
434955422fSJulie Hockett     return true;
444955422fSJulie Hockett 
45e975a473SJulie Hockett   llvm::SmallString<128> USR;
46e975a473SJulie Hockett   // If there is an error generating a USR for the decl, skip this decl.
47e975a473SJulie Hockett   if (index::generateUSRForDecl(D, USR))
48e975a473SJulie Hockett     return true;
49*1b7631a6SPeterChou1   // Prevent Visiting USR twice
50*1b7631a6SPeterChou1   {
51*1b7631a6SPeterChou1     std::lock_guard<llvm::sys::Mutex> Guard(USRVisitedGuard);
52*1b7631a6SPeterChou1     StringRef Visited = USR.str();
53*1b7631a6SPeterChou1     if (USRVisited.count(Visited) && !isTypedefAnonRecord<T>(D))
54*1b7631a6SPeterChou1       return true;
55*1b7631a6SPeterChou1     // We considered a USR to be visited only when its defined
56*1b7631a6SPeterChou1     if (IsDefinition)
57*1b7631a6SPeterChou1       USRVisited.insert(Visited);
58*1b7631a6SPeterChou1   }
59665e9676SDiego Astiazaran   bool IsFileInRootDir;
60665e9676SDiego Astiazaran   llvm::SmallString<128> File =
61665e9676SDiego Astiazaran       getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
62665e9676SDiego Astiazaran   auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()),
63665e9676SDiego Astiazaran                                getLine(D, D->getASTContext()), File,
64665e9676SDiego Astiazaran                                IsFileInRootDir, CDCtx.PublicOnly);
65eb50a2e8SJulie Hockett 
668899c29bSJulie Hockett   // A null in place of I indicates that the serializer is skipping this decl
678899c29bSJulie Hockett   // for some reason (e.g. we're only reporting public decls).
68097aedc9SJulie Hockett   if (I.first)
69097aedc9SJulie Hockett     CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I.first->USR)),
70097aedc9SJulie Hockett                              serialize::serialize(I.first));
71097aedc9SJulie Hockett   if (I.second)
72097aedc9SJulie Hockett     CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I.second->USR)),
73097aedc9SJulie Hockett                              serialize::serialize(I.second));
74e975a473SJulie Hockett   return true;
75e975a473SJulie Hockett }
76e975a473SJulie Hockett 
77e975a473SJulie Hockett bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl *D) {
78*1b7631a6SPeterChou1   return mapDecl(D, /*isDefinition=*/true);
79e975a473SJulie Hockett }
80e975a473SJulie Hockett 
81*1b7631a6SPeterChou1 bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) {
82*1b7631a6SPeterChou1   return mapDecl(D, D->isThisDeclarationADefinition());
83*1b7631a6SPeterChou1 }
84e975a473SJulie Hockett 
85*1b7631a6SPeterChou1 bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) {
86*1b7631a6SPeterChou1   return mapDecl(D, D->isThisDeclarationADefinition());
87*1b7631a6SPeterChou1 }
88e975a473SJulie Hockett 
89e975a473SJulie Hockett bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl *D) {
90*1b7631a6SPeterChou1   return mapDecl(D, D->isThisDeclarationADefinition());
91e975a473SJulie Hockett }
92e975a473SJulie Hockett 
93e975a473SJulie Hockett bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) {
94e975a473SJulie Hockett   // Don't visit CXXMethodDecls twice
9562e48ed1SKazu Hirata   if (isa<CXXMethodDecl>(D))
96e975a473SJulie Hockett     return true;
97*1b7631a6SPeterChou1   return mapDecl(D, D->isThisDeclarationADefinition());
98e975a473SJulie Hockett }
99e975a473SJulie Hockett 
10021fb70c6SBrett Wilson bool MapASTVisitor::VisitTypedefDecl(const TypedefDecl *D) {
101*1b7631a6SPeterChou1   return mapDecl(D, /*isDefinition=*/true);
10221fb70c6SBrett Wilson }
10321fb70c6SBrett Wilson 
10421fb70c6SBrett Wilson bool MapASTVisitor::VisitTypeAliasDecl(const TypeAliasDecl *D) {
105*1b7631a6SPeterChou1   return mapDecl(D, /*isDefinition=*/true);
10621fb70c6SBrett Wilson }
10721fb70c6SBrett Wilson 
108e975a473SJulie Hockett comments::FullComment *
109e975a473SJulie Hockett MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
110e975a473SJulie Hockett   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
111e975a473SJulie Hockett   // FIXME: Move setAttached to the initial comment parsing.
112e975a473SJulie Hockett   if (Comment) {
113e975a473SJulie Hockett     Comment->setAttached();
114e975a473SJulie Hockett     return Comment->parse(Context, nullptr, D);
115e975a473SJulie Hockett   }
116e975a473SJulie Hockett   return nullptr;
117e975a473SJulie Hockett }
118e975a473SJulie Hockett 
119e975a473SJulie Hockett int MapASTVisitor::getLine(const NamedDecl *D,
120e975a473SJulie Hockett                            const ASTContext &Context) const {
12143465bf3SStephen Kelly   return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine();
122e975a473SJulie Hockett }
123e975a473SJulie Hockett 
124665e9676SDiego Astiazaran llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
125665e9676SDiego Astiazaran                                               const ASTContext &Context,
126665e9676SDiego Astiazaran                                               llvm::StringRef RootDir,
127665e9676SDiego Astiazaran                                               bool &IsFileInRootDir) const {
128665e9676SDiego Astiazaran   llvm::SmallString<128> File(Context.getSourceManager()
12943465bf3SStephen Kelly                                   .getPresumedLoc(D->getBeginLoc())
130665e9676SDiego Astiazaran                                   .getFilename());
131665e9676SDiego Astiazaran   IsFileInRootDir = false;
132cc4ecfd6SKazu Hirata   if (RootDir.empty() || !File.starts_with(RootDir))
133665e9676SDiego Astiazaran     return File;
134665e9676SDiego Astiazaran   IsFileInRootDir = true;
135665e9676SDiego Astiazaran   llvm::SmallString<128> Prefix(RootDir);
136665e9676SDiego Astiazaran   // replace_path_prefix removes the exact prefix provided. The result of
137665e9676SDiego Astiazaran   // calling that function on ("A/B/C.c", "A/B", "") would be "/C.c", which
138665e9676SDiego Astiazaran   // starts with a / that is not needed. This is why we fix Prefix so it always
139665e9676SDiego Astiazaran   // ends with a / and the result has the desired format.
140665e9676SDiego Astiazaran   if (!llvm::sys::path::is_separator(Prefix.back()))
141665e9676SDiego Astiazaran     Prefix += llvm::sys::path::get_separator();
142665e9676SDiego Astiazaran   llvm::sys::path::replace_path_prefix(File, Prefix, "");
143665e9676SDiego Astiazaran   return File;
144e975a473SJulie Hockett }
145e975a473SJulie Hockett 
146e975a473SJulie Hockett } // namespace doc
147e975a473SJulie Hockett } // namespace clang
148