#pragma once #include "Utility.h" #include "Resolver.h" #include "SymbolKind.h" #include "RelationKind.h" #include "FilterASTVisitor.h" namespace clice { template class SemanticVisitor : public FilteredASTVisitor> { public: using Base = FilteredASTVisitor; SemanticVisitor(CompilationUnit& unit, bool interested_only) : Base(unit, interested_only), unit(unit), resolver(unit.resolver()) {} public: Derived& getDerived() { return static_cast(*this); } /// 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, kind, location); } } /// 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, kind, location); } } /// 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); } } void handleAttrOccurrence(const clang::Attr* attr, clang::SourceRange range) { assert(attr && "Invalid attribute"); assert(range.isValid() && "Invalid range"); if constexpr(!std::same_as) { getDerived().handleAttrOccurrence(attr, range); } } /// 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 run() { if(Base::interested_only) { for(auto decl: unit.top_level_decls()) { Base::TraverseDecl(decl); } } else { Base::TraverseAST(unit.context()); } for(auto directive: unit.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 = unit.context().getCurrentNamedModule()) { // auto keyword = module->DefinitionLoc; // auto begin = TB.spelledTokenContaining(keyword); // // assert(begin->kind() == clang::tok::identifier && begin->text(SM) == "module" && // // "Invalid module declaration"); // // begin += 1; // auto end = TB.spelledTokens(unit.file_id(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 /// ============================================================================ #define VISIT_DECL(type) bool Visit##type(const clang::type* decl) #define VISIT_STMT(type) bool Visit##type(const clang::type* stmt) #define VISIT_EXPR(type) bool Visit##type(const clang::type* expr) #define VISIT_TYPE(type) bool Visit##type(const clang::type* type) #define VISIT_TYPELOC(type) bool Visit##type(clang::type loc) VISIT_DECL(ImportDecl) { /// FIXME: // auto tokens = TB.expandedTokens(decl->getSourceRange()); // // assert(tokens.size() >= 2 && tokens[0].kind() == clang::tok::identifier && // tokens[0].text(SM) == "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 = ast::decl_of(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, llvm::cast(decl->getDeclContext()), 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 = ast::decl_of(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