-
Notifications
You must be signed in to change notification settings - Fork 54
Аникин Максим. Лабораторная работа 1. Clang Ast. Вариант 1. #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: course-spring-2025
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
set(Title "DataType") | ||
set(Student "Anikin_Maksim") | ||
set(Group "FIIT2") | ||
set(TARGET_NAME "${Title}_${Student}_${Group}_ClangAST") | ||
|
||
file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) | ||
add_llvm_library(${TARGET_NAME} MODULE ${SOURCES} PLUGIN_TOOL clang) | ||
|
||
if(WIN32 OR CYGWIN) | ||
set(LLVM_LINK_COMPONENTS Support) | ||
clang_target_link_libraries(${TARGET_NAME} PRIVATE | ||
clangAST | ||
clangBasic | ||
clangFrontend | ||
) | ||
endif() | ||
|
||
set(CLANG_TEST_DEPS "${TARGET_NAME}" ${CLANG_TEST_DEPS} PARENT_SCOPE) |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,166 @@ | ||||||||||||||
#include "clang/AST/ASTConsumer.h" | ||||||||||||||
#include "clang/AST/RecursiveASTVisitor.h" | ||||||||||||||
#include "clang/Frontend/CompilerInstance.h" | ||||||||||||||
#include "clang/Frontend/FrontendPluginRegistry.h" | ||||||||||||||
#include "llvm/Support/raw_ostream.h" | ||||||||||||||
#include <algorithm> | ||||||||||||||
#include <string> | ||||||||||||||
|
||||||||||||||
namespace anikin_m_ast { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the context of a plugin, then it is better to use an anonymous namespace |
||||||||||||||
|
||||||||||||||
std::string getAccessSpecifierString(clang::AccessSpecifier access) { | ||||||||||||||
if (access == clang::AS_public) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. switch..case is a bit more effective here. |
||||||||||||||
return "public"; | ||||||||||||||
else if (access == clang::AS_private) | ||||||||||||||
return "private"; | ||||||||||||||
else if (access == clang::AS_protected) | ||||||||||||||
return "protected"; | ||||||||||||||
return ""; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
bool containsFields(const clang::CXXRecordDecl *record) { | ||||||||||||||
for (const auto *decl : record->decls()) { | ||||||||||||||
if (llvm::isa<clang::FieldDecl>(decl)) | ||||||||||||||
return true; | ||||||||||||||
if (const auto *var = llvm::dyn_cast<clang::VarDecl>(decl)) { | ||||||||||||||
return true; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
return false; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
void processFields(const clang::CXXRecordDecl *record, llvm::raw_ostream &out) { | ||||||||||||||
for (const auto *decl : record->decls()) { | ||||||||||||||
if (const auto *field = llvm::dyn_cast<clang::FieldDecl>(decl)) { | ||||||||||||||
out << "| |_ " << field->getName() << " (" | ||||||||||||||
<< field->getType().getAsString() << "|" | ||||||||||||||
<< getAccessSpecifierString(field->getAccess()) << ")\n"; | ||||||||||||||
} else if (const auto *var = llvm::dyn_cast<clang::VarDecl>(decl)) { | ||||||||||||||
std::string typeStr = var->getType().getAsString(); | ||||||||||||||
out << "| |_ " << var->getName() << " (" << typeStr; | ||||||||||||||
if (var->isStaticDataMember()) | ||||||||||||||
out << "|static"; | ||||||||||||||
|
||||||||||||||
out << "|" << getAccessSpecifierString(var->getAccess()); | ||||||||||||||
|
||||||||||||||
out << ")\n"; | ||||||||||||||
Comment on lines
+43
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
void processMethods(const clang::CXXRecordDecl *record, | ||||||||||||||
llvm::raw_ostream &out) { | ||||||||||||||
if (std::none_of(record->method_begin(), record->method_end(), | ||||||||||||||
[](const clang::CXXMethodDecl *method) { | ||||||||||||||
return !method->isImplicit(); | ||||||||||||||
})) { | ||||||||||||||
out << "| |_ (has no methods)\n"; | ||||||||||||||
return; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
for (auto &&method : record->methods()) { | ||||||||||||||
if (method->isImplicit()) | ||||||||||||||
continue; | ||||||||||||||
|
||||||||||||||
out << "| |_ " << method->getNameAsString() << " (" | ||||||||||||||
<< method->getReturnType().getAsString(); | ||||||||||||||
out << "("; | ||||||||||||||
llvm::interleaveComma(method->parameters(), out, | ||||||||||||||
[](const clang::ParmVarDecl *param) { | ||||||||||||||
llvm::outs() << param->getType().getAsString(); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+69
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
|
||||||||||||||
); | ||||||||||||||
out << ")"; | ||||||||||||||
if (method->isStatic()) | ||||||||||||||
out << "|static"; | ||||||||||||||
out << "|" << getAccessSpecifierString(method->getAccess()); | ||||||||||||||
if (record && | ||||||||||||||
std::any_of(record->friends().begin(), record->friends().end(), | ||||||||||||||
[method](const clang::FriendDecl *friendDecl) { | ||||||||||||||
if (const auto *FD = friendDecl->getFriendDecl()) | ||||||||||||||
return FD == method; | ||||||||||||||
return false; | ||||||||||||||
})) | ||||||||||||||
out << "|friend"; | ||||||||||||||
if (method->hasAttr<clang::OverrideAttr>()) | ||||||||||||||
out << "|override"; | ||||||||||||||
else if (method->isVirtual()) | ||||||||||||||
out << (method->isPureVirtual() ? "|virtual|pure" : "|virtual"); | ||||||||||||||
|
||||||||||||||
out << ")\n"; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
class ASTWalker final : public clang::RecursiveASTVisitor<ASTWalker> { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is better to follow the generally accepted naming when writing your classes ( |
||||||||||||||
public: | ||||||||||||||
explicit ASTWalker(clang::ASTContext *context) : context_(context) {} | ||||||||||||||
bool VisitCXXRecordDecl(clang::CXXRecordDecl *record) { | ||||||||||||||
auto &&out = llvm::outs(); | ||||||||||||||
// is union | ||||||||||||||
if (record->isUnion()) { | ||||||||||||||
out << record->getNameAsString() << "(union)\n"; | ||||||||||||||
} else { | ||||||||||||||
// is class or whatever | ||||||||||||||
out << record->getNameAsString(); | ||||||||||||||
if (record->getDescribedClassTemplate()) { | ||||||||||||||
out << "(" << (record->isStruct() ? "struct" : "class") << "|template)"; | ||||||||||||||
} else { | ||||||||||||||
out << "(" << (record->isStruct() ? "struct" : "class") << ")"; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if (record->getNumBases()) { | ||||||||||||||
out << " -> "; | ||||||||||||||
llvm::interleaveComma( | ||||||||||||||
record->bases(), out, [&](const clang::CXXBaseSpecifier &base) { | ||||||||||||||
out << getAccessSpecifierString(base.getAccessSpecifier()) << " " | ||||||||||||||
<< base.getType().getAsString(); | ||||||||||||||
}); | ||||||||||||||
} | ||||||||||||||
out << "\n"; | ||||||||||||||
} | ||||||||||||||
out << "|_Fields\n"; | ||||||||||||||
if (!containsFields(record)) { | ||||||||||||||
out << "| |_ (has no fields)\n"; | ||||||||||||||
} else { | ||||||||||||||
processFields(record, out); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
out << "|_Methods\n"; | ||||||||||||||
processMethods(record, out); | ||||||||||||||
|
||||||||||||||
return true; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
private: | ||||||||||||||
clang::ASTContext *context_; | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
class ASTAnalyzer final : public clang::ASTConsumer { | ||||||||||||||
public: | ||||||||||||||
explicit ASTAnalyzer(clang::ASTContext *context) : walker_(context) {} | ||||||||||||||
|
||||||||||||||
void HandleTranslationUnit(clang::ASTContext &context) override { | ||||||||||||||
walker_.TraverseDecl(context.getTranslationUnitDecl()); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
private: | ||||||||||||||
ASTWalker walker_; | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
class ASTPluginAction final : public clang::PluginASTAction { | ||||||||||||||
public: | ||||||||||||||
std::unique_ptr<clang::ASTConsumer> | ||||||||||||||
CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override { | ||||||||||||||
return std::make_unique<ASTAnalyzer>(&ci.getASTContext()); | ||||||||||||||
} | ||||||||||||||
bool ParseArgs(const clang::CompilerInstance &ci, | ||||||||||||||
const std::vector<std::string> &args) override { | ||||||||||||||
return true; | ||||||||||||||
} | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
} // namespace anikin_m_ast | ||||||||||||||
|
||||||||||||||
static clang::FrontendPluginRegistry::Add<anikin_m_ast::ASTPluginAction> | ||||||||||||||
Y("data_type", "Analyzes AST and prints information about data types"); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add test please struct A {};
struct B {
struct A {};
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// RUN: %clang_cc1 -load %llvmshlibdir/DataType_Anikin_Maksim_FIIT2_ClangAST%pluginext -plugin data_type -fsyntax-only %s 2>&1 | FileCheck %s | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding tests for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
// CHECK: Human(struct) | ||
// CHECK-NEXT: |_Fields | ||
// CHECK-NEXT: | |_ age (unsigned int|public) | ||
// CHECK-NEXT: | |_ height (unsigned int|public) | ||
// CHECK-NEXT: |_Methods | ||
// CHECK-NEXT: | |_ sleep (void()|public|virtual|pure) | ||
struct Human { | ||
unsigned age; | ||
unsigned height; | ||
virtual void sleep() = 0; | ||
virtual void eat() = 0; | ||
}; | ||
|
||
// CHECK: Engineer(struct) -> public Human | ||
// CHECK-NEXT: |_Fields | ||
// CHECK-NEXT: | |_ salary (unsigned int|public) | ||
// CHECK-NEXT: |_Methods | ||
// CHECK-NEXT: | |_ sleep (void()|public|override) | ||
// CHECK-NEXT: | |_ eat (void()|public|override) | ||
// CHECK-NEXT: | |_ work (double()|public) | ||
// CHECK-NEXT: | |_ type_float (float()|public) | ||
// CHECK-NEXT: | |_ type_char (char()|public) | ||
struct Engineer : Human { | ||
unsigned salary; | ||
void sleep() override { /* something */ } | ||
void eat() override { /* something */ } | ||
double work() { /* something */ } | ||
float type_float() { } | ||
char type_char() { } | ||
}; | ||
|
||
aobolensk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// CHECK: BaseClass(class) | ||
// CHECK-NEXT: |_Fields | ||
// CHECK-NEXT: | |_ (has no fields) | ||
// CHECK-NEXT: |_Methods | ||
// CHECK-NEXT: | |_ (has no methods) | ||
class BaseClass {}; | ||
|
||
aobolensk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// CHECK: NaslClass(class) -> public BaseClass | ||
// CHECK-NEXT: |_Fields | ||
// CHECK-NEXT: | |_ (has no fields) | ||
// CHECK-NEXT: |_Methods | ||
// CHECK-NEXT: | |_ (has no methods) | ||
class NaslClass : public BaseClass {}; | ||
|
||
// CHECK: TempClass(class|template) | ||
// CHECK-NEXT: |_Fields | ||
// CHECK-NEXT: | |_ type_t (T|private) | ||
// CHECK-NEXT: |_Methods | ||
// CHECK-NEXT: | |_ (has no methods) | ||
|
||
template <class T> class TempClass { | ||
T type_t; | ||
}; | ||
|
||
// CHECK: UnionCheck(union) | ||
// CHECK-NEXT: |_Fields | ||
// CHECK-NEXT: | |_ foo (int|public) | ||
// CHECK-NEXT: | |_ boo (char|public) | ||
// CHECK-NEXT: |_Methods | ||
// CHECK-NEXT: | |_ (has no methods) | ||
union UnionCheck { | ||
int foo; | ||
char boo; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need these header files?