1 //===-- Mapper.cpp - ClangDoc Mapper ----------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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/ADT/StringSet.h"
16 #include "llvm/Support/Mutex.h"
21 static llvm::StringSet
<> USRVisited
;
22 static llvm::sys::Mutex USRVisitedGuard
;
24 template <typename T
> bool isTypedefAnonRecord(const T
*D
) {
25 if (const auto *C
= dyn_cast
<CXXRecordDecl
>(D
)) {
26 return C
->getTypedefNameForAnonDecl();
31 void MapASTVisitor::HandleTranslationUnit(ASTContext
&Context
) {
32 TraverseDecl(Context
.getTranslationUnitDecl());
36 bool MapASTVisitor::mapDecl(const T
*D
, bool IsDefinition
) {
37 // If we're looking a decl not in user files, skip this decl.
38 if (D
->getASTContext().getSourceManager().isInSystemHeader(D
->getLocation()))
41 // Skip function-internal decls.
42 if (D
->getParentFunctionOrMethod())
45 llvm::SmallString
<128> USR
;
46 // If there is an error generating a USR for the decl, skip this decl.
47 if (index::generateUSRForDecl(D
, USR
))
49 // Prevent Visiting USR twice
51 std::lock_guard
<llvm::sys::Mutex
> Guard(USRVisitedGuard
);
52 StringRef Visited
= USR
.str();
53 if (USRVisited
.count(Visited
) && !isTypedefAnonRecord
<T
>(D
))
55 // We considered a USR to be visited only when its defined
57 USRVisited
.insert(Visited
);
60 llvm::SmallString
<128> File
=
61 getFile(D
, D
->getASTContext(), CDCtx
.SourceRoot
, IsFileInRootDir
);
62 auto I
= serialize::emitInfo(D
, getComment(D
, D
->getASTContext()),
63 getLine(D
, D
->getASTContext()), File
,
64 IsFileInRootDir
, CDCtx
.PublicOnly
);
66 // A null in place of I indicates that the serializer is skipping this decl
67 // for some reason (e.g. we're only reporting public decls).
69 CDCtx
.ECtx
->reportResult(llvm::toHex(llvm::toStringRef(I
.first
->USR
)),
70 serialize::serialize(I
.first
));
72 CDCtx
.ECtx
->reportResult(llvm::toHex(llvm::toStringRef(I
.second
->USR
)),
73 serialize::serialize(I
.second
));
77 bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl
*D
) {
78 return mapDecl(D
, /*isDefinition=*/true);
81 bool MapASTVisitor::VisitRecordDecl(const RecordDecl
*D
) {
82 return mapDecl(D
, D
->isThisDeclarationADefinition());
85 bool MapASTVisitor::VisitEnumDecl(const EnumDecl
*D
) {
86 return mapDecl(D
, D
->isThisDeclarationADefinition());
89 bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl
*D
) {
90 return mapDecl(D
, D
->isThisDeclarationADefinition());
93 bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl
*D
) {
94 // Don't visit CXXMethodDecls twice
95 if (isa
<CXXMethodDecl
>(D
))
97 return mapDecl(D
, D
->isThisDeclarationADefinition());
100 bool MapASTVisitor::VisitTypedefDecl(const TypedefDecl
*D
) {
101 return mapDecl(D
, /*isDefinition=*/true);
104 bool MapASTVisitor::VisitTypeAliasDecl(const TypeAliasDecl
*D
) {
105 return mapDecl(D
, /*isDefinition=*/true);
108 comments::FullComment
*
109 MapASTVisitor::getComment(const NamedDecl
*D
, const ASTContext
&Context
) const {
110 RawComment
*Comment
= Context
.getRawCommentForDeclNoCache(D
);
111 // FIXME: Move setAttached to the initial comment parsing.
113 Comment
->setAttached();
114 return Comment
->parse(Context
, nullptr, D
);
119 int MapASTVisitor::getLine(const NamedDecl
*D
,
120 const ASTContext
&Context
) const {
121 return Context
.getSourceManager().getPresumedLoc(D
->getBeginLoc()).getLine();
124 llvm::SmallString
<128> MapASTVisitor::getFile(const NamedDecl
*D
,
125 const ASTContext
&Context
,
126 llvm::StringRef RootDir
,
127 bool &IsFileInRootDir
) const {
128 llvm::SmallString
<128> File(Context
.getSourceManager()
129 .getPresumedLoc(D
->getBeginLoc())
131 IsFileInRootDir
= false;
132 if (RootDir
.empty() || !File
.starts_with(RootDir
))
134 IsFileInRootDir
= true;
135 llvm::SmallString
<128> Prefix(RootDir
);
136 // replace_path_prefix removes the exact prefix provided. The result of
137 // calling that function on ("A/B/C.c", "A/B", "") would be "/C.c", which
138 // starts with a / that is not needed. This is why we fix Prefix so it always
139 // ends with a / and the result has the desired format.
140 if (!llvm::sys::path::is_separator(Prefix
.back()))
141 Prefix
+= llvm::sys::path::get_separator();
142 llvm::sys::path::replace_path_prefix(File
, Prefix
, "");