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