diff --git a/README.md b/README.md index aa12e330..54145c8c 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ Regarding the openness of languages, you can refer to the table below: | JavaScript | Y | Y | RELEASE | | Go | Y | Y | RELEASE | | XML | Y | Y | RELEASE | -| Cfamily | N | N | BETA | +| Cfamily | Y | Y | BETA | | SQL | Y | Y | BETA | | Swift | N | N | BETA | | Properties | Y | Y | BETA | diff --git a/README_cn.md b/README_cn.md index 27d478d8..ef410e35 100644 --- a/README_cn.md +++ b/README_cn.md @@ -102,7 +102,7 @@ CodeFuse-Query 包括**Sparrow CLI **和CodeFuse-Query**在线服务Query中心* | JavaScript | Y | Y | RELEASE | | Go | Y | Y | RELEASE | | XML | Y | Y | RELEASE | -| Cfamily | N | N | BETA | +| Cfamily | Y | Y | BETA | | SQL | Y | Y | BETA | | Swift | N | N | BETA | | Properties | Y | Y | BETA | diff --git a/language/cfamily/extractor/.clang-format b/language/cfamily/extractor/.clang-format new file mode 100644 index 00000000..afe3f0d7 --- /dev/null +++ b/language/cfamily/extractor/.clang-format @@ -0,0 +1,6 @@ +# We'll use defaults from the LLVM style, but with 4 columns indentation. +BasedOnStyle: LLVM +IndentWidth: 4 +## The column limit. +## A column limit of 0 means that there is no column limit. In this case, clang-format will respect the input’s line breaking decisions within statements. +ColumnLimit: 100 diff --git a/language/cfamily/extractor/.clang-tidy b/language/cfamily/extractor/.clang-tidy new file mode 100644 index 00000000..430dbd8e --- /dev/null +++ b/language/cfamily/extractor/.clang-tidy @@ -0,0 +1,28 @@ +# Specify a comma-separated list of positive and negative globs: positive globs add subsets of checks, while negative globs (prefixed with "-") remove them. +# Current header guard does not follow preferred style [llvm-header-guard] so disable it +Checks: '-*,clang-diagnostic-*,llvm-*,-llvm-header-guard,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,-misc-no-recursion,readability-identifier-naming' +CheckOptions: + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.EnumCase + value: CamelCase + - key: readability-identifier-naming.FunctionCase + value: camelBack + - key: readability-identifier-naming.MemberCase + value: camelBack + - key: readability-identifier-naming.PrivateMemberPrefix + value: '_' + - key: readability-identifier-naming.ProtectedMemberPrefix + value: '_' + - key: readability-identifier-naming.ParameterCase + value: camelBack + - key: readability-identifier-naming.UnionCase + value: CamelCase + - key: readability-identifier-naming.VariableCase + value: camelBack + - key: readability-identifier-naming.IgnoreMainLikeFunctions + value: 1 + - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors + value: 1 + - key: modernize-use-default-member-init.UseAssignment + value: 1 diff --git a/language/cfamily/extractor/.gitignore b/language/cfamily/extractor/.gitignore new file mode 100644 index 00000000..941879cb --- /dev/null +++ b/language/cfamily/extractor/.gitignore @@ -0,0 +1,17 @@ +#OS X specific files. +.DS_store + +# Nested build directory +/cmake-build-* +/Tests/cmake-build-* + +# VS2017 and VSCode config files. +.vscode +.vs + +# CLion project configuration +/.idea + +# Ignore all bazel-* symlinks. There is no full list since this can change +# based on the name of the directory bazel is cloned into. +/bazel-* \ No newline at end of file diff --git a/language/cfamily/extractor/AST/ASTUtil.cpp b/language/cfamily/extractor/AST/ASTUtil.cpp new file mode 100644 index 00000000..b7bcdceb --- /dev/null +++ b/language/cfamily/extractor/AST/ASTUtil.cpp @@ -0,0 +1,21 @@ + + +#include "ASTUtil.hpp" + +using namespace llvm; + +/// Get Root-Relative path of the given file +/// \param absoluteFile +/// \param relativeFile +/// \return true if root-relative path solved, or false +bool getRootRelativePath(StringRef &absolutePath, StringRef &relativePath) { + SmallString<256> cwd; + sys::fs::current_path(cwd); + auto npos = cwd.rfind(sys::path::get_separator().data()); + if (absolutePath.contains(cwd.substr(0, npos))) { + relativePath = absolutePath.substr(npos); + return true; + } + relativePath = absolutePath; + return false; +} diff --git a/language/cfamily/extractor/AST/ASTUtil.hpp b/language/cfamily/extractor/AST/ASTUtil.hpp new file mode 100644 index 00000000..eef69126 --- /dev/null +++ b/language/cfamily/extractor/AST/ASTUtil.hpp @@ -0,0 +1,12 @@ + + +#ifndef COREF_CFAMILY_SRC_EXTRACTOR_ASTUTIL_HPP +#define COREF_CFAMILY_SRC_EXTRACTOR_ASTUTIL_HPP + +#include + +using namespace llvm; + +bool getRootRelativePath(StringRef &absolutePath, StringRef &relativePath); + +#endif // COREF_CFAMILY_SRC_EXTRACTOR_ASTUTIL_HPP diff --git a/language/cfamily/extractor/AST/CorefASTConsumer.hpp b/language/cfamily/extractor/AST/CorefASTConsumer.hpp new file mode 100644 index 00000000..26db094f --- /dev/null +++ b/language/cfamily/extractor/AST/CorefASTConsumer.hpp @@ -0,0 +1,120 @@ + +#ifndef COREF_CFAMILY_SRC_EXTRACTOR_COREFASTCONSUMER_HPP +#define COREF_CFAMILY_SRC_EXTRACTOR_COREFASTCONSUMER_HPP + +#include "ASTUtil.hpp" +#include "CorefASTVisitor.hpp" +#include +#include + +using namespace llvm; + +namespace coref { + +class CorefASTConsumer : public clang::ASTConsumer { + private: + const CorefUri _corefUri; + std::set _visitedFileIds; + std::unique_ptr _blacklistDirFilter; + + inline bool isInBlackListDir(StringRef absolutePath) { + return _blacklistDirFilter && _blacklistDirFilter->match(absolutePath); + } + + protected: + /// An override HandleTranslationUnit + /// This method is called when the ASTs for entire translation unit have + /// been parsed. \param astContext + void HandleTranslationUnit(clang::ASTContext &astContext) final { + coref::StorageFacade::transaction([&]() mutable { + // insert entry for Program table, entry could be existed already. + auto programOid = CorefUri::generateCorpusOId(_corefUri.getCorpus()); + coref::StorageFacade::insertClassObj(Program{programOid, _corefUri.getCorpus()}); + + std::unordered_map newVisitFileMap{}; + coref::CorefASTVisitor visitor(astContext, _corefUri, programOid, newVisitFileMap); + + auto decls = astContext.getTranslationUnitDecl()->decls(); + auto &sourceMngr = astContext.getSourceManager(); + for (auto &decl : decls) { + auto curFileId = sourceMngr.getFileID(decl->getLocation()); + + if (_visitedFileIds.find(curFileId) != _visitedFileIds.end()) { + // skip visited files + continue; + } + + // skip AST nodes having invalid source location + if (!decl->getLocation().isValid()) { + continue; + } + + // todo: need to verify the accuracy of function + // "isInSystemHeader" & "isInSystemMacro" + if (sourceMngr.isInSystemHeader(decl->getLocation()) || + sourceMngr.isInSystemMacro(decl->getLocation())) { + // skip AST nodes in system headers + _visitedFileIds.insert(curFileId); + continue; + } + + StringRef absolutePath = sourceMngr.getFilename(decl->getLocation()); + if (absolutePath.empty()) + continue; + if (isInBlackListDir(absolutePath)) { + _visitedFileIds.insert(curFileId); + continue; + } + + StringRef relativePath; + getRootRelativePath(absolutePath, relativePath); + // note: relativePath would be an absolute path when handling a + // framework file. + + auto fileOid = + CorefUri::generateFileOId(_corefUri.getCorpus(), std::string(relativePath)); + if (coref::StorageFacade::checkFileObjExist(programOid, fileOid)) { + // skip file that have been added in Sqlite DB + _visitedFileIds.insert(curFileId); + continue; + } + + if (newVisitFileMap.find(fileOid) == newVisitFileMap.end()) { + File file{fileOid, std::string(relativePath), + std::string(sys::path::extension(relativePath)), + std::string(sys::path::filename(relativePath)), programOid}; + newVisitFileMap.insert({fileOid, std::move(file)}); + } + visitor.setExtractFileOid(fileOid); + visitor.TraverseDecl(decl); + } + + // traverse the newFileStruct and update the File table + for (auto &[fileOid, f] : newVisitFileMap) { + coref::StorageFacade::insertClassObj(std::move(f)); + } + + return true; + }); + } + + public: + CorefASTConsumer(const CorefUri &corefUri, std::vector &blacklistDir) + : _corefUri(corefUri) { + std::stringstream regexStr; + bool first = true; + for (auto dir : blacklistDir) { + if (first) { + regexStr << "(" << dir << ")"; + first = false; + } else { + regexStr << "|(" << dir << ")"; + } + } + _blacklistDirFilter = std::make_unique(regexStr.str()); + _blacklistDirFilter->isValid(); + }; +}; +} // namespace coref + +#endif // COREF_CFAMILY_SRC_EXTRACTOR_COREFASTCONSUMER_HPP diff --git a/language/cfamily/extractor/AST/CorefASTFrontendAction.hpp b/language/cfamily/extractor/AST/CorefASTFrontendAction.hpp new file mode 100644 index 00000000..b589d256 --- /dev/null +++ b/language/cfamily/extractor/AST/CorefASTFrontendAction.hpp @@ -0,0 +1,60 @@ + +#ifndef COREF_CFAMILY_SRC_EXTRACTOR_COREFASTFRONTENDACTION_HPP +#define COREF_CFAMILY_SRC_EXTRACTOR_COREFASTFRONTENDACTION_HPP + +#include "../Coref/CorefUri.hpp" +#include "CorefASTConsumer.hpp" +#include +#include + +namespace coref { + +class CorefASTFrontendAction : public clang::ASTFrontendAction { + private: + std::string _corpus; + std::vector& _blacklistDir; + + public: + /// Constructor of CorefASTFrontendAction Class + /// \param corpusName + explicit CorefASTFrontendAction(const std::string &corpusName, std::vector& blacklistDir) + : clang::ASTFrontendAction(), _corpus(corpusName), _blacklistDir(blacklistDir) {} + + protected: + /// An override of CreateASTConsumer + /// \param ci + /// \param input_file + /// \return + std::unique_ptr CreateASTConsumer(clang::CompilerInstance &ci, + llvm::StringRef inputFile) override { + // TODO fill in corpus info + auto absoluteInputFile = std::filesystem::absolute(inputFile.str()); + auto corefUri = CorefUri(_corpus, absoluteInputFile.string()); + llvm::outs() << "Consuming file: " << absoluteInputFile.string() + << " of corpus: " << _corpus << "\n"; + return std::unique_ptr(new coref::CorefASTConsumer(corefUri, _blacklistDir)); + } +}; + +struct CorefFrontendActionFactory : public clang::tooling::FrontendActionFactory { + /// Constructor of CorefFrontendActionFactory Class + /// \param corpusName + explicit CorefFrontendActionFactory(const std::string &corpusName, std::vector& blacklistDir) + : clang::tooling::FrontendActionFactory(), _corpusName(corpusName), _blacklistDir(blacklistDir) {} + + /// a Factory method to create clang::FrontendAction + /// \return + std::unique_ptr create() override { + auto *action = + dynamic_cast(new CorefASTFrontendAction(_corpusName, _blacklistDir)); + return std::unique_ptr(action); + } + + private: + std::string _corpusName; + std::vector& _blacklistDir; +}; + +} // namespace coref + +#endif // COREF_CFAMILY_SRC_EXTRACTOR_COREFASTFRONTENDACTION_HPP diff --git a/language/cfamily/extractor/AST/CorefASTVisitor.cpp b/language/cfamily/extractor/AST/CorefASTVisitor.cpp new file mode 100644 index 00000000..ae502c74 --- /dev/null +++ b/language/cfamily/extractor/AST/CorefASTVisitor.cpp @@ -0,0 +1,743 @@ + + +#include "CorefASTVisitor.hpp" +#include "ASTUtil.hpp" +#include "Coref/Utils/Switcher.h" +#include "SymbolNameGenerator.hpp" +#include "clang/AST/ParentMapContext.h" +#include + +using namespace coref; + +CorefASTVisitor::CorefASTVisitor(const clang::ASTContext &context, const CorefUri &corefUri, + CorefOid programOid, + std::unordered_map &newVisitFileMap) + : _context(context), _corefUri(corefUri), _programOid(programOid), + _newVisitFileMap(newVisitFileMap) {} + +const clang::ASTContext &CorefASTVisitor::getContext() const { return _context; } + +template CorefOid CorefASTVisitor::getParentOid(const NodeT &node) { + clang::DynTypedNodeList parentNode = + const_cast(getContext()).getParents(node); + + CorefOid parentOid = -1; + if (parentNode.size() > 0) { + if (const auto *parentStmt = + clang::dyn_cast_or_null(parentNode[0].get())) { + parentOid = getOid(parentStmt); + } else if (const auto *parentDecl = + clang::dyn_cast_or_null(parentNode[0].get())) { + parentOid = getOid(parentDecl); + } else if (const auto *parentTypeloc = clang::dyn_cast_or_null( + parentNode[0].get())) { + parentOid = getOid(*parentTypeloc); + } else { + llvm::errs() << "unknown parent node!\n"; + } + } + return parentOid; +} + +CorefOid CorefASTVisitor::getOid(const clang::Stmt *stmt) const { + if (stmt == nullptr) { + return -1L; + } + std::string signature = SignatureGenerator::generate(stmt, getContext()); + return CorefUri::generateOId(_corefUri.getCorpus(), signature); +} + +CorefOid CorefASTVisitor::getOid(const clang::Decl *decl) const { + if (decl == nullptr) { + return -1L; + } + std::string signature = SignatureGenerator::generate(decl); + return CorefUri::generateOId(_corefUri.getCorpus(), signature); +} + +CorefOid CorefASTVisitor::getOid(const clang::Type *type) const { + if (type == nullptr) { + return -1L; + } + std::string signature = SignatureGenerator::generate(type, getContext()); + return CorefUri::generateOId(_corefUri.getCorpus(), signature); +} + +CorefOid CorefASTVisitor::getOid(const clang::QualType *qualType) const { + if (qualType == nullptr) { + return -1L; + } + return getOid(qualType->getTypePtrOrNull()); +} + +CorefOid CorefASTVisitor::getOid(const clang::TypeLoc &typeLoc) const { + std::string signature = SignatureGenerator::generate(typeLoc, getContext()); + return CorefUri::generateOId(_corefUri.getCorpus(), signature); +} + +std::string CorefASTVisitor::astToString(const clang::Stmt *stmt) const { + std::string outputStr; + llvm::raw_string_ostream outputStream(outputStr); + stmt->printPretty(outputStream, nullptr, getContext().getPrintingPolicy(), 0); + return outputStr; +} + +std::string CorefASTVisitor::astToString(const clang::Decl *decl) const { + std::string outputStr; + llvm::raw_string_ostream outputStream(outputStr); + decl->print(outputStream, 0); + return outputStr; +} + +std::string CorefASTVisitor::astToString(const clang::Type *type) const { + return SignatureGenerator::generate(type, getContext()); +} + +std::string CorefASTVisitor::astToString(const clang::QualType *qualType) const { + return SignatureGenerator::generate(qualType, getContext()); +} + +Location CorefASTVisitor::getLocation(const clang::SourceRange &sourceRange) const { + int startLineNumber = + getContext().getSourceManager().getPresumedLineNumber(sourceRange.getBegin()); + int startColumnNumber = + getContext().getSourceManager().getPresumedColumnNumber(sourceRange.getBegin()); + + int endLineNumber = getContext().getSourceManager().getPresumedLineNumber(sourceRange.getEnd()); + int endColumnNumber = + getContext().getSourceManager().getPresumedColumnNumber(sourceRange.getEnd()); + + std::string signature = + SignatureGenerator::generateLocationSignature(_context.getSourceManager(), sourceRange); + + // TODO location oid is the same as the node's oid for now, should improve it + auto oid = CorefUri::generateOId(_corefUri.getCorpus(), signature); + + return Location{oid, _fileOid, startLineNumber, startColumnNumber, + endLineNumber, endColumnNumber}; +} + +CorefOid CorefASTVisitor::getLocationOid(const CorefOid nodeOid, + const clang::SourceRange sourceRange) { + if (_locationMap.find(nodeOid) != _locationMap.end()) + return _locationMap[nodeOid]; + auto loc = getLocation(sourceRange); + coref::StorageFacade::insertClassObj(loc); + _locationMap[nodeOid] = loc.oid; + return loc.oid; +} + +/// Visit Statement Nodes +/// the parent node of a stmt could be stmt or decl. +/// _parentMap is used to record order index. +/// \param stmt +/// \return +bool CorefASTVisitor::VisitStmt(const clang::Stmt *stmt) { + const auto stmtOid = getOid(stmt); + auto parentOid = getParentOid(*stmt); + int indexOrder = 0; + if (parentOid > 0) { + if (_parentMap.count(parentOid) > 0) { + indexOrder = _parentMap[parentOid]; + indexOrder++; + _parentMap[parentOid] = indexOrder; + } else { + _parentMap.insert({parentOid, indexOrder}); + } + } + + coref::StorageFacade::insertClassObj(Statement{stmtOid, parentOid, indexOrder, + getLocationOid(stmtOid, stmt->getSourceRange()), + stmt->getStmtClassName(), astToString(stmt)}); + return true; +} + +/// Declaration Statement - local variable decl +/// \param stmt +/// \return +bool CorefASTVisitor::VisitDeclStmt(const clang::DeclStmt *stmt) { + coref::StorageFacade::insertClassObj(DeclarationStatement{getOid(stmt)}); + return true; +} + +bool CorefASTVisitor::VisitIfStmt(const clang::IfStmt *stmt) { + IfStatement stmtModel{getOid(stmt), getOid(stmt->getCond()), getOid(stmt->getThen())}; + if (stmt->getElse() != nullptr) { + ElseStatementInIf elseStmtModel{getOid(stmt->getElse()), getOid(stmt)}; + coref::StorageFacade::insertClassObj(std::move(elseStmtModel)); + } + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitSwitchStmt(const clang::SwitchStmt *stmt) { + coref::SwitchStatement stmtModel{getOid(stmt), getOid(stmt->getCond()), + getOid(stmt->getSwitchCaseList())}; + + // Cases are not stored in order, sort them first. + // (In fact they seem to be stored in reverse order, don't rely on this) + std::vector cases; + for (const clang::SwitchCase *_case = stmt->getSwitchCaseList(); _case; + _case = _case->getNextSwitchCase()) + cases.push_back(_case); + + const clang::SwitchCase *nextCase; + for (auto reverseIt = cases.rbegin(); reverseIt != cases.rend(); ++reverseIt) { + nextCase = reverseIt + 1 == cases.rend() ? nullptr : *(reverseIt + 1); + coref::SwitchCase switchCaseModel{getOid(*reverseIt), getOid((*reverseIt)->getSubStmt()), + getOid(nextCase), + llvm::isa(*reverseIt)}; + coref::StorageFacade::insertClassObj(std::move(switchCaseModel)); + } + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitWhileStmt(const clang::WhileStmt *stmt) { + coref::WhileStatement stmtModel{getOid(stmt), getOid(stmt->getCond()), getOid(stmt->getBody())}; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitDoStmt(const clang::DoStmt *stmt) { + coref::DoStatement stmtModel{getOid(stmt), getOid(stmt->getCond()), getOid(stmt->getBody())}; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitForStmt(const clang::ForStmt *stmt) { + coref::ForStatement stmtModel{getOid(stmt), getOid(stmt->getInit()), getOid(stmt->getBody()), + getOid(stmt->getCond()), getOid(stmt->getInc())}; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitObjCForCollectionStmt(const clang::ObjCForCollectionStmt *stmt) { + coref::ObjCForCollectionStatement stmtModel{getOid(stmt), getOid(stmt->getElement()), + getOid(stmt->getCollection()), + getOid(stmt->getBody())}; + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitCXXForRangeStmt(const clang::CXXForRangeStmt *stmt) { + coref::CxxForRangeStatement stmtModel{getOid(stmt), getOid(stmt->getBody()), + getOid(stmt->getLoopVariable()), + getOid(stmt->getRangeInit())}; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitValueStmt(const clang::ValueStmt *stmt) { + coref::ValueStatement stmtModel{getOid(stmt)}; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitExpr(const clang::Expr *stmt) { + coref::Expression stmtModel{getOid(stmt)}; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitObjCPropertyRefExpr(const clang::ObjCPropertyRefExpr *stmt) { + if (stmt->isExplicitProperty()) { + traverseDeclIfNotVisited((clang::Decl *)stmt->getExplicitProperty()); + coref::ObjCPropertyRefExpression refExpr{getOid(stmt), stmt->isMessagingGetter(), + getOid(stmt->getExplicitProperty())}; + coref::StorageFacade::insertClassObj(std::move(refExpr)); + } + return true; +} + +bool CorefASTVisitor::VisitObjCIvarRefExpr(const clang::ObjCIvarRefExpr *stmt) { + traverseDeclIfNotVisited((clang::Decl *)stmt->getDecl()); + coref::ObjCInstanceVariableRefExpression refExpr{getOid(stmt), getOid(stmt->getDecl())}; + coref::StorageFacade::insertClassObj(std::move(refExpr)); + return true; +} + +bool CorefASTVisitor::VisitObjCStringLiteral(const clang::ObjCStringLiteral *stmt) { + coref::ObjCStringLiteral stmtModel{getOid(stmt)}; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitConditionalOperator(const clang::ConditionalOperator *stmt) { + coref::ConditionalOperatorExpression stmtModel{ + getOid(stmt), getOid(stmt->getCond()), getOid(stmt->getTrueExpr()), + getOid(stmt->getFalseExpr()), + false // todo: tbd + }; + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::getDeclName(std::string *result, clang::NamedDecl *decl) { + sbrella::c7::NameGenerator gen(decl, *result); + gen.Visit(decl); + return not gen.hasError(); +} + +bool CorefASTVisitor::VisitDecl(const clang::Decl *decl) { + const auto declOid = getOid(decl); + auto parentOid = getParentOid(*decl); + int indexOrder = 0; + if (parentOid > 0) { + if (_parentMap.count(parentOid) > 0) { + indexOrder = _parentMap[parentOid]; + indexOrder++; + _parentMap[parentOid] = indexOrder; + } else { + _parentMap.insert({parentOid, indexOrder}); + } + } + coref::StorageFacade::insertClassObj( + Declaration{declOid, parentOid, indexOrder, getLocationOid(declOid, decl->getSourceRange()), + decl->getDeclKindName(), astToString(decl)}); + return true; +} + +bool CorefASTVisitor::VisitVarDecl(const clang::VarDecl *decl) { + VariableDeclaration declModel{getOid(decl)}; + + coref::StorageFacade::insertClassObj(std::move(declModel)); + return true; +} + +bool CorefASTVisitor::VisitNamedDecl(const clang::NamedDecl *decl) { + NamedDeclaration declModel{getOid(decl), decl->getQualifiedNameAsString()}; + + coref::StorageFacade::insertClassObj(std::move(declModel)); + + std::string symbolName; + if (getDeclName(&symbolName, const_cast(decl))) { + SymbolTable symbolTable{ + getOid(decl), + symbolName, + }; + coref::StorageFacade::insertClassObj(std::move(symbolTable)); + } + return true; +} + +bool CorefASTVisitor::VisitObjCContainerDecl(const clang::ObjCContainerDecl *decl) { + ObjCContainerDeclaration declModel{getOid(decl), decl->clang::Decl::getDeclKindName(), + // todo: declarationContextOid ? + -1L}; + + coref::StorageFacade::insertClassObj(std::move(declModel)); + return true; +} + +bool CorefASTVisitor::VisitObjCCategoryDecl(const clang::ObjCCategoryDecl *decl) { + auto categoryDeclOid = getOid(decl); + auto categoryName = + llvm::formatv("{0}({1})", decl->getClassInterface()->getName(), decl->getName()).str(); + + traverseDeclIfNotVisited((clang::Decl *)decl->getClassInterface()); + ObjCCategoryDeclaration declModel{ + categoryDeclOid, getOid(decl->getClassInterface()), decl->IsClassExtension(), + categoryName // category display name. + }; + coref::StorageFacade::insertClassObj(std::move(declModel)); + + // like an interface, it has own protocols. + for (auto &protocolDecl : decl->protocols()) { + traverseDeclIfNotVisited((clang::Decl *)protocolDecl); + auto protocolOid = getOid(protocolDecl); + coref::StorageFacade::insertClassObj(ObjCCategoryProtocolBinding{ + categoryDeclOid, + protocolOid, + }); + coref::StorageFacade::insertClassObj(ClassHierarchy{categoryDeclOid, protocolOid}); + } + return true; +} + +/// Visit ObjCInterfaceDecl nodes +/// 1. a new entry to ObjCInterfaceDeclaration table +/// 2. if this interface has super interface, add hierarchy information to +/// ClassHierarchy table +/// 3. if this interface supports any protocol, add hierarchy information to +/// ClassHierarchy table +/// and add interface&protocol binding to ObjCInterfaceProtocolBinding table. +/// \param decl +/// \return +bool CorefASTVisitor::VisitObjCInterfaceDecl(const clang::ObjCInterfaceDecl *decl) { + auto interfaceOid = getOid(decl); + auto *superDecl = decl->getSuperClass(); + auto superInterfaceOid = getOid(superDecl); + + traverseDeclIfNotVisited((clang::Decl *)superDecl); + coref::StorageFacade::insertClassObj(ObjCInterfaceDeclaration{ + interfaceOid, superInterfaceOid, decl->isThisDeclarationADefinition()}); + if (superDecl != nullptr) { + coref::StorageFacade::insertClassObj(ClassHierarchy{interfaceOid, superInterfaceOid}); + } + + // handling interface and corresponding protocols' bindings. + for (auto &protocolDecl : decl->protocols()) { + traverseDeclIfNotVisited((clang::Decl *)protocolDecl); + auto protocolOid = getOid(protocolDecl); + coref::StorageFacade::insertClassObj(ObjCInterfaceProtocolBinding{ + interfaceOid, + protocolOid, + }); + coref::StorageFacade::insertClassObj(ClassHierarchy{interfaceOid, protocolOid}); + } + return true; +} + +bool CorefASTVisitor::VisitObjCProtocolDecl(const clang::ObjCProtocolDecl *decl) { + ObjCProtocolDeclaration declModel{getOid(decl), decl->isThisDeclarationADefinition()}; + + coref::StorageFacade::insertClassObj(std::move(declModel)); + return true; +} + +bool CorefASTVisitor::VisitObjCMessageExpr(const clang::ObjCMessageExpr *stmt) { + std::string receiverKind; + + switch (stmt->getReceiverKind()) { + case clang::ObjCMessageExpr::Instance: + receiverKind = "instance"; + break; + case clang::ObjCMessageExpr::Class: + receiverKind = "class"; + break; + case clang::ObjCMessageExpr::SuperClass: + receiverKind = "super_class"; + break; + case clang::ObjCMessageExpr::SuperInstance: + receiverKind = "super_instance"; + break; + } + + auto receiverType = stmt->getReceiverType(); + + traverseDeclIfNotVisited((clang::Decl *)stmt->getReceiverInterface()); + traverseDeclIfNotVisited((clang::Decl *)stmt->getMethodDecl()); + + auto stmtOid = getOid(stmt); + coref::ObjCMessageExpression stmtModel{stmtOid, receiverKind, getOid(&receiverType), + getOid(stmt->getReceiverInterface()), + getOid(stmt->getMethodDecl())}; + + for (const auto *arg : stmt->arguments()) { + coref::ObjCMessageExpressionArguments argExpr{getOid(arg), stmtOid}; + coref::StorageFacade::insertClassObj(std::move(argExpr)); + } + + coref::StorageFacade::insertClassObj(std::move(stmtModel)); + return true; +} + +bool CorefASTVisitor::VisitObjCMethodDecl(const clang::ObjCMethodDecl *decl) { + const auto *const containerDecl = clang::dyn_cast_or_null(decl->getDeclContext()); + auto returnType = decl->getReturnType(); + coref::StorageFacade::insertClassObj( + ObjCMethodDeclaration{getOid(decl), getOid(containerDecl), getOid(&returnType), + decl->isThisDeclarationADefinition()}); + + for (auto *parmVarDecl : decl->parameters()) { + auto qualType = parmVarDecl->getType(); + coref::StorageFacade::insertClassObj( + ParamVariableDeclaration{getOid(parmVarDecl), getOid(decl), getOid(&qualType)}); + } + + generateCallableEnclosingStatement(decl->getBody(), getOid(decl)); + return true; +} + +bool CorefASTVisitor::VisitBlockDecl(const clang::BlockDecl *decl) { + coref::StorageFacade::insertClassObj(BlockDeclaration{getOid(decl)}); + + for (auto *parmVarDecl : decl->parameters()) { + auto qualType = parmVarDecl->getType(); + coref::StorageFacade::insertClassObj( + ParamVariableDeclaration{getOid(parmVarDecl), getOid(decl), getOid(&qualType)}); + } + + generateCallableEnclosingStatement(decl->getBody(), getOid(decl)); + return true; +} + +bool CorefASTVisitor::VisitBlockExpr(const clang::BlockExpr *expr) { + coref::StorageFacade::insertClassObj( + BlockExpression{getOid(expr), getOid(expr->getBlockDecl())}); + + return true; +} + +bool CorefASTVisitor::VisitObjCPropertyDecl(const clang::ObjCPropertyDecl *decl) { + std::string propertyControl; + switch (decl->getPropertyImplementation()) { + case clang::ObjCPropertyDecl::PropertyControl::None: + propertyControl = "none"; + break; + case clang::ObjCPropertyDecl::PropertyControl::Required: + propertyControl = "required"; + break; + case clang::ObjCPropertyDecl::PropertyControl::Optional: + propertyControl = "optional"; + break; + } + + std::string setterKind; + switch (decl->getSetterKind()) { + case clang::ObjCPropertyDecl::SetterKind::Assign: + setterKind = "assign"; + break; + case clang::ObjCPropertyDecl::SetterKind::Retain: + setterKind = "retain"; + break; + case clang::ObjCPropertyDecl::SetterKind::Copy: + setterKind = "copy"; + break; + case clang::ObjCPropertyDecl::SetterKind::Weak: + setterKind = "weak"; + break; + } + const auto *const containerDecl = clang::dyn_cast_or_null(decl->getDeclContext()); + auto qualType = decl->getType(); + ObjCPropertyDeclaration declModel{getOid(decl), getOid(&qualType), getOid(containerDecl), + propertyControl, setterKind}; + + coref::StorageFacade::insertClassObj(std::move(declModel)); + return true; +} + +bool CorefASTVisitor::VisitObjCImplDecl(const clang::ObjCImplDecl *decl) { + auto kind = decl->getKind(); + if (kind != clang::Decl::Kind::ObjCImplementation && + kind != clang::Decl::Kind::ObjCCategoryImpl) { + llvm::errs() << "unknown ObjCImplDecl type :" << kind << "\n"; + return true; + } + bool isCategory = (kind == clang::Decl::Kind::ObjCCategoryImpl); + coref::StorageFacade::insertClassObj(ObjCImplementationDeclaration{getOid(decl), isCategory}); + return true; +} + +bool CorefASTVisitor::VisitRecordDecl(const clang::RecordDecl *decl) { + coref::StorageFacade::insertClassObj(RecordDeclaration{ + getOid(decl), + }); + + // Extracting info of CXXRecordDecl + if (const auto *cxxRecordDecl = clang::dyn_cast_or_null(decl)) { + coref::StorageFacade::insertClassObj(CxxRecordDeclaration{getOid(cxxRecordDecl)}); + // Forward declaration doesn't have base class + if (decl->isThisDeclarationADefinition()) { + // Extracting info of bases classes of a CXXRecordDecl + for (const auto &base : cxxRecordDecl->bases()) { + if (auto *baseCxxRecordDecl = base.getType()->getAsCXXRecordDecl()) { + traverseDeclIfNotVisited((clang::Decl *)baseCxxRecordDecl); + coref::StorageFacade::insertClassObj( + ClassHierarchy{getOid(decl), getOid(baseCxxRecordDecl)}); + } + } + } + } + + return true; +} + +bool CorefASTVisitor::VisitCallExpr(const clang::CallExpr *expr) { + traverseDeclIfNotVisited((clang::Decl *)expr->getCalleeDecl()); + coref::StorageFacade::insertClassObj( + CallExpression{getOid(expr), getOid(expr->getCalleeDecl())}); + + for (const auto *arg : expr->arguments()) { + coref::StorageFacade::insertClassObj(CallExpressionArguments{getOid(arg), getOid(expr)}); + } + return true; +} + +bool CorefASTVisitor::VisitCXXMemberCallExpr(const clang::CXXMemberCallExpr *expr) { + auto type = expr->getObjectType(); + traverseDeclIfNotVisited((clang::Decl *)expr->getRecordDecl()); + traverseDeclIfNotVisited((clang::Decl *)expr->getMethodDecl()); + coref::StorageFacade::insertClassObj(CxxMemberCallExpression{ + getOid(expr), getOid(&type), getOid(expr->getMethodDecl()), getOid(expr->getRecordDecl())}); + return true; +} + +bool CorefASTVisitor::VisitCXXMethodDecl(const clang::CXXMethodDecl *decl) { + coref::StorageFacade::insertClassObj( + CxxMethodDeclaration{getOid(decl), getOid(decl->getParent())}); + return true; +} + +bool CorefASTVisitor::VisitDeclaratorDecl(const clang::DeclaratorDecl *decl) { + coref::StorageFacade::insertClassObj(DeclaratorDeclaration{getOid(decl)}); + return true; +} + +bool CorefASTVisitor::VisitFieldDecl(const clang::FieldDecl *decl) { + // Exclude those nodes that are ObjCPropertyDecl + if (const clang::RecordDecl *recordDecl = decl->getParent()) { + auto fieldType = decl->getType(); + coref::StorageFacade::insertClassObj(FieldDeclaration{ + getOid(decl->getCanonicalDecl()), getOid(&fieldType), getOid(recordDecl)}); + } + + return true; +} + +bool CorefASTVisitor::VisitObjCIvarDecl(const clang::ObjCIvarDecl *decl) { + traverseDeclIfNotVisited((clang::Decl *)decl->getContainingInterface()); + coref::ObjCInstanceVariableDeclaration iVarDeclaration{getOid(decl), + getOid(decl->getContainingInterface())}; + coref::StorageFacade::insertClassObj(std::move(iVarDeclaration)); + return true; +} + +bool CorefASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) { + auto returnType = decl->getReturnType(); + coref::StorageFacade::insertClassObj(FunctionDeclaration{getOid(decl), getOid(&returnType), + decl->isThisDeclarationADefinition()}); + + for (auto *parmVarDecl : decl->parameters()) { + auto qualType = parmVarDecl->getType(); + coref::StorageFacade::insertClassObj( + ParamVariableDeclaration{getOid(parmVarDecl), getOid(decl), getOid(&qualType)}); + } + + generateCallableEnclosingStatement(decl->getBody(), getOid(decl)); + return true; +} + +bool CorefASTVisitor::VisitValueDecl(const clang::ValueDecl *decl) { + coref::StorageFacade::insertClassObj(ValueDeclaration{getOid(decl)}); + return true; +} + +bool CorefASTVisitor::VisitObjCObjectType(const clang::ObjCObjectType *type) { + ObjCObjectType typeModel{getOid(type), getOid(type->getInterface())}; + + coref::StorageFacade::insertClassObj(std::move(typeModel)); + return true; +} + +bool CorefASTVisitor::VisitType(const clang::Type *type) { + if (type->isAnyPointerType()) { + auto pointee = type->getPointeeType(); + PointerType model{getOid(type), getOid(&pointee)}; + coref::StorageFacade::insertClassObj(std::move(model)); + } + + Type typeModel{getOid(type), type->getTypeClassName(), astToString(type)}; + + coref::StorageFacade::insertClassObj(std::move(typeModel)); + return true; +} + +bool CorefASTVisitor::VisitTagType(const clang::TagType *type) { + TagType typeModel{getOid(type), getOid(type->getDecl())}; + + coref::StorageFacade::insertClassObj(std::move(typeModel)); + return true; +} + +bool CorefASTVisitor::VisitTypeLoc(const clang::TypeLoc typeLoc) { + // todo : impl + return true; +} + +bool CorefASTVisitor::VisitTypeDecl(const clang::TypeDecl *decl) { + const auto qualType = getContext().getTypeDeclType(decl); + + TypeDeclaration declModel{getOid(decl), getOid(&qualType), astToString(decl)}; + + coref::StorageFacade::insertClassObj(std::move(declModel)); + return true; +} + +bool CorefASTVisitor::VisitTagDecl(const clang::TagDecl *decl) { + std::string tagKind; + switch (decl->getTagKind()) { + case clang::TagTypeKind::TTK_Class: + tagKind = "class"; + break; + case clang::TagTypeKind::TTK_Enum: + tagKind = "enum"; + break; + case clang::TagTypeKind::TTK_Interface: + tagKind = "interface"; + break; + case clang::TagTypeKind::TTK_Struct: + tagKind = "struct"; + break; + case clang::TagTypeKind::TTK_Union: + tagKind = "union"; + break; + } + + TagDeclaration declModel{ + getOid(decl), + tagKind, + decl->isThisDeclarationADefinition(), + }; + + coref::StorageFacade::insertClassObj(std::move(declModel)); + return true; +} + +void CorefASTVisitor::generateCallableEnclosingStatement(const clang::Stmt *stmt, + CorefOid callableOid) { + if (stmt != nullptr) { + for (const auto *child : stmt->children()) { + if (const auto *s = clang::dyn_cast_or_null(child)) { + coref::StorageFacade::insertClassObj( + CallableEnclosingStatement{getOid(s), callableOid}); + } + generateCallableEnclosingStatement(child, callableOid); + } + } +} + +/** + * Traverse Declarations that have not been visited yet. + * These declarations mostly were defined in header files that have been filtered out by + * customer. As they were referred by other nodes, e.g. "ObjcMessageExpr", "CxxCallExpression", + * we manually trigger a visit for the node to get the symbol information. + * + * @param decl + */ +void CorefASTVisitor::traverseDeclIfNotVisited(clang::Decl *decl) { + auto &sourceMngr = _context.getSourceManager(); + if (decl == nullptr || !decl->getLocation().isValid() || + sourceMngr.isInSystemHeader(decl->getLocation()) || + sourceMngr.isInSystemMacro(decl->getLocation()) || + coref::StorageFacade::checkDeclOidExist(getOid(decl))) { + return; + } + + llvm::StringRef absolutePath = sourceMngr.getFilename(decl->getLocation()); + if (absolutePath.empty()) { + return; + } + llvm::StringRef relativePath; + getRootRelativePath(absolutePath, relativePath); + + auto fileOid = CorefUri::generateFileOId(_corefUri.getCorpus(), std::string(relativePath)); + if (_newVisitFileMap.find(fileOid) == _newVisitFileMap.end()) { + File file{fileOid, std::string(relativePath), + std::string(sys::path::extension(relativePath)), + std::string(sys::path::filename(relativePath)), _programOid}; + _newVisitFileMap.insert({fileOid, std::move(file)}); + } + sbrella::c7::Switcher sw(_fileOid, fileOid); + TraverseDecl(decl); +} diff --git a/language/cfamily/extractor/AST/CorefASTVisitor.hpp b/language/cfamily/extractor/AST/CorefASTVisitor.hpp new file mode 100644 index 00000000..091526ed --- /dev/null +++ b/language/cfamily/extractor/AST/CorefASTVisitor.hpp @@ -0,0 +1,223 @@ + +#ifndef COREF_CFAMILY_SRC_EXTRACTOR_COREFASTVISITOR_HPP +#define COREF_CFAMILY_SRC_EXTRACTOR_COREFASTVISITOR_HPP + +#include "Coref/CorefUri.hpp" +#include "Coref/SignatureGenerator.hpp" +#include "Storage/StorageFacade.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace coref { + +class CorefASTVisitor : public clang::RecursiveASTVisitor { + friend class clang::RecursiveASTVisitor; + + private: + const clang::ASTContext &_context; + CorefUri _corefUri; + + /// Store an AST node's OID and its location OID + /// The key represents the node's OID, the value represents its location OID + std::unordered_map _locationMap; + + /// + std::unordered_map _parentMap; + /// Store current program Oid + CorefOid _programOid; + /// Store current file Oid + CorefOid _fileOid{0}; + /// Store new visited file info while traversing AST nodes. + std::unordered_map &_newVisitFileMap; + + /// Generate the corresponding relationship between a statement and its + /// caller callable a callable could be function decl, method decl and block + /// decl. \param stmt: \param callableOid + void generateCallableEnclosingStatement(const clang::Stmt *stmt, CorefOid callableOid); + + public: + CorefASTVisitor(const clang::ASTContext &context, const CorefUri &corefUri, CorefOid programOid, + std::unordered_map &newVisitFileMap); + + /// Get clang::ASTContext Object + /// \return + [[nodiscard]] const clang::ASTContext &getContext() const; + + /// Get the parent Oid of the given node + /// \tparam NodeT: The type of the given AST node + /// \param node: The given AST node + /// \return: The parent Oid + template CorefOid getParentOid(const NodeT &node); + + /// Get the Oid of the given stmt node + /// \param stmt + /// \return Oid + CorefOid getOid(const clang::Stmt *stmt) const; + + /// Get the Oid of the given decl node + /// \param decl + /// \return Oid + CorefOid getOid(const clang::Decl *decl) const; + + /// Get the Oid of the given type node + /// \param type + /// \return Oid + CorefOid getOid(const clang::Type *type) const; + + /// Get the Oid of the given qualtype node + /// \param decl + /// \return Oid + CorefOid getOid(const clang::QualType *qualType) const; + + /// Get Oid of TypeLoc + /// \param typeLoc + /// \return + CorefOid getOid(const clang::TypeLoc &typeLoc) const; + + /// Get the source code snippet corresponding to the stmt node + /// \param stmt + /// \return + std::string astToString(const clang::Stmt *stmt) const; + + /// Get the source code snippet corresponding to the decl node + /// \param stmt + /// \return + std::string astToString(const clang::Decl *decl) const; + + /// Get the source code snippet corresponding to the type node + /// \param type + /// \return + std::string astToString(const clang::Type *type) const; + + /// Get the source code snippet corresponding to the qualtype node + /// \param stmt + /// \return + std::string astToString(const clang::QualType *qualType) const; + + /// Get location by the sourceRange + /// \param sourceRange + /// \return + Location getLocation(const clang::SourceRange &sourceRange) const; + + /// Get the Location Oid. + /// It looks up _locationMap with nodeOid for the Location Oid first, + /// If not found, it calls getLocation to generate a new Location and return + /// its oid. \param nodeOid \param sourceRange \return Oid + CorefOid getLocationOid(const CorefOid nodeOid, const clang::SourceRange sourceRange); + + /// Get location by the sourceRange + /// \tparam T: Node type + /// \param node: AST node + /// \return + template Location getLocation(const T *node) { + return getLocation(node->getSourceRange()); + } + + /// Set file Oid of the file being extracted + /// \param oid + inline void setExtractFileOid(CorefOid oid) { _fileOid = oid; } + + bool VisitStmt(const clang::Stmt *stmt); + + bool VisitDeclStmt(const clang::DeclStmt *stmt); + + bool VisitIfStmt(const clang::IfStmt *stmt); + + bool VisitSwitchStmt(const clang::SwitchStmt *stmt); + + bool VisitWhileStmt(const clang::WhileStmt *stmt); + + bool VisitDoStmt(const clang::DoStmt *stmt); + + bool VisitForStmt(const clang::ForStmt *stmt); + + bool VisitObjCForCollectionStmt(const clang::ObjCForCollectionStmt *stmt); + + bool VisitObjCCategoryDecl(const clang::ObjCCategoryDecl *decl); + + bool VisitCXXForRangeStmt(const clang::CXXForRangeStmt *stmt); + + bool VisitValueStmt(const clang::ValueStmt *stmt); + + bool VisitExpr(const clang::Expr *stmt); + + bool VisitObjCPropertyRefExpr(const clang::ObjCPropertyRefExpr *stmt); + + bool VisitObjCIvarRefExpr(const clang::ObjCIvarRefExpr *stmt); + + bool VisitObjCStringLiteral(const clang::ObjCStringLiteral *stmt); + + bool VisitConditionalOperator(const clang::ConditionalOperator *stmt); + + bool VisitObjCMessageExpr(const clang::ObjCMessageExpr *stmt); + + bool VisitDecl(const clang::Decl *decl); + + // Not modeling this for now becasue except OMPDeclareMapperDecl, + // all declaration context are also inherited from Decl + // bool VisitDeclContext(const clang::DeclContext *declContext); + + bool VisitVarDecl(const clang::VarDecl *decl); + + bool VisitNamedDecl(const clang::NamedDecl *decl); + + bool VisitObjCContainerDecl(const clang::ObjCContainerDecl *decl); + + bool VisitObjCInterfaceDecl(const clang::ObjCInterfaceDecl *decl); + + bool VisitObjCProtocolDecl(const clang::ObjCProtocolDecl *decl); + + bool VisitObjCMethodDecl(const clang::ObjCMethodDecl *decl); + + bool VisitBlockDecl(const clang::BlockDecl *decl); + + bool VisitBlockExpr(const clang::BlockExpr *expr); + + bool VisitObjCPropertyDecl(const clang::ObjCPropertyDecl *decl); + + bool VisitObjCImplDecl(const clang::ObjCImplDecl *d); + + bool VisitRecordDecl(const clang::RecordDecl *decl); + + bool VisitCallExpr(const clang::CallExpr *expr); + + bool VisitCXXMemberCallExpr(const clang::CXXMemberCallExpr *expr); + + bool VisitCXXMethodDecl(const clang::CXXMethodDecl *decl); + + bool VisitDeclaratorDecl(const clang::DeclaratorDecl *decl); + + bool VisitFieldDecl(const clang::FieldDecl *decl); + + bool VisitObjCIvarDecl(const clang::ObjCIvarDecl *decl); + + bool VisitFunctionDecl(const clang::FunctionDecl *decl); + + bool VisitValueDecl(const clang::ValueDecl *decl); + + bool VisitObjCObjectType(const clang::ObjCObjectType *type); + + bool VisitType(const clang::Type *type); + + bool VisitTagType(const clang::TagType *type); + + bool VisitTypeLoc(const clang::TypeLoc typeLoc); + + bool VisitTypeDecl(const clang::TypeDecl *decl); + + bool VisitTagDecl(const clang::TagDecl *decl); + + bool getDeclName(std::string *result, clang::NamedDecl *decl); + + void traverseDeclIfNotVisited(clang::Decl *decl); +}; + +} // namespace coref + +#endif // COREF_CFAMILY_SRC_EXTRACTOR_COREFASTVISITOR_HPP diff --git a/language/cfamily/extractor/AST/CorefOptionsParser.cpp b/language/cfamily/extractor/AST/CorefOptionsParser.cpp new file mode 100644 index 00000000..c1732adc --- /dev/null +++ b/language/cfamily/extractor/AST/CorefOptionsParser.cpp @@ -0,0 +1,114 @@ + + +#include "CorefOptionsParser.hpp" + +using namespace clang::tooling; +using namespace llvm; + +llvm::Error CorefOptionsParser::init(int &argc, const char **argv, cl::OptionCategory &category) { + + static llvm::cl::opt compileCmdOption( + "compile-commands", llvm::cl::desc("Path to the compile_commands.json file"), + llvm::cl::cat(category)); + static llvm::cl::alias shortCompileCmdOption("p", llvm::cl::desc("Alias for compile"), + llvm::cl::aliasopt(compileCmdOption)); + + static llvm::cl::opt sqliteDbDirOption( + "output-db-path", llvm::cl::Required, llvm::cl::desc("The directory of sqlite DB"), + llvm::cl::cat(category)); + static llvm::cl::alias shortSqliteDbDirOption("o", + llvm::cl::desc("Alias for output-db-directory"), + llvm::cl::aliasopt(sqliteDbDirOption)); + + static llvm::cl::opt corpusOption( + "corpus", llvm::cl::desc("Specify the corpus of the codebase"), llvm::cl::cat(category)); + static llvm::cl::alias shortCorpusOption("c", llvm::cl::desc("Alias for corpus"), + llvm::cl::aliasopt(corpusOption)); + + static llvm::cl::list blacklistDirOption( + "blacklist-dir", llvm::cl::desc("The directories not to be extracted"), + llvm::cl::cat(category)); + + static llvm::cl::list argsAfter( + "extra-arg", llvm::cl::desc("Additional argument to append to the compiler command line"), + llvm::cl::cat(category)); + + static llvm::cl::list argsBefore( + "extra-arg-before", + llvm::cl::desc("Additional argument to prepend to the compiler command line"), + llvm::cl::cat(category)); + + cl::ResetAllOptionOccurrences(); + + cl::HideUnrelatedOptions(category); + + std::string errorMessage; + _compilations = FixedCompilationDatabase::loadFromCommandLine(argc, argv, errorMessage); + if (!errorMessage.empty()) + errorMessage.append("\n"); + llvm::raw_string_ostream os(errorMessage); + // Stop initializing if command-line option parsing failed. + if (!cl::ParseCommandLineOptions(argc, argv, "", &os)) { + os.flush(); + return llvm::make_error("[CommonOptionsParser]: " + errorMessage, + llvm::inconvertibleErrorCode()); + } + + cl::PrintOptionValues(); + + if (!sqliteDbDirOption.empty()) { + _sqliteDbDir = sqliteDbDirOption; + } + + if (!corpusOption.empty()) { + _corpusName = corpusOption; + } else { + _corpusName = "not specified"; + } + + if (!blacklistDirOption.empty()) { + _blacklistDir = blacklistDirOption; + } + + if (!_compilations && !compileCmdOption.empty()) { + // CompileCmdOption can be specified as directory + // if it is specified as json file and is not named as + // 'compile_commands.json', copy one for it. + if (!llvm::sys::fs::is_directory(compileCmdOption)) { + auto filename = llvm::sys::path::filename(compileCmdOption); + if (!filename.equals("compile_commands.json")) { + SmallString<256> tmpFile = StringRef(compileCmdOption); + sys::path::remove_filename(tmpFile); + sys::path::append(tmpFile, "compile_commands.json"); + llvm::outs() << "compile_commands.json generated. located in " + << std::string(tmpFile) << "\n"; + llvm::sys::fs::copy_file(compileCmdOption, tmpFile); + } + } + _compilations = + CompilationDatabase::autoDetectFromDirectory(compileCmdOption, errorMessage); + if (!_compilations) { + llvm::errs() << "Error while trying to load a compilation database:\n" + << errorMessage << "Running without flags.\n"; + _compilations.reset(new FixedCompilationDatabase(".", std::vector())); + } + } + + auto adjustingCompilations = + std::make_unique(std::move(_compilations)); + _adjuster = getInsertArgumentAdjuster(argsBefore, ArgumentInsertPosition::BEGIN); + _adjuster = combineAdjusters(std::move(_adjuster), + getInsertArgumentAdjuster(argsAfter, ArgumentInsertPosition::END)); + adjustingCompilations->appendArgumentsAdjuster(_adjuster); + _compilations = std::move(adjustingCompilations); + return llvm::Error::success(); +} + +llvm::Expected CorefOptionsParser::create(int &argc, const char **argv, + llvm::cl::OptionCategory &category) { + CorefOptionsParser parser; + llvm::Error err = parser.init(argc, argv, category); + if (err) + return std::move(err); + return std::move(parser); +} diff --git a/language/cfamily/extractor/AST/CorefOptionsParser.hpp b/language/cfamily/extractor/AST/CorefOptionsParser.hpp new file mode 100644 index 00000000..bc2528be --- /dev/null +++ b/language/cfamily/extractor/AST/CorefOptionsParser.hpp @@ -0,0 +1,41 @@ + +#ifndef COREF_CFAMILY_SRC_EXTRACTOR_COREFOPTIONSPARSER_HPP +#define COREF_CFAMILY_SRC_EXTRACTOR_COREFOPTIONSPARSER_HPP + +#include +#include + +namespace clang { +namespace tooling { + +class CorefOptionsParser { + public: + static llvm::Expected create(int &argc, const char **argv, + llvm::cl::OptionCategory &category); + + /// Returns a reference to the loaded compilations database. + CompilationDatabase &getCompilations() { return *_compilations; } + + /// Returns a std::string of the path to sqlite db. + inline std::string getSqliteDbDir() { return _sqliteDbDir; } + + /// Returns corpus name of the codebase. + inline std::string getCorpusName() { return _corpusName; } + + /// Returns corpus name of the codebase. + inline std::vector& getBlacklistDir() { return _blacklistDir; } + + private: + CorefOptionsParser() = default; + llvm::Error init(int &argc, const char **argv, llvm::cl::OptionCategory &category); + std::unique_ptr _compilations; + ArgumentsAdjuster _adjuster; + std::string _sqliteDbDir; + std::string _corpusName; + std::vector _blacklistDir{}; +}; + +} // namespace tooling +} // namespace clang + +#endif // COREF_CFAMILY_SRC_EXTRACTOR_COREFOPTIONSPARSER_HPP diff --git a/language/cfamily/extractor/AST/SymbolNameGenerator.cpp b/language/cfamily/extractor/AST/SymbolNameGenerator.cpp new file mode 100644 index 00000000..37322700 --- /dev/null +++ b/language/cfamily/extractor/AST/SymbolNameGenerator.cpp @@ -0,0 +1,1008 @@ +// Based on https://code.alipay.com/codeinsight_thirdparty/index7 +// Copyright (C), 2016-present, Sourcebrella, Inc Ltd - All rights reserved. +// Unauthorized copying, using, modifying of this file, via any medium is +// strictly prohibited. +// Proprietary and confidential. +// +// Author: Kai Luo +// File Description: +// Creation Date: 2018-05-30 +// Modification History: Created. +// clang-format off +#include "SymbolNameGenerator.hpp" +#include "Coref/Utils/Switcher.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" + +#include "ThirdParty/digestpp/digestpp.hpp" + +#include +#include +#include + +namespace sbrella { +namespace c7 { + +class NameGeneratorImpl : public clang::DeclVisitor { +public: + std::string symbol_name; + + NameGeneratorImpl(clang::NamedDecl *originalDecl, std::string &output); + + bool HasError() const { return has_error_; } + + void VisitDeclContext(clang::DeclContext *context); + + void VisitNamespaceDecl(clang::NamespaceDecl *decl); + + void VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl *decl); + + void VisitTagDecl(clang::TagDecl *decl); + + void VisitVarDecl(clang::VarDecl *decl); + + void VisitFunctionDecl(clang::FunctionDecl *decl); + + void VisitNamedDecl(clang::NamedDecl *decl); + + void VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl); + + void VisitTemplateTypeParmDecl(clang::TemplateTypeParmDecl *decl); + void VisitNonTypeTemplateParmDecl(clang::NonTypeTemplateParmDecl *decl); + void VisitTemplateTemplateParmDecl(clang::TemplateTemplateParmDecl *decl); + + // Objective-C + void VisitObjCContainerDecl(clang::ObjCContainerDecl *decl); + void VisitObjCMethodDecl(clang::ObjCMethodDecl *decl); + + static std::string GetFileHashId(const std::string &s); + +private: + void outputSeparator(); + void outputFunctionIdentifier(clang::DeclarationName name); + void _pVisitFunction_base(clang::FunctionDecl *decl, clang::FunctionTemplateDecl *ftdecl = nullptr); + void _pGenTemplateDeclSymbol(clang::TemplateDecl *decl, const clang::PrintingPolicy &policy, bool with_space_suffix = true); + template + void _pGenTemplateParameterDecl(_tTemplateParameterDecl *decl); + + void _pGenTemplateArgumentName(const clang::TemplateArgument &arg, const clang::PrintingPolicy &policy); + void _pGenTemplateNameName(const clang::TemplateName &tn, const clang::PrintingPolicy &policy); + void _pGenTemplateDeclNameForTemplateName(clang::TemplateDecl *td, const clang::PrintingPolicy &policy); + void _pGenQualTypeName(const clang::QualType &qt, const clang::PrintingPolicy &policy); + + void _pGenFileLocationPrefix(const clang::Decl *decl); + void _pGenPrefixWithSymbolName(const char *typeprefix = nullptr); + void _pGenPrefixWithSymbolName(const clang::NamedDecl *decl); + + static std::string _pGetUnnamedDeclName(clang::NamedDecl *nd); + +private: + clang::NamedDecl *original_decl_; + llvm::raw_string_ostream out_stream_; + bool has_error_ = false; + bool ignore_context_prefix_ = false; + bool need_separator_ = false; + bool need_file_prefix_ = false; + bool need_offset_prefix_ = false; + bool in_decl_context_ = false; + bool gen_template_parameter_name_ = true; + bool has_generated_filelocation_prefix = false; + static std::unordered_map m_FileHashMap; +}; + +NameGeneratorImpl::NameGeneratorImpl(clang::NamedDecl *originalDecl, std::string &output) + : original_decl_(originalDecl), out_stream_(output) {} + +void NameGeneratorImpl::outputSeparator() { + if (!need_separator_) + return; + out_stream_ << "::"; + need_separator_ = false; +} + +void NameGeneratorImpl::_pGenFileLocationPrefix(const clang::Decl *decl) { + // The symbol has internal linkage, so prepend a filename to the symbol + // name to help disambiguate it from symbols in other files with the same + // name. It's common to define something with internal linkage in a header + // file that's shared between many translation units in a program (e.g. + // static functions, constant variables), so use the name of the file + // containing the declaration rather than the name of the translation unit. + // Use just the basename to keep the symbol name short. + if (has_generated_filelocation_prefix || !(need_file_prefix_ || need_offset_prefix_)) + return; + + const clang::Decl *define_decl = nullptr; + if (clang::isa(decl)) { + auto *fd = clang::dyn_cast(decl); + if (fd->getStorageClass() == clang::SC_Static) { + const clang::FunctionDecl *define; + if (fd->hasBody(define)) { + define_decl = define; + } + } + } + else if (clang::isa(decl)) { + auto *vd = clang::dyn_cast(decl); + if (vd->getStorageClass() == clang::SC_Static) { + const clang::VarDecl *define = vd->getActingDefinition(); + if (define) { + define_decl = define; + } + } + } + if (define_decl) + decl = define_decl; + else if (const clang::Decl *canonicalDecl = decl->getCanonicalDecl()) + decl = canonicalDecl; + clang::SourceManager &sourceManager = decl->getASTContext().getSourceManager(); + clang::SourceLocation sloc = sourceManager.getSpellingLoc(decl->getLocation()); + if (sloc.isValid()) { + clang::FileID fileID = sourceManager.getFileID(sloc); + if (!fileID.isInvalid()) { + if (auto fileEntry = sourceManager.getFileEntryForID(fileID)) { + std::string filename = llvm::sys::path::filename(fileEntry->getName()).str(); + out_stream_ << filename; + out_stream_ << '@' << GetFileHashId(fileEntry->getName().str()); + if (need_offset_prefix_) + out_stream_ << '@' << sourceManager.getFileOffset(sloc); + out_stream_ << '/'; + } + } + } + + has_generated_filelocation_prefix = true; +} + +void NameGeneratorImpl::_pGenPrefixWithSymbolName(const clang::NamedDecl *decl) { + const char *typeprefix = nullptr; + if (decl) { + if (auto tdecl = llvm::dyn_cast_or_null(decl)) { + if (tdecl->isClass()) + typeprefix = "class"; + else if (tdecl->isStruct()) + typeprefix = "struct"; + else if (tdecl->isEnum()) + typeprefix = "enum"; + else if (tdecl->isUnion()) + typeprefix = "union"; + } + else if (llvm::isa(decl)) + typeprefix = "typedef"; + else if (llvm::isa(decl)) + typeprefix = "typealias"; + else if (llvm::isa(decl)) + typeprefix = "property"; + else if (llvm::isa(decl)) + typeprefix = "field"; + else if (llvm::isa(decl)) + typeprefix = "enumfield"; + else if (llvm::isa(decl)) + typeprefix = "class"; + else if (llvm::isa(decl)) + typeprefix = "typealias"; + else if (llvm::isa(decl)) + typeprefix = "label"; + else if (llvm::isa(decl)) + typeprefix = "namespacealias"; + else if (llvm::isa(decl)) + typeprefix = nullptr; + else if (llvm::isa(decl)) + typeprefix = nullptr; + else if (auto containerDecl = llvm::dyn_cast_or_null(decl)) { + if (containerDecl->getDeclKind() == clang::Decl::ObjCProtocol) + typeprefix = "obj_c_protocol"; + else + typeprefix = "obj_c_class"; + } +// else +// std::cerr << decl->getDeclKindName() << " " << symbol_name << " " +// << decl->getSourceRange().getBegin().printToString(decl->getASTContext().getSourceManager()) << std::endl; + } + _pGenPrefixWithSymbolName(typeprefix); +} + +void NameGeneratorImpl::_pGenPrefixWithSymbolName(const char *typeprefix) { + if (typeprefix) { + out_stream_ << '$' << typeprefix << '$'; + } + out_stream_ << symbol_name; +} + +void NameGeneratorImpl::VisitDeclContext(clang::DeclContext *context) { + if (ignore_context_prefix_) + return; + + if (auto decl = llvm::dyn_cast_or_null(context)) { + if (auto funcDecl = llvm::dyn_cast_or_null(decl)) { + if (funcDecl->isThisDeclarationADefinition()) { + // For declarations inside a function body, prefix both a + // filename and a file offset. + need_offset_prefix_ = true; + } + } + Switcher sw1(in_decl_context_, true); + Visit(decl); + return; + } + + _pGenFileLocationPrefix(original_decl_); +} + +void NameGeneratorImpl::VisitNamespaceDecl(clang::NamespaceDecl *decl) { + if (decl->isAnonymousNamespace()) { + need_file_prefix_ = true; + } + VisitDeclContext(decl->getDeclContext()); + outputSeparator(); + need_separator_ = true; + + clang::IdentifierInfo *II = decl->getIdentifier(); + if (II) // Simple name. + symbol_name = II->getName(); + else + symbol_name = ""; + + _pGenPrefixWithSymbolName("namespace"); +} + +void NameGeneratorImpl::VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl *decl) { + VisitTagDecl(decl); + + // Only add template arguments for explicit specializations, not for + // instantiations. This loop will skip over implicit instantiations of + // partial specializations. (e.g. It will turn + // std::vector> into + // std::vector). + while (decl->getSpecializationKind() != clang::TSK_ExplicitSpecialization) { + if (auto partialSpec = decl->getInstantiatedFrom().dyn_cast()) { + decl = partialSpec; + } + else { + return; + } + } + + out_stream_ << '<'; + const clang::TemplateArgumentList &args = decl->getTemplateArgs(); + for (unsigned i = 0, iEnd = args.size(); i != iEnd; ++i) { + if (i != 0) + out_stream_ << ", "; + _pGenTemplateArgumentName(args[i], decl->getASTContext().getPrintingPolicy()); + } + out_stream_ << '>'; +} + +void NameGeneratorImpl::VisitTagDecl(clang::TagDecl *decl) { +// // Leave the enum name out of the symbol names of the enumerators. +// if (in_decl_context_ && decl->isEnum()) +// return; + + // For "typedef struct { ... } S", refer to the struct using S. + clang::NamedDecl *namedDecl = decl; + clang::TypedefNameDecl *anonDecl = decl->getTypedefNameForAnonDecl(); + if (anonDecl != nullptr) + namedDecl = anonDecl; + clang::IdentifierInfo *identifier = namedDecl->getIdentifier(); + + if (!identifier) { + need_file_prefix_ = true; + } + VisitDeclContext(decl->getDeclContext()); + + outputSeparator(); + + if (identifier) { + symbol_name = identifier->getName(); + } else { + symbol_name = _pGetUnnamedDeclName(decl); + } + _pGenPrefixWithSymbolName(namedDecl); + need_separator_ = true; +} + +void NameGeneratorImpl::VisitVarDecl(clang::VarDecl *decl) { +// if (!decl->isExternC() && !decl->hasExternalStorage()) + if (!decl->hasExternalFormalLinkage()) + need_file_prefix_ = true; + VisitDeclContext(decl->getDeclContext()); + + outputSeparator(); + clang::IdentifierInfo *identifier = decl->getIdentifier(); + if (identifier) { // Simple name. + symbol_name = identifier->getName(); + } + else { + symbol_name = _pGetUnnamedDeclName(decl); + } + _pGenPrefixWithSymbolName("var"); + need_separator_ = true; +} + +// Write the name of the function to the output. Do not write any qualifiers +// or template parameters/arguments (i.e. no colons and no brackets). +void NameGeneratorImpl::outputFunctionIdentifier(clang::DeclarationName name) { + // DeclarationName::printName includes template parameters for the + // constructors and destructors of template classes. Omit these by digging + // into the name for the InjectedClassName type. + clang::DeclarationName::NameKind nameKind = name.getNameKind(); + if (nameKind == clang::DeclarationName::CXXConstructorName || + nameKind == clang::DeclarationName::CXXDestructorName) { + const clang::Type *nameType = name.getCXXNameType().getTypePtr(); + assert(nameType != nullptr); + const clang::InjectedClassNameType *injectedNameType = + nameType->getAs(); + if (injectedNameType != nullptr) { + if (nameKind == clang::DeclarationName::CXXDestructorName) + out_stream_ << '~'; + out_stream_ << injectedNameType->getDecl()->getName(); + symbol_name = '~' + injectedNameType->getDecl()->getName().str(); + return; + } + } + symbol_name = name.getAsString(); + _pGenPrefixWithSymbolName("func"); +} + +void NameGeneratorImpl::_pVisitFunction_base(clang::FunctionDecl *decl, clang::FunctionTemplateDecl *ftdecl) { + clang::PrintingPolicy policy = decl->getASTContext().getPrintingPolicy(); + policy.SuppressTagKeyword = true; + bool isExternC = false; + + // When a function is only marked extern "C" in a header file but not in + // the source file, there are two declarations, where only one + // declaration's isExternC is true. Use the isExternC flag on the + // canonical declaration for consistency. + if (clang::FunctionDecl *canonical = decl->getCanonicalDecl()) + isExternC = canonical->isExternC(); + + // TODO: Review correctness for C code. +// if (!isExternC && !decl->isInExternCContext() && !decl->isInExternCXXContext()) + if (!decl->hasExternalFormalLinkage()) + need_file_prefix_ = true; + + VisitDeclContext(decl->getDeclContext()); + outputSeparator(); + _pGenTemplateDeclSymbol(ftdecl, policy, true); + outputFunctionIdentifier(decl->getDeclName()); + + // Print the template arguments for template functions (not for + // non-template methods of template classes), and for explicit + // specializations only -- not for an instantiations of a template pattern. + if (const clang::TemplateArgumentList *templateArgs = + decl->getTemplateSpecializationArgs()) { + out_stream_ << '<'; + for (unsigned int i = 0; i < templateArgs->size(); ++i) { + if (i != 0) + out_stream_ << ", "; + templateArgs->get(i).print(policy, out_stream_, true); + } + out_stream_ << '>'; + } + + // For extern "C++" function declarations, include the parameter types in + // the name. + if (decl->getASTContext().getLangOpts().CPlusPlus && !isExternC) { + out_stream_ << '('; + bool firstParm = true; + for (auto it = decl->param_begin(), itEnd = decl->param_end(); it != itEnd; + ++it) { + clang::ParmVarDecl *parm = *it; + if (!firstParm) + out_stream_ << ", "; + clang::QualType parmType = parm->getType(); + parmType = parmType->getCanonicalTypeUnqualified(); + // NOTE: In one template tree, different level have different TemplateParameter depth. + // Code: + // template class A { + // template class B { + // void func(T1 a, T2 b); + // }; + // }; + // gen: A::B::func(TP(0,0), TP(1,0)) + _pGenQualTypeName(parmType, policy); + firstParm = false; + } + if (decl->isVariadic()) { + if (decl->param_size() > 0) + out_stream_ << ", "; + out_stream_ << "..."; + } + out_stream_ << ')'; + + // C++ method type qualifiers. + if (auto method = llvm::dyn_cast(decl)) { + auto quals = method->getMethodQualifiers(); + if (quals.hasConst()) + out_stream_ << " const"; + if (quals.hasRestrict()) + out_stream_ << " restrict"; + if (quals.hasVolatile()) + out_stream_ << " volatile"; + } + } + else { + out_stream_ << "(*)"; + } + need_separator_ = true; +} + +void NameGeneratorImpl::_pGenTemplateDeclSymbol(clang::TemplateDecl *decl, const clang::PrintingPolicy &policy, bool with_space_suffix) { + // Example: template class, typename...> + if (decl && decl->getTemplateParameters()) { + llvm::ArrayRef parameters = decl->getTemplateParameters()->asArray(); + out_stream_ << "template <"; + for (unsigned int i = 0; i < parameters.size(); ++i) { + if (i != 0) + out_stream_ << ", "; + const auto ¶meter = parameters.data()[i]; + if (llvm::isa(parameter)) { + const auto &pdecl = *llvm::dyn_cast(parameter); + pdecl.getType().print(out_stream_, policy); + if (pdecl.isParameterPack()) { + out_stream_ << "..."; + } + } + else if (llvm::isa(parameter)) { + const auto &pdecl = *llvm::dyn_cast(parameter); + out_stream_ << (pdecl.wasDeclaredWithTypename() ? "typename" : "class"); + if (pdecl.isParameterPack()) { + out_stream_ << "..."; + } + } + else if (llvm::isa(parameter)) { + _pGenTemplateDeclSymbol(llvm::dyn_cast(parameter), policy, false); + } + } + out_stream_ << ">"; + if (with_space_suffix) + out_stream_ << " "; + } +} + +static llvm::PointerUnion getFunctionTemplateDecl(clang::FunctionDecl *decl) { + if (decl->getDescribedFunctionTemplate()) { // TODO: handle more + return decl->getDescribedFunctionTemplate(); + } + else if (decl->getTemplateInstantiationPattern()) { + return getFunctionTemplateDecl(decl->getTemplateInstantiationPattern()); + } + else { + return decl; + } +} + +static llvm::PointerUnion +getClassTemplateDecl(clang::CXXRecordDecl *decl) { + if (decl->getDescribedClassTemplate()) { // TODO: handle more + return decl->getDescribedClassTemplate(); + } + else if (decl->getTemplateInstantiationPattern()) { + return getClassTemplateDecl(decl->getTemplateInstantiationPattern()); + } + else if (llvm::isa(decl)) { + return llvm::dyn_cast(decl); + } + else { + return decl; + } +} + +static void* getPointer(const clang::TemplateParameter &tp) { + if (tp.is()) { + return tp.get(); + } + else if (tp.is()) { + return tp.get(); + } + else if (tp.is()) { + return tp.get(); + } + return nullptr; +} + +struct TemplateParameterDepthIndex { + unsigned int Depth; + unsigned int Index; +}; + +static TemplateParameterDepthIndex getDepthIndex(const clang::TemplateParameter &tp) { + if (tp.is()) { + auto p = tp.get(); + return { .Depth = p->getDepth(), .Index = p->getIndex() }; + } + else if (tp.is()) { + auto p = tp.get(); + return { .Depth = p->getDepth(), .Index = p->getIndex() }; + } + else if (tp.is()) { + auto p = tp.get(); + return { .Depth = p->getDepth(), .Index = p->getIndex() }; + } + return { .Depth = unsigned(-1), .Index = unsigned(-1) }; +} + +static clang::TemplateParameter getTemplateParameter(clang::NamedDecl *decl) { + if (llvm::isa(decl)) { + return llvm::dyn_cast(decl); + } + else if (llvm::isa(decl)) { + return llvm::dyn_cast(decl); + } + else if (llvm::isa(decl)) { + return llvm::dyn_cast(decl); + } + clang::TemplateParameter tp; + return tp; +} + +void NameGeneratorImpl::VisitFunctionDecl(clang::FunctionDecl *decl) { + auto fdecl = getFunctionTemplateDecl(decl); + if (fdecl.is()) { + _pVisitFunction_base(fdecl.get()); + } + else { + VisitFunctionTemplateDecl(fdecl.get()); + } +} + +void NameGeneratorImpl::VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl) { + _pVisitFunction_base(decl->getTemplatedDecl(), decl); +} + +// GetTemplateParameterDeclName + +static std::string GetTemplateParameterDeclName(const TemplateParameterDepthIndex &depth_index) { + // TemplateParameter name: "TP(depth,index)" + // NOTE: Change TP(x,y) to type-parameter-x-y, if there is TP(x,y) in comments, it is it. + return "type-parameter-" + std::to_string(depth_index.Depth) + "-" + std::to_string(depth_index.Index) + ""; +} + +static std::string GetTemplateParameterDeclName(const clang::TemplateParameter &tp) { + TemplateParameterDepthIndex depth_index = getDepthIndex(tp); + return GetTemplateParameterDeclName(depth_index); +} + +// GetTemplateParameterLevelPath + +static bool findTemplateParameterLevelListBase(void *ptr, std::list &result, clang::TemplateParameterList *list) { + if (!list) + return false; + for (unsigned int i = 0; i != list->size(); ++i) { + clang::NamedDecl *nd = list->getParam(i); + clang::TemplateParameter td = getTemplateParameter(nd); + if (td.isNull()) + continue; + result.push_back(td); + if (nd == ptr) + return true; + if (td.is()) { + if (findTemplateParameterLevelListBase(ptr, result, td.get()->getTemplateParameters())) + return true; + } + result.pop_back(); + } + return false; +} + +static std::list findTemplateParameterLevelList(const clang::TemplateParameter &tp, clang::TemplateParameterList *list) { + // Search one template parameters list to find a TemplateParameter, return the path of level. + std::list result; + findTemplateParameterLevelListBase(getPointer(tp), result, list); + return result; +} + +static std::list findTemplateParameterLevelList(const clang::TemplateParameter &tp, clang::TemplateDecl *context) { + // Search one template parameters list to find a TemplateParameter, return the path of level. + return findTemplateParameterLevelList(tp, context->getTemplateParameters()); +} + +template +static std::pair> +searchDeclInDeclRange(_tTemplateParameterDecl *decl, const _tDeclWithDecls *decl_find_root) { + // Search one decls list to find 'decl', return the path of level. + // DeclRange: the sub nodes (decls) of one decl. + const clang::DeclContext::decl_range &range = decl_find_root->decls(); + for (auto d : range) { + // TypeAliasTemplateDecl + if (llvm::isa(d)) { + auto result = findTemplateParameterLevelList(decl, llvm::dyn_cast(d)); + if (!result.empty()) { + return std::make_pair(llvm::dyn_cast(d), std::move(result)); + } + } + else if (llvm::isa(d)) { + auto result = findTemplateParameterLevelList(decl, llvm::dyn_cast(d)); + if (!result.empty()) { + return std::make_pair(llvm::dyn_cast(d), std::move(result)); + } + } + // NOTE: VarTemplateDecl in namespace 's context is not namespace at clang 3.6. + else if (llvm::isa(d)) { + auto result = searchDeclInDeclRange(decl, llvm::dyn_cast(d)); + if (result.first) { + return result; + } + } + // NOTE: VarTemplateDecl in cxxrecord 's context is not cxxrecord at clang 3.6. + else if (llvm::isa(d)) { + auto result = searchDeclInDeclRange(decl, llvm::dyn_cast(d)); + if (result.first) { + return result; + } + } + } + return std::make_pair(nullptr, std::list()); +} + +template +static std::pair> GetTemplateParameterLevelPath(_tTemplateParameterDecl *decl) { + // Get Template Parameter Level Path + // Example: + // template