#pragma once #include "Compiler.h" #include "Resolver.h" #include "Utility.h" #include "Basic/RelationKind.h" #include "Basic/SymbolKind.h" #include "Support/Support.h" namespace clice { template class SemanticVisitor : public clang::RecursiveASTVisitor> { public: using Base = clang::RecursiveASTVisitor; SemanticVisitor(ASTInfo& info, bool mainFileOnly = false) : sema(info.sema()), pp(info.pp()), resolver(info.resolver()), srcMgr(info.srcMgr()), tokBuf(info.tokBuf()), info(info), mainFileOnly(mainFileOnly) {} public: public: consteval bool VisitImplicitInstantiation() { return true; } Derived& getDerived() { return static_cast(*this); } bool needFilter(clang::SourceLocation location) { return location.isInvalid() || (mainFileOnly && !srcMgr.isInMainFile(location)); } void dump [[gnu::noinline]] (clang::SourceLocation loc) { auto location = srcMgr.getPresumedLoc(loc); llvm::SmallString<128> path; auto err = fs::real_path(location.getFilename(), path); llvm::outs() << path << ":" << location.getLine() << ":" << location.getColumn() << "\n"; } /// Invoked when a declaration occur is seen in source code. /// @param decl The decl corresponding to the symbol. /// @param kind The kind of the occurrence, such as declaration, definition, reference. /// @param location The location of the occurrence. Note that declaration name must be one /// token, so just one source location is enough. void handleDeclOccurrence(const clang::NamedDecl* decl, RelationKind kind, clang::SourceLocation location) { assert(decl && "Invalid decl"); assert(kind.is_one_of(RelationKind::Declaration, RelationKind::Definition, RelationKind::Reference, RelationKind::WeakReference) && "Invalid kind"); assert(location.isValid() && "Invalid location"); /// Forwards to the derived class. Check whether the derived class has /// its own implementation to avoid infinite recursion. if constexpr(!std::same_as) { getDerived().handleDeclOccurrence(decl, location, kind); } } /// Invoked when a macro occurrence is seen in source code. void handleMacroOccurrence(const clang::MacroInfo* def, RelationKind kind, clang::SourceLocation location) { assert(def && "Invalid macro"); assert(kind.is_one_of(RelationKind::Definition, RelationKind::Reference) && "Invalid kind"); assert(location.isValid() && "Invalid location"); if constexpr(!std::same_as) { getDerived().handleMacroOccurrence(def, location, kind); } } /// Invoked when a module occurrence is seen in source code. /// @param keyword The location of the `module` or `import` keyword. /// @param identifiers Tokens that make up the module name. void handleModuleOccurrence(clang::SourceLocation keyword, llvm::ArrayRef identifiers) { assert(keyword.isValid() && keyword.isFileID() && "Invalid keyword location"); /// FIXME: Check whether identifiers are valid. if constexpr(!std::same_as) { getDerived().handleModuleOccurrence(keyword, identifiers); } } /// Invoked when a relation between two decls is seen in source code. /// @param decl The source decl. /// @param kind The kind of the relation. /// @param target The target decl, may same as the source decl. /// @param range The source range of the relation, may be invalid. void handleRelation(const clang::NamedDecl* decl, RelationKind kind, const clang::NamedDecl* target, clang::SourceRange range) { assert(decl && "Invalid decl"); assert(target && "Invalid target"); if constexpr(!std::same_as) { getDerived().handleRelation(decl, kind, target, range); } } void handleOccurrence(const clang::BuiltinType* type, clang::SourceRange range) { /// FIXME: /// Builtin type doesn't have corresponding decl. So we handle it separately. /// And it is possible that a builtin type is composed of multiple tokens. /// e.g. `unsigned long long`. } void handleOccurrence(const clang::Attr* attr, clang::SourceRange range) { /// FIXME: } void run() { Base::TraverseAST(sema.getASTContext()); for(auto directive: info.directives()) { for(auto macro: directive.second.macros) { switch(macro.kind) { case MacroRef::Kind::Def: { handleMacroOccurrence(macro.macro, RelationKind::Definition, macro.loc); break; } case MacroRef::Kind::Ref: case MacroRef::Kind::Undef: { handleMacroOccurrence(macro.macro, RelationKind::Reference, macro.loc); break; } } } } if(auto module = sema.getASTContext().getCurrentNamedModule()) { auto keyword = module->DefinitionLoc; auto begin = tokBuf.spelledTokenContaining(keyword); assert(begin->kind() == clang::tok::identifier && begin->text(srcMgr) == "module" && "Invalid module declaration"); begin += 1; auto end = tokBuf.spelledTokens(srcMgr.getFileID(keyword)).end(); for(auto iter = begin; iter != end; ++iter) { if(iter->kind() == clang::tok::identifier) { if(auto next = iter + 1; next != end && (next->kind() == clang::tok::period || next->kind() == clang::tok::colon)) { iter += 1; continue; } end = iter + 1; break; } std::unreachable(); } handleModuleOccurrence(keyword, llvm::ArrayRef(begin, end)); } } public: /// ============================================================================ /// Declaration /// ============================================================================ TRAVERSE_DECL(Decl) { if(!decl) { return true; } if(!llvm::isa(decl) && needFilter(decl->getLocation())) { return true; } decls.push_back(decl); auto result = Base::TraverseDecl(decl); decls.pop_back(); return result; } VISIT_DECL(ImportDecl) { auto tokens = tokBuf.expandedTokens(decl->getSourceRange()); assert(tokens.size() >= 2 && tokens[0].kind() == clang::tok::identifier && tokens[0].text(srcMgr) == "import" && "Invalid import declaration"); assert([&]() { auto range = tokens.drop_front(1); for(auto iter = range.begin(); iter != range.end(); ++iter) { if(iter->kind() == clang::tok::identifier) { if(auto next = iter + 1; next != range.end() && (next->kind() == clang::tok::coloncolon || next->kind() == clang::tok::period)) { continue; } break; } else { return false; } } return true; }() && "Invalid import declaration"); handleModuleOccurrence(tokens[0].location(), tokens.drop_front(1)); return true; } /// namespace Foo { } /// ^~~~ definition VISIT_DECL(NamespaceDecl) { handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation()); handleRelation(decl, RelationKind::Definition, decl, decl->getLocation()); return true; } /// namespace Foo = Bar /// ^ ^~~~ reference /// ^~~~ definition VISIT_DECL(NamespaceAliasDecl) { handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation()); handleRelation(decl, RelationKind::Definition, decl, decl->getLocation()); handleDeclOccurrence(decl->getNamespace(), RelationKind::Reference, decl->getTargetNameLoc()); handleRelation(decl->getNamespace(), RelationKind::Reference, decl->getNamespace(), decl->getTargetNameLoc()); return true; } /// using namespace Foo /// ^~~~~~~ reference VISIT_DECL(UsingDirectiveDecl) { handleDeclOccurrence(decl->getNominatedNamespace(), RelationKind::Reference, decl->getLocation()); handleRelation(decl, RelationKind::Reference, decl->getNominatedNamespace(), decl->getLocation()); return true; } /// label: /// ^~~~ definition VISIT_DECL(LabelDecl) { handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation()); handleRelation(decl, RelationKind::Definition, decl, decl->getLocation()); return true; } /// struct X { int foo; }; /// ^~~~ definition VISIT_DECL(FieldDecl) { handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation()); handleRelation(decl, RelationKind::Definition, decl, decl->getLocation()); if(auto target = declForType(decl->getType())) { handleRelation(decl, RelationKind::TypeDefinition, target, decl->getLocation()); } return true; } /// enum Foo { bar }; /// ^~~~ definition VISIT_DECL(EnumConstantDecl) { handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation()); handleRelation(decl, RelationKind::Definition, decl, decl->getLocation()); handleRelation(decl, RelationKind::TypeDefinition, declForType(decl->getType()), decl->getLocation()); return true; } /// using Foo::bar; /// ^~~~ reference VISIT_DECL(UsingDecl) { for(auto shadow: decl->shadows()) { handleDeclOccurrence(shadow, RelationKind::WeakReference, decl->getLocation()); handleRelation(decl, RelationKind::WeakReference, decl, decl->getLocation()); } return true; } /// auto [a, b] = std::make_tuple(1, 2); /// ^~~~ definition VISIT_DECL(BindingDecl) { handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation()); handleRelation(decl, RelationKind::Definition, decl, decl->getLocation()); if(auto target = declForType(decl->getType())) { handleRelation(decl, RelationKind::TypeDefinition, target, decl->getLocation()); } return true; } /// template /// ^~~~ definition VISIT_DECL(TemplateTypeParmDecl) { handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation()); handleRelation(decl, RelationKind::Definition, decl, decl->getLocation()); return true; } /// template