Last active
August 25, 2024 18:42
-
-
Save alexst07/7dadf36ea663171e91778a77d01fbbda to your computer and use it in GitHub Desktop.
Parser C++ declarations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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