Files
clice/include/Compiler/Semantic.h
2024-12-04 01:12:40 +08:00

565 lines
19 KiB
C++

#include "Compiler.h"
#include "Resolver.h"
#include "Support/Enum.h"
#include "Support/FileSystem.h"
#include "Utility.h"
namespace clice {
enum class RelationKind : 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,
};
// struct RelationKind : enum_type<RelationKinds, true> {
// using enum RelationKinds;
// using enum_type::enum_type;
// using enum_type::operator=;
// };
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,
};
enum class SymbolKind {
};
template <typename Derived>
class SemanticVisitor : public clang::RecursiveASTVisitor<SemanticVisitor<Derived>> {
public:
using Base = clang::RecursiveASTVisitor<SemanticVisitor>;
SemanticVisitor(ASTInfo& compiler, bool mainFileOnly = false) :
sema(compiler.sema()), pp(compiler.pp()), resolver(compiler.resolver()),
srcMgr(compiler.srcMgr()), tokBuf(compiler.tokBuf()), mainFileOnly(mainFileOnly) {}
public:
public:
consteval bool VisitImplicitInstantiation() {
return true;
}
Derived& getDerived() {
return static_cast<Derived&>(*this);
}
bool needFilter(clang::SourceLocation location) {
return location.isInvalid() || (mainFileOnly && !srcMgr.isInMainFile(location));
}
void dump(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::SourceLocation location,
OccurrenceKind kind = OccurrenceKind::Source) {}
/// 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(clang::Attr* attr, clang::SourceRange range) {}
/// Always uses expansion location if the original location is a macro location.
void handleRelation(const clang::Decl* decl, RelationKind kind, clang::SourceRange range) {
///
}
public:
/// ============================================================================
/// Declaration
/// ============================================================================
TRAVERSE_DECL(Decl) {
if(!llvm::isa<clang::TranslationUnitDecl>(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());
return true;
}
VISIT_DECL(NamespaceAliasDecl) {
/// `namespace Foo = Bar`
/// ^ ^~~~ reference
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
getDerived().handleOccurrence(decl->getNamespace(), decl->getTargetNameLoc());
return true;
}
VISIT_DECL(UsingDirectiveDecl) {
/// `using namespace Foo`
/// ^~~~~~~ reference
getDerived().handleOccurrence(decl->getNominatedNamespace(), decl->getLocation());
return true;
}
VISIT_DECL(LabelDecl) {
/// `label:`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(FieldDecl) {
/// `int foo;`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(EnumConstantDecl) {
/// `enum Foo { bar };`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(UsingDecl) {
/// `using Foo::bar;`
/// ^~~~ reference
getDerived().handleOccurrence(decl, decl->getLocation());
/// FIXME:
return true;
}
VISIT_DECL(BindingDecl) {
/// `auto [a, b] = std::make_tuple(1, 2);`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(TemplateTypeParmDecl) {
/// `template <typename T>`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(TemplateTemplateParmDecl) {
/// `template <template <typename> class T>`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(NonTypeTemplateParmDecl) {
/// `template <int N>`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(TagDecl) {
/// FIXME:
/// It's possible that a class template specialization is a full specialization or a
/// explicit instantiation. And `ClassTemplatePartialSpecializationDecl` is the subclass of
/// `ClassTemplateSpecializationDecl`, it is also handled here.
///
/// For full specialization:
/// `template <> class Foo<int> { };`
/// ^~~~ declaration/definition
///
/// For explicit instantiation:
/// `template class Foo<int>;`
/// ^~~~ reference
// if(decl->getDescribedTemplate() ||
// llvm::isa<clang::ClassTemplateSpecializationDecl,
// clang::ClassTemplatePartialSpecializationDecl>(decl)) {
// return true;
// }
/// `struct/class/union/enum Foo { };`
/// ^~~~ declaration/definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(FunctionDecl) {
/// FIXME:
/// Because `TraverseFunctionTemplateDecl` will also traverse it's templated function. We
/// already handled the template function in `VisitFunctionTemplateDecl`. So we skip them
/// here.
// if(decl->getDescribedFunctionTemplate()) {
// return true;
// }
/// `void foo();`
/// ^~~~ declaration/definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(FunctionTemplateDecl) {
/// `template void foo<int>();`
/// ^~~~ reference
/// FIXME: Clang currently doesn't record the information of explicit instantiation
/// correctly. See https://github.com/llvm/llvm-project/issues/115418. And it is not added
/// to its lexical context. So here we use a workaround to handle explicit instantiation of
/// function template.
// for(auto spec: decl->specializations()) {
// auto kind = spec->getTemplateSpecializationKind();
// if(kind == clang::TSK_ExplicitInstantiationDeclaration ||
// kind == clang::TSK_ExplicitInstantiationDefinition) {
// /// WORKAROUND: Clang currently doesn't record the location of explicit
// /// instantiation. Use the location of the point of instantiation instead.
// if(auto location = builder.addLocation(spec->getPointOfInstantiation())) {
// auto symbol = builder.addSymbol(decl);
// symbol.addOccurrence(location);
// symbol.addReference(location);
// }
// }
// }
return true;
}
VISIT_DECL(TypedefNameDecl) {
/// FIXME: the location of type alias template is not recorded correctly, actually
/// it is the location of using keyword. But location its templated type is correct.
/// Temporarily use the location of the templated type.
/// `using Foo = int;`
/// ^~~~ declaration/definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(VarDecl) {
/// FIXME:
/// Because `TraverseVarTemplateSpecializationDecl` will also traverse it's templated
/// variable. We already handled the template variable in `VisitVar...`.
/// So we skip them here.
// if(decl->getDescribedVarTemplate() ||
// llvm::isa<clang::VarTemplateSpecializationDecl,
// clang::VarTemplatePartialSpecializationDecl>(decl)) {
// return true;
// }
/// `int foo;`
/// ^~~~ declaration/definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(VarTemplateSpecializationDecl) {
/// FIXME: it's strange that `VarTemplateSpecializationDecl` occurs in the lexical context.
/// This should be a bug of clang. Skip it here. And clang also doesn't record the
/// information about explicit instantiation of var template correctly. Skip them
/// temporarily.
auto kind = decl->getSpecializationKind();
if(kind == clang::TSK_ImplicitInstantiation ||
kind == clang::TSK_ExplicitInstantiationDeclaration ||
kind == clang::TSK_ExplicitInstantiationDefinition) {
return true;
}
/// FIXME:
/// It's possible that a var template specialization is a full specialization or a explicit
/// instantiation. And `VarTemplatePartialSpecializationDecl` is the subclass of
/// `VarTemplateSpecializationDecl`, it is also handled here.
///
/// For full specialization:
/// `template <> int foo<int>;`
/// ^~~~ declaration/definition
///
/// For explicit instantiation:
/// `template int foo<int>;`
/// ^~~~ reference
/// getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
VISIT_DECL(ConceptDecl) {
/// `template <typename T> concept Foo = ...;`
/// ^~~~ definition
getDerived().handleOccurrence(decl, decl->getLocation());
return true;
}
bool TraverseConceptReference(clang::ConceptReference* reference) {
/// `requires Foo<T>;`
/// ^~~~ reference
getDerived().handleOccurrence(reference->getNamedConcept(), reference->getConceptNameLoc());
return Base::TraverseConceptReference(reference);
}
/// ============================================================================
/// TypeLoc
/// ============================================================================
/// We don't care about type without location information.
constexpr bool TraverseType [[gnu::const]] (clang::QualType) {
return true;
}
bool TraverseTypeLoc(clang::TypeLoc loc) {
/// FIXME: Workaround for `QualifiedTypeLoc`.
if(auto QL = loc.getAs<clang::QualifiedTypeLoc>()) {
return Base::TraverseTypeLoc(QL.getUnqualifiedLoc());
}
if(needFilter(loc.getLocalSourceRange().getBegin())) {
return true;
}
return Base::TraverseTypeLoc(loc);
}
VISIT_TYPELOC(BuiltinTypeLoc) {
/// `int foo`
/// ^~~~ reference
getDerived().handleOccurrence(loc.getTypePtr(), loc.getLocalSourceRange());
return true;
}
VISIT_TYPELOC(TagTypeLoc) {
/// `struct Foo { }; Foo foo`
/// ^~~~ reference
getDerived().handleOccurrence(loc.getDecl(), loc.getNameLoc());
return true;
}
VISIT_TYPELOC(TypedefTypeLoc) {
/// `using Foo = int; Foo foo`
/// ^~~~ reference
getDerived().handleOccurrence(loc.getTypedefNameDecl(), loc.getNameLoc());
return true;
}
VISIT_TYPELOC(TemplateTypeParmTypeLoc) {
/// `template <typename T> void foo(T t)`
/// ^~~~ reference
getDerived().handleOccurrence(loc.getDecl(), loc.getNameLoc());
return true;
}
VISIT_TYPELOC(TemplateSpecializationTypeLoc) {
/// `std::vector<int>`
/// ^~~~ reference
getDerived().handleOccurrence(declForType(loc.getType()), loc.getTemplateNameLoc());
return true;
}
VISIT_TYPELOC(DependentNameTypeLoc) {
/// `std::vector<T>::value_type`
/// ^~~~ reference
for(auto decl: resolver.lookup(loc.getTypePtr())) {
getDerived().handleOccurrence(decl, loc.getNameLoc());
}
return true;
}
VISIT_TYPELOC(DependentTemplateSpecializationTypeLoc) {
/// `std::allocator<T>::rebind<U>`
/// ^~~~ reference
for(auto decl: resolver.lookup(loc.getTypePtr())) {
getDerived().handleOccurrence(decl, loc.getTemplateNameLoc());
}
return true;
}
/// ============================================================================
/// Specifier
/// ============================================================================
bool TraverseAttr(clang::Attr* attr) {
if(needFilter(attr->getLocation())) {
return true;
}
getDerived().handleOccurrence(attr, attr->getLocation());
return Base::TraverseAttr(attr);
}
/// FIXME: clang currently doesn't traverse attributes in `AttrbutedStmt` correctly.
/// See https://github.com/llvm/llvm-project/issues/117687.
bool TraverseAttributedStmt(clang::AttributedStmt* stmt) {
if(needFilter(stmt->getBeginLoc())) {
return true;
}
for(auto attr: stmt->getAttrs()) {
getDerived().handleOccurrence(attr, attr->getRange());
}
return Base::TraverseAttributedStmt(stmt);
}
bool TraverseCXXBaseSpecifier(const clang::CXXBaseSpecifier& base) {
return Base::TraverseCXXBaseSpecifier(base);
}
/// We don't care about name specifier without location information.
constexpr bool TraverseNestedNameSpecifier [[gnu::const]] (clang::NestedNameSpecifier*) {
return true;
}
bool TraverseNestedNameSpecifierLoc(clang::NestedNameSpecifierLoc loc) {
if(!loc || needFilter(loc.getLocalBeginLoc())) {
return true;
}
auto NNS = loc.getNestedNameSpecifier();
switch(NNS->getKind()) {
case clang::NestedNameSpecifier::Namespace: {
getDerived().handleOccurrence(NNS->getAsNamespace(), loc.getLocalBeginLoc());
break;
}
case clang::NestedNameSpecifier::NamespaceAlias: {
getDerived().handleOccurrence(NNS->getAsNamespaceAlias(), loc.getLocalBeginLoc());
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 Base::TraverseNestedNameSpecifierLoc(loc);
}
bool TraverseConstructorInitializer(clang::CXXCtorInitializer* init) {
return Base::TraverseConstructorInitializer(init);
}
/// ============================================================================
/// Statement
/// ============================================================================
bool TraverseStmt(clang::Stmt* stmt) {
if(stmt && needFilter(stmt->getBeginLoc())) {
return true;
}
return Base::TraverseStmt(stmt);
}
VISIT_EXPR(CallExpr) {
// TODO: consider lambda expression.
return true;
}
VISIT_EXPR(DeclRefExpr) {
/// `foo = 1`
/// ^~~~ reference
getDerived().handleOccurrence(expr->getDecl(), expr->getLocation());
return true;
}
VISIT_EXPR(MemberExpr) {
/// `foo.bar`
/// ^~~~ reference
getDerived().handleOccurrence(expr->getMemberDecl(), expr->getMemberLoc());
return true;
}
VISIT_EXPR(UnresolvedLookupExpr) {
/// `std::is_same<T, U>::value`
/// ^~~~ reference
for(auto decl: resolver.lookup(expr)) {
getDerived().handleOccurrence(decl,
expr->getNameLoc(),
OccurrenceKind::PseudoInstantiation);
}
return true;
}
VISIT_EXPR(DependentScopeDeclRefExpr) {
/// `std::is_same<T, U>::value`
/// ^~~~ reference
for(auto decl: resolver.lookup(expr)) {
getDerived().handleOccurrence(decl, expr->getNameInfo().getLoc());
}
return true;
}
VISIT_EXPR(CXXDependentScopeMemberExpr) {
/// `std::is_same<T, U>::value.T::value`
/// ^~~~ reference
return true;
}
protected:
bool mainFileOnly;
clang::Sema& sema;
clang::Preprocessor& pp;
TemplateResolver& resolver;
clang::SourceManager& srcMgr;
clang::syntax::TokenBuffer& tokBuf;
llvm::SmallVector<clang::Decl*> decls;
};
} // namespace clice