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/Support/Error.h"
20 void MapASTVisitor::HandleTranslationUnit(ASTContext
&Context
) {
21 TraverseDecl(Context
.getTranslationUnitDecl());
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()))
29 // Skip function-internal decls.
30 if (D
->getParentFunctionOrMethod())
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
, USR
))
38 llvm::SmallString
<128> File
=
39 getFile(D
, D
->getASTContext(), CDCtx
.SourceRoot
, IsFileInRootDir
);
40 auto I
= serialize::emitInfo(D
, getComment(D
, D
->getASTContext()),
41 getLine(D
, D
->getASTContext()), File
,
42 IsFileInRootDir
, CDCtx
.PublicOnly
);
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).
47 CDCtx
.ECtx
->reportResult(llvm::toHex(llvm::toStringRef(I
.first
->USR
)),
48 serialize::serialize(I
.first
));
50 CDCtx
.ECtx
->reportResult(llvm::toHex(llvm::toStringRef(I
.second
->USR
)),
51 serialize::serialize(I
.second
));
55 bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl
*D
) {
59 bool MapASTVisitor::VisitRecordDecl(const RecordDecl
*D
) { return mapDecl(D
); }
61 bool MapASTVisitor::VisitEnumDecl(const EnumDecl
*D
) { return mapDecl(D
); }
63 bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl
*D
) {
67 bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl
*D
) {
68 // Don't visit CXXMethodDecls twice
69 if (isa
<CXXMethodDecl
>(D
))
74 comments::FullComment
*
75 MapASTVisitor::getComment(const NamedDecl
*D
, const ASTContext
&Context
) const {
76 RawComment
*Comment
= Context
.getRawCommentForDeclNoCache(D
);
77 // FIXME: Move setAttached to the initial comment parsing.
79 Comment
->setAttached();
80 return Comment
->parse(Context
, nullptr, D
);
85 int MapASTVisitor::getLine(const NamedDecl
*D
,
86 const ASTContext
&Context
) const {
87 return Context
.getSourceManager().getPresumedLoc(D
->getBeginLoc()).getLine();
90 llvm::SmallString
<128> MapASTVisitor::getFile(const NamedDecl
*D
,
91 const ASTContext
&Context
,
92 llvm::StringRef RootDir
,
93 bool &IsFileInRootDir
) const {
94 llvm::SmallString
<128> File(Context
.getSourceManager()
95 .getPresumedLoc(D
->getBeginLoc())
97 IsFileInRootDir
= false;
98 if (RootDir
.empty() || !File
.startswith(RootDir
))
100 IsFileInRootDir
= true;
101 llvm::SmallString
<128> Prefix(RootDir
);
102 // replace_path_prefix removes the exact prefix provided. The result of
103 // calling that function on ("A/B/C.c", "A/B", "") would be "/C.c", which
104 // starts with a / that is not needed. This is why we fix Prefix so it always
105 // ends with a / and the result has the desired format.
106 if (!llvm::sys::path::is_separator(Prefix
.back()))
107 Prefix
+= llvm::sys::path::get_separator();
108 llvm::sys::path::replace_path_prefix(File
, Prefix
, "");