730 lines
28 KiB
C++
730 lines
28 KiB
C++
#pragma once
|
|
|
|
#include "Utility.h"
|
|
#include "Resolver.h"
|
|
#include "SymbolKind.h"
|
|
#include "RelationKind.h"
|
|
#include "FilterASTVisitor.h"
|
|
|
|
namespace clice {
|
|
|
|
template <typename Derived>
|
|
class SemanticVisitor : public FilteredASTVisitor<SemanticVisitor<Derived>> {
|
|
public:
|
|
using Base = FilteredASTVisitor<SemanticVisitor>;
|
|
|
|
SemanticVisitor(CompilationUnit& unit, bool interestedOnly) :
|
|
Base(unit, interestedOnly, {}), unit(unit), resolver(unit.resolver()) {}
|
|
|
|
public:
|
|
Derived& getDerived() {
|
|
return static_cast<Derived&>(*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<decltype(&SemanticVisitor::handleDeclOccurrence),
|
|
decltype(&Derived::handleDeclOccurrence)>) {
|
|
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<decltype(&SemanticVisitor::handleMacroOccurrence),
|
|
decltype(&Derived::handleMacroOccurrence)>) {
|
|
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<clang::syntax::Token> identifiers) {
|
|
assert(keyword.isValid() && keyword.isFileID() && "Invalid keyword location");
|
|
|
|
/// FIXME: Check whether identifiers are valid.
|
|
|
|
if constexpr(!std::same_as<decltype(&SemanticVisitor::handleModuleOccurrence),
|
|
decltype(&Derived::handleModuleOccurrence)>) {
|
|
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<decltype(&SemanticVisitor::handleAttrOccurrence),
|
|
decltype(&Derived::handleAttrOccurrence)>) {
|
|
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<decltype(&SemanticVisitor::handleRelation),
|
|
decltype(&Derived::handleRelation)>) {
|
|
getDerived().handleRelation(decl, kind, target, range);
|
|
}
|
|
}
|
|
|
|
void run() {
|
|
if(Base::interestedOnly) {
|
|
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<clang::syntax::Token>(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 = 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,
|
|
llvm::cast<clang::NamedDecl>(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 = declForType(decl->getType())) {
|
|
handleRelation(decl, RelationKind::TypeDefinition, target, decl->getLocation());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// template <typename T>
|
|
/// ^~~~ definition
|
|
VISIT_DECL(TemplateTypeParmDecl) {
|
|
handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation());
|
|
handleRelation(decl, RelationKind::Definition, decl, decl->getLocation());
|
|
return true;
|
|
}
|
|
|
|
/// template <template <typename> class T>
|
|
/// ^~~~ definition
|
|
VISIT_DECL(TemplateTemplateParmDecl) {
|
|
handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation());
|
|
handleRelation(decl, RelationKind::Definition, decl, decl->getLocation());
|
|
return true;
|
|
}
|
|
|
|
/// template <int N>
|
|
/// ^~~~ definition
|
|
VISIT_DECL(NonTypeTemplateParmDecl) {
|
|
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;
|
|
}
|
|
|
|
/// struct/class/union/enum Foo { ... };
|
|
/// ^~~~ declaration/definition
|
|
VISIT_DECL(TagDecl) {
|
|
if(auto CTSD = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
|
|
switch(CTSD->getSpecializationKind()) {
|
|
case clang::TSK_Undeclared:
|
|
case clang::TSK_ImplicitInstantiation: {
|
|
std::unreachable();
|
|
}
|
|
|
|
case clang::TSK_ExplicitSpecialization: {
|
|
break;
|
|
}
|
|
|
|
case clang::TSK_ExplicitInstantiationDeclaration:
|
|
case clang::TSK_ExplicitInstantiationDefinition: {
|
|
auto decl = instantiatedFrom(CTSD);
|
|
handleDeclOccurrence(decl, RelationKind::Reference, CTSD->getLocation());
|
|
handleRelation(decl, RelationKind::Reference, decl, CTSD->getLocation());
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
RelationKind kind = decl->isThisDeclarationADefinition() ? RelationKind::Definition
|
|
: RelationKind::Declaration;
|
|
handleDeclOccurrence(decl, kind, decl->getLocation());
|
|
handleRelation(decl, kind, decl, decl->getLocation());
|
|
|
|
if(auto CRD = llvm::dyn_cast<clang::CXXRecordDecl>(decl)) {
|
|
if(auto def = CRD->getDefinition()) {
|
|
for(auto base: CRD->bases()) {
|
|
/// FIXME: Handle dependent base class.
|
|
if(auto target = declForType(base.getType())) {
|
|
handleRelation(def, RelationKind::Base, target, base.getSourceRange());
|
|
handleRelation(target, RelationKind::Derived, def, base.getSourceRange());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TraverseFunctionDecl(clang::FunctionDecl* decl) {
|
|
if(!decl) {
|
|
return true;
|
|
}
|
|
|
|
decls.emplace_back(decl);
|
|
auto result = Base::TraverseFunctionDecl(decl);
|
|
decls.pop_back();
|
|
return result;
|
|
}
|
|
|
|
/// void foo() { ... }
|
|
/// ^~~~ declaration/definition
|
|
VISIT_DECL(FunctionDecl) {
|
|
switch(decl->getTemplateSpecializationKind()) {
|
|
case clang::TSK_ImplicitInstantiation: {
|
|
std::unreachable();
|
|
}
|
|
|
|
/// FIXME: Clang currently doesn't record source location of explicit
|
|
/// instantiation of function template correctly. Skip it temporarily.
|
|
case clang::TSK_ExplicitInstantiationDeclaration:
|
|
case clang::TSK_ExplicitInstantiationDefinition: {
|
|
return true;
|
|
}
|
|
|
|
case clang::TSK_Undeclared:
|
|
case clang::TSK_ExplicitSpecialization: {
|
|
break;
|
|
}
|
|
}
|
|
|
|
RelationKind kind = decl->isThisDeclarationADefinition() ? RelationKind::Definition
|
|
: RelationKind::Declaration;
|
|
handleDeclOccurrence(decl, kind, decl->getLocation());
|
|
handleRelation(decl, kind, decl, decl->getLocation());
|
|
|
|
/// FIXME: Handle `CXXConversionDecl` and `CXXDeductionGuide`.
|
|
|
|
if(auto method = llvm::dyn_cast<clang::CXXMethodDecl>(decl)) {
|
|
for(auto override: method->overridden_methods()) {
|
|
handleRelation(method, RelationKind::Interface, override, decl->getLocation());
|
|
handleRelation(override, RelationKind::Implementation, method, decl->getLocation());
|
|
}
|
|
|
|
if(auto ctor = llvm::dyn_cast<clang::CXXConstructorDecl>(method)) {
|
|
handleRelation(ctor,
|
|
RelationKind::TypeDefinition,
|
|
ctor->getParent(),
|
|
decl->getLocation());
|
|
handleRelation(ctor->getParent(),
|
|
RelationKind::Constructor,
|
|
ctor,
|
|
decl->getLocation());
|
|
}
|
|
|
|
if(auto dtor = llvm::dyn_cast<clang::CXXDestructorDecl>(method)) {
|
|
handleRelation(dtor,
|
|
RelationKind::TypeDefinition,
|
|
dtor->getParent(),
|
|
decl->getLocation());
|
|
handleRelation(dtor->getParent(),
|
|
RelationKind::Destructor,
|
|
dtor,
|
|
decl->getLocation());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// using Foo = int;
|
|
/// ^~~~ definition
|
|
VISIT_DECL(TypedefNameDecl) {
|
|
handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation());
|
|
handleRelation(decl, RelationKind::Definition, decl, decl->getLocation());
|
|
|
|
if(auto target = declForType(decl->getUnderlyingType())) {
|
|
handleRelation(decl, RelationKind::TypeDefinition, target, decl->getLocation());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// int foo = 2;
|
|
/// ^~~~ declaration/definition
|
|
VISIT_DECL(VarDecl) {
|
|
if(auto TD = llvm::dyn_cast<clang::VarTemplateSpecializationDecl>(decl)) {
|
|
switch(TD->getSpecializationKind()) {
|
|
/// FIXME: Implicit instantiation should not occur in the lexical context. But
|
|
/// clang currently wrongly adds it to the lexical context. Skip it here.
|
|
case clang::TSK_ImplicitInstantiation:
|
|
|
|
/// FIXME: Clang currently doesn't record source location of explicit
|
|
/// instantiation of variable template correctly. Skip it temporarily.
|
|
case clang::TSK_ExplicitInstantiationDeclaration:
|
|
case clang::TSK_ExplicitInstantiationDefinition: {
|
|
return true;
|
|
}
|
|
|
|
case clang::TSK_Undeclared:
|
|
case clang::TSK_ExplicitSpecialization: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RelationKind kind = decl->isThisDeclarationADefinition() ? RelationKind::Definition
|
|
: RelationKind::Declaration;
|
|
handleDeclOccurrence(decl, kind, decl->getLocation());
|
|
handleRelation(decl, kind, decl, decl->getLocation());
|
|
|
|
if(auto target = declForType(decl->getType())) {
|
|
handleRelation(decl, RelationKind::TypeDefinition, target, decl->getLocation());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// TODO: Traverse explicit and implicit instantiations to add occurrence
|
|
/// to replacement template arguments.
|
|
|
|
VISIT_DECL(ClassTemplateDecl) {
|
|
return true;
|
|
}
|
|
|
|
VISIT_DECL(FunctionTemplateDecl) {
|
|
return true;
|
|
}
|
|
|
|
VISIT_DECL(VarTemplateDecl) {
|
|
return true;
|
|
}
|
|
|
|
/// template <typename T> concept Foo = ...;
|
|
/// ^~~~ definition
|
|
VISIT_DECL(ConceptDecl) {
|
|
handleDeclOccurrence(decl, RelationKind::Definition, decl->getLocation());
|
|
handleRelation(decl, RelationKind::Definition, decl, decl->getLocation());
|
|
return true;
|
|
}
|
|
|
|
/// requires Foo<T>;
|
|
/// ^~~~ reference
|
|
bool VisitConceptReference(clang::ConceptReference* reference) {
|
|
auto decl = reference->getNamedConcept();
|
|
auto location = reference->getConceptNameLoc();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
return true;
|
|
}
|
|
|
|
/// ============================================================================
|
|
/// TypeLoc
|
|
/// ============================================================================
|
|
|
|
/// unsigned int foo = 2;
|
|
/// ^~~~~~~~^~~~~~~~ reference
|
|
VISIT_TYPELOC(BuiltinTypeLoc) {
|
|
/// Should we handle builtin type separately? Note that it may
|
|
/// be composed of multiple tokens.
|
|
return true;
|
|
}
|
|
|
|
/// struct Foo foo;
|
|
/// ^~~~ reference
|
|
VISIT_TYPELOC(TagTypeLoc) {
|
|
auto decl = loc.getDecl();
|
|
auto location = loc.getNameLoc();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
return true;
|
|
}
|
|
|
|
/// using Foo = int; Foo foo;
|
|
/// ^~~~ reference
|
|
VISIT_TYPELOC(TypedefTypeLoc) {
|
|
auto decl = loc.getTypedefNameDecl();
|
|
auto location = loc.getNameLoc();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
return true;
|
|
}
|
|
|
|
/// template <typename T> void foo(T t)
|
|
/// ^~~~ reference
|
|
VISIT_TYPELOC(TemplateTypeParmTypeLoc) {
|
|
auto decl = loc.getDecl();
|
|
auto location = loc.getNameLoc();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
return true;
|
|
}
|
|
|
|
/// std::vector<int>
|
|
/// ^~~~ reference
|
|
VISIT_TYPELOC(TemplateSpecializationTypeLoc) {
|
|
if(auto type = loc.getTypePtr(); type->isDependentType()) {
|
|
/// FIXME: for dependent type, we always use the template resolver to
|
|
/// resolve the template decl.
|
|
return true;
|
|
}
|
|
|
|
auto decl = declForType(loc.getType());
|
|
auto location = loc.getTemplateNameLoc();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
return true;
|
|
}
|
|
|
|
/// std::vector<T>::value_type
|
|
/// ^~~~ reference
|
|
VISIT_TYPELOC(DependentNameTypeLoc) {
|
|
auto location = loc.getNameLoc();
|
|
// for(auto decl: resolver.lookup(loc.getTypePtr())) {
|
|
// handleDeclOccurrence(decl, RelationKind::WeakReference, location);
|
|
// handleRelation(decl, RelationKind::WeakReference, decl, location);
|
|
// }
|
|
return true;
|
|
}
|
|
|
|
/// std::allocator<T>::rebind<U>
|
|
/// ^~~~ reference
|
|
VISIT_TYPELOC(DependentTemplateSpecializationTypeLoc) {
|
|
auto location = loc.getTemplateNameLoc();
|
|
// for(auto decl: resolver.lookup(loc.getTypePtr())) {
|
|
// handleDeclOccurrence(decl, RelationKind::WeakReference, location);
|
|
// handleRelation(decl, RelationKind::WeakReference, decl, location);
|
|
// }
|
|
return true;
|
|
}
|
|
|
|
/// ============================================================================
|
|
/// Specifier
|
|
/// ============================================================================
|
|
|
|
bool VisitNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc loc) {
|
|
auto NNS = loc.getNestedNameSpecifier();
|
|
switch(NNS->getKind()) {
|
|
case clang::NestedNameSpecifier::Namespace: {
|
|
auto decl = NNS->getAsNamespace();
|
|
auto location = loc.getLocalBeginLoc();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
break;
|
|
}
|
|
|
|
case clang::NestedNameSpecifier::NamespaceAlias: {
|
|
auto decl = NNS->getAsNamespaceAlias();
|
|
auto location = loc.getLocalBeginLoc();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
break;
|
|
}
|
|
|
|
case clang::NestedNameSpecifier::Identifier: {
|
|
assert(NNS->isDependent() && "Identifier NNS should be dependent");
|
|
// FIXME: use TemplateResolver here.
|
|
break;
|
|
}
|
|
|
|
case clang::NestedNameSpecifier::TypeSpec:
|
|
case clang::NestedNameSpecifier::TypeSpecWithTemplate:
|
|
case clang::NestedNameSpecifier::Global:
|
|
case clang::NestedNameSpecifier::Super: {
|
|
break;
|
|
};
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VisitAttr(clang::Attr* attr) {
|
|
getDerived().handleAttrOccurrence(attr, attr->getRange());
|
|
return true;
|
|
}
|
|
|
|
/// ============================================================================
|
|
/// Statement
|
|
/// ============================================================================
|
|
|
|
/// foo = 1
|
|
/// ^~~~ reference
|
|
VISIT_EXPR(DeclRefExpr) {
|
|
auto decl = expr->getDecl();
|
|
auto location = expr->getLocation();
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
return true;
|
|
}
|
|
|
|
/// foo.bar
|
|
/// ^~~~ reference
|
|
VISIT_EXPR(MemberExpr) {
|
|
auto decl = expr->getMemberDecl();
|
|
auto location = expr->getMemberLoc();
|
|
if(location.isInvalid()) {
|
|
/// FIXME: if the location of member loc is invalid, this represents it is a
|
|
/// implicit member expr, e.g. `if(x)`, implicit `if(x.operator bool())`. Try to
|
|
/// use parens around the member loc for occurrence.
|
|
///
|
|
/// Known implicit member expr:
|
|
/// - implicit constructor
|
|
/// - implicit conversion operator
|
|
|
|
assert((llvm::isa<clang::CXXConstructorDecl, clang::CXXConversionDecl>(decl)) &&
|
|
"Invalid member location");
|
|
|
|
return true;
|
|
}
|
|
|
|
handleDeclOccurrence(decl, RelationKind::Reference, location);
|
|
handleRelation(decl, RelationKind::Reference, decl, location);
|
|
return true;
|
|
}
|
|
|
|
/// std::is_same<T, U>::value
|
|
/// ^~~~ reference
|
|
VISIT_EXPR(UnresolvedLookupExpr) {
|
|
return true;
|
|
}
|
|
|
|
/// std::is_same<T, U>::value
|
|
/// ^~~~ reference
|
|
VISIT_EXPR(DependentScopeDeclRefExpr) {
|
|
return true;
|
|
}
|
|
|
|
/// foo.T::value
|
|
/// ^~~~ reference
|
|
VISIT_EXPR(CXXDependentScopeMemberExpr) {
|
|
return true;
|
|
}
|
|
|
|
VISIT_EXPR(CallExpr) {
|
|
/// FIXME: Is the function decls is empty, it means that
|
|
/// the callee is a global variable decl or static assert decl.
|
|
/// Should we handle it separately?
|
|
if(decls.empty()) {
|
|
return true;
|
|
}
|
|
|
|
auto back = decls.back();
|
|
const clang::NamedDecl* caller = llvm::dyn_cast<clang::FunctionDecl>(back);
|
|
const clang::NamedDecl* callee = nullptr;
|
|
|
|
if(auto decl = expr->getCalleeDecl()) {
|
|
if(auto ND = llvm::dyn_cast<clang::NamedDecl>(decl)) {
|
|
callee = ND;
|
|
}
|
|
}
|
|
|
|
if(callee && caller) {
|
|
handleRelation(caller, RelationKind::Callee, callee, expr->getSourceRange());
|
|
handleRelation(callee, RelationKind::Caller, caller, expr->getSourceRange());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
CompilationUnit& unit;
|
|
TemplateResolver& resolver;
|
|
llvm::SmallVector<clang::Decl*> decls;
|
|
};
|
|
|
|
} // namespace clice
|