#pragma once #include "Compiler.h" #include "Resolver.h" #include "Utility.h" #include "Support/Support.h" namespace clice { /// In the LSP, there are several different kinds, such as `SemanticTokenType`, /// `CompletionItemKind`, and `SymbolKind`. Unfortunately, these kinds do not cover all the semantic /// information we need. It's also inconsistent that some kinds exist in one category but not in /// another, for example, `Namespace` is present in `SemanticTokenType` but not in /// `CompletionItemKind`. To address this, we define our own `SymbolKind`, which will be used /// consistently across our responses to the client and in the index. Users who prefer to stick to /// standard LSP kinds can map our `SymbolKind` to the corresponding LSP kinds through /// configuration. struct SymbolKind : refl::Enum { enum Kind : uint8_t { Comment = 0, ///< C/C++ comments. Number, ///< C/C++ number literal. Character, ///< C/C++ character literal. String, ///< C/C++ string literal. Keyword, ///< C/C++ keyword. Directive, ///< C/C++ preprocessor directive, e.g. `#include`. Header, ///< C/C++ header name, e.g. `` and `"foo.h"`. Module, ///< C++20 module name. Macro, ///< C/C++ macro. MacroParameter, ///< C/C++ macro parameter. Namespace, ///> C++ namespace. Class, ///> C/C++ class. Struct, ///> C/C++ struct. Union, ///> C/C++ union. Enum, ///> C/C++ enum. Type, ///> C/C++ type alias and C++ template type parameter. Field, ///> C/C++ field. EnumMember, ///> C/C++ enum member. Function, ///> C/C++ function. Method, ///> C++ method. Variable, ///> C/C++ variable, includes C++17 structured bindings. Parameter, ///> C/C++ parameter. Label, ///> C/C++ label. Concept, ///> C++20 concept. Attribute, ///> GNU/MSVC/C++11/C23 attribute. Operator, ///> C/C++ operator. Paren, ///> `(` and `)`. Bracket, ///> `[` and `]`. Brace, ///> `{` and `}`. Angle, ///> `<` and `>`. Invalid, }; using Enum::Enum; constexpr inline static auto InvalidEnum = Kind::Invalid; static SymbolKind from(const clang::Decl* decl); static SymbolKind from(const clang::tok::TokenKind kind); }; /// A bit field enum to describe the kind of relation between two symbols. struct RelationKind : refl::Enum { enum Kind : uint32_t { Invalid, Declaration, Definition, Reference, // Write Relation. Read, Write, Interface, Implementation, /// When target is a type definition of source, source is possible type or constructor. TypeDefinition, /// When target is a base class of source. Base, /// When target is a derived class of source. Derived, /// When target is a constructor of source. Constructor, /// When target is a destructor of source. Destructor, // When target is a caller of source. Caller, // When target is a callee of source. Callee, }; using Enum::Enum; }; enum class OccurrenceKind { /// This occurrence directly corresponds to a unique source symbol. Source, /// This occurrence is a macro expansion. MacroExpansion, /// This occurrence is from `PseudoInstantiation` and may be not correct. PseudoInstantiation, /// This occurrence is from `ImplicitInstantiation` or `ExplicitInstantiation` of a template. Instantiation, }; 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()), 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"; } /// An occurrence directly corresponding to a symbol in source code. /// In most cases, a location just correspondings to unique decl. /// So a location will be just visited once. But in some other cases, /// a location may correspond to multiple decls. Note that we already /// filter some nodes with invalid location. /// /// Always uses spelling location if the original location is a macro location. void handleOccurrence(const clang::Decl* decl, clang::SourceRange range, RelationKind kind) {} /// 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::BuiltinType* type, clang::SourceRange range, OccurrenceKind kind = OccurrenceKind::Source) {} void handleOccurrence(const clang::Attr* attr, clang::SourceRange range) {} 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(NamespaceDecl) { /// `namespace Foo { }` /// ^~~~ definition getDerived().handleOccurrence(decl, decl->getLocation(), RelationKind::Definition); return true; } VISIT_DECL(NamespaceAliasDecl) { /// `namespace Foo = Bar` /// ^ ^~~~ reference /// ^~~~ definition getDerived().handleOccurrence(decl, decl->getLocation(), RelationKind::Definition); getDerived().handleOccurrence(decl->getNamespace(), decl->getTargetNameLoc(), RelationKind::Reference); return true; } VISIT_DECL(UsingDirectiveDecl) { /// `using namespace Foo` /// ^~~~~~~ reference getDerived().handleOccurrence(decl->getNominatedNamespace(), decl->getLocation(), RelationKind::Reference); return true; } VISIT_DECL(LabelDecl) { /// `label:` /// ^~~~ definition getDerived().handleOccurrence(decl, decl->getLocation(), RelationKind::Definition); return true; } VISIT_DECL(FieldDecl) { /// `int foo;` /// ^~~~ definition getDerived().handleOccurrence(decl, decl->getLocation(), RelationKind::Definition); /// FIXME: add Type Definition return true; } VISIT_DECL(EnumConstantDecl) { /// `enum Foo { bar };` /// ^~~~ definition getDerived().handleOccurrence(decl, decl->getLocation(), RelationKind::Definition); /// FIXME: add Type Definition return true; } VISIT_DECL(UsingDecl) { /// `using Foo::bar;` /// ^~~~ reference for(auto shadow: decl->shadows()) { getDerived().handleOccurrence(shadow, decl->getLocation(), RelationKind::Reference); } return true; } VISIT_DECL(BindingDecl) { /// `auto [a, b] = std::make_tuple(1, 2);` /// ^~~~ definition getDerived().handleOccurrence(decl, decl->getLocation(), RelationKind::Definition); /// FIXME: add Type Definition return true; } VISIT_DECL(TemplateTypeParmDecl) { /// `template ` /// ^~~~ definition getDerived().handleOccurrence(decl, decl->getLocation(), RelationKind::Definition); return true; } VISIT_DECL(TemplateTemplateParmDecl) { /// `template