Skip to content

Instantly share code, notes, and snippets.

@alexst07
Last active August 25, 2024 18:42
Show Gist options
  • Save alexst07/7dadf36ea663171e91778a77d01fbbda to your computer and use it in GitHub Desktop.
Save alexst07/7dadf36ea663171e91778a77d01fbbda to your computer and use it in GitHub Desktop.
Parser C++ declarations
cmake_minimum_required(VERSION 3.10)
project(ClangFunctionParser)
find_package(Clang REQUIRED CONFIG)
add_executable(function_parser main.cpp)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
target_include_directories(function_parser PRIVATE ${CLANG_INCLUDE_DIRS})
target_link_libraries(function_parser PRIVATE
clangTooling
clangBasic
clangASTMatchers
)
set_target_properties(function_parser PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
template<typename T>
concept HasValueType = requires {
typename T::value_type;
typename T::test;
};
template <class myType>
myType GetMax (myType a, int b) {
return (a>b?a:b);
}
template<class T>
class A {
A(){}
using value_type = int;
int func_A1(T a, int b) {
return 5;
}
};
class SimpleClass {
public:
using value_type = int; // This is the nested type alias that satisfies the concept
using test = float;
};
template<class T>
class B {
B(){}
int func_A1(T a, int b) {
return 5;
}
};
class X{};
float test(A<float> x, float b, A<B<int>> y) {
return 4.0;
}
template<class T>
float test2(A<T> x, float b, A<B<T>> y) {
return 4.0;
}
int main() {
return 0;
}
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclTemplate.h>
#include <llvm/Support/CommandLine.h>
#include <clang/Sema/Sema.h>
#include <clang/Sema/Template.h>
#include <clang/AST/ASTContext.h>
#include <iostream>
using namespace clang;
using namespace clang::tooling;
using namespace llvm;
// Define the option category for command-line options
static llvm::cl::OptionCategory MyToolCategory("my-tool options");
std::map<std::string, const CXXRecordDecl*> ClassMap;
std::map<std::string, const ConceptDecl*> ConceptMap;
void CheckConceptUsage(Sema &SemaRef, const ConceptDecl *Concept, const CXXRecordDecl *Class) {
// Get the type of the class.
QualType ClassType = SemaRef.Context.getRecordType(Class);
// Create a TemplateArgument representing the class type.
TemplateArgument ClassTemplateArg(ClassType);
// Prepare the necessary data structures for the constraint check.
ConstraintSatisfaction Satisfaction;
// Create a MultiLevelTemplateArgumentList
MultiLevelTemplateArgumentList TemplateArgs;
ArrayRef<TemplateArgument> TemplateArgsRef(ClassTemplateArg);
TemplateArgs.addOuterTemplateArguments(const_cast<CXXRecordDecl *>(Class), TemplateArgsRef, /*Final*/ true);
// TemplateArgs.addOuterTemplateArguments(ArrayRef<TemplateArgument>(ClassTemplateArg));
// Retrieve the constraint expression associated with the concept
const Expr *ConstraintExpr = Concept->getConstraintExpr();
if (!ConstraintExpr) {
llvm::outs() << "The concept " << Concept->getNameAsString()
<< " has no constraints (requires clause) to check.\n";
return;
}
// Cast the constraint expression to RequiresExpr to access its components
if (const RequiresExpr *ReqExpr = llvm::dyn_cast<RequiresExpr>(ConstraintExpr)) {
std::cout << "--- CheckConceptUsage if " << std::endl;
// Get the list of requirements (constraints) in the requires expression
llvm::SmallVector<const Expr*, 4> ConstraintExprs;
for (const auto &Requirement : ReqExpr->getRequirements()) {
std::cout << "--- CheckConceptUsage for " << std::endl;
if (const auto *ExprReq = llvm::dyn_cast<clang::concepts::ExprRequirement>(Requirement)) {
// Handle expression requirements
std::cout << "--- CheckConceptUsage ExprRequirement" << std::endl;
ConstraintExprs.push_back(ExprReq->getExpr());
} else if (const auto *TypeReq = llvm::dyn_cast<clang::concepts::TypeRequirement>(Requirement)) {
// Handle type requirements by evaluating the type's instantiation dependency
std::cout << "--- CheckConceptUsage TypeRequirement" << std::endl;
QualType Type = TypeReq->getType()->getType();
QualType DependentType = TypeReq->getType()->getType();
if (Type->isDependentType()) {
std::cout << "--- CheckConceptUsage isDependentType" << std::endl;
// Create a pseudo-expression that checks if this type exists
// TypeTraitExpr *TraitExpr = TypeTraitExpr::Create(
// SemaRef.Context,
// DependentType,
// SourceLocation(),
// UTT_IsCompleteType, // Use a type trait like "is complete type"
// ArrayRef<QualType>(DependentType),
// SourceLocation(),
// SemaRef.Context.BoolTy
// );
// ConstraintExprs.push_back(TraitExpr);
}
}
}
std::cout << "--- CheckConceptUsage ConstraintExprs size:" << ConstraintExprs.size() << std::endl;
// Now use the updated list of constraints in the satisfaction check
bool IsSatisfied = SemaRef.CheckConstraintSatisfaction(
Concept,
ConstraintExprs,
TemplateArgs,
Class->getSourceRange(),
Satisfaction
);
if (IsSatisfied) {
llvm::outs() << "The class " << Class->getName() << " satisfies the concept " << Concept->getName() << ".\n";
} else {
llvm::outs() << "The class " << Class->getName() << " does NOT satisfy the concept " << Concept->getName() << ".\n";
}
} else {
llvm::outs() << "The concept " << Concept->getNameAsString()
<< " does not have a valid requires expression.\n";
}
}
class FunctionConsumer : public ASTConsumer {
public:
explicit FunctionConsumer(CompilerInstance &CI): CI(CI) {}
virtual void HandleTranslationUnit(ASTContext &Context) override {
TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
this->Context = &Context;
Sema &SemaRef = CI.getSema();
for (Decl *D : TU->decls()) {
printDeclKind(D);
if (CXXRecordDecl *CRD = llvm::dyn_cast<CXXRecordDecl>(D)) {
// Handle classes/structs and their methods
handleClass(CRD);
} else if (ConceptDecl *CD = llvm::dyn_cast<ConceptDecl>(D)) {
// Handle template classes
std::cout << "--- CONCEPT: " << std::endl;
printConceptDetails(CD, &Context);
std::cout << "--- END CONCEPT: " << std::endl;
}
}
// After collecting all declarations, test a class against a concept
auto ConceptIter = ConceptMap.find("HasValueType"); // Replace with actual concept name
auto ClassIter = ClassMap.find("SimpleClass"); // Replace with actual class name
if (ConceptIter != ConceptMap.end() && ClassIter != ClassMap.end()) {
CheckConceptUsage(SemaRef, ConceptIter->second, ClassIter->second);
}
}
private:
ASTContext *Context;
CompilerInstance &CI;
void printDeclKind(Decl *D) {
std::cout << "Declaration Kind: " << D->getDeclKindName() << std::endl;
}
void printConceptDetails(const clang::ConceptDecl *CD, clang::ASTContext *Context) {
std::cout << "Concept: " << CD->getNameAsString() << std::endl;
ConceptMap[CD->getNameAsString()] = CD;
std::cout << std::endl;
}
void handleClass(CXXRecordDecl *CRD) {
if (!CRD->isThisDeclarationADefinition()) {
return; // Skip forward declarations
}
std::cout << "Class: " << CRD->getNameAsString() << std::endl;
// Store the class in the map
ClassMap[CRD->getNameAsString()] = CRD;
}
};
class FindFunctionsAction : public ASTFrontendAction {
public:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) override {
return std::make_unique<FunctionConsumer>(CI);
}
};
int main(int argc, const char **argv) {
auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser& OptionsParser = ExpectedParser.get();
ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());
// Manually add the required C++ standard flag
std::vector<std::string> CompileFlags = {"-std=c++20"};
Tool.appendArgumentsAdjuster(getInsertArgumentAdjuster(CompileFlags, ArgumentInsertPosition::BEGIN));
return Tool.run(newFrontendActionFactory<FindFunctionsAction>().get());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment