Files
clice/include/AST/Semantic.h
2025-08-10 22:31:00 +08:00

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 interested_only) :
Base(unit, interested_only), 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::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<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 = 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<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 = ast::decl_of(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 = ast::decl_of(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 = ast::instantiated_from(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 = ast::decl_of(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 = ast::decl_of(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 = ast::decl_of(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 = ast::decl_of(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