Skip to content

Аникин Максим. Лабораторная работа 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

Open
wants to merge 3 commits into
base: course-spring-2025
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions clang/compiler-course/anikin_m_ast/CMakeLists.txt
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)
166 changes: 166 additions & 0 deletions clang/compiler-course/anikin_m_ast/anikin_m_ast.cpp
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>
Comment on lines +6 to +7
Copy link
Collaborator

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?


namespace anikin_m_ast {
Copy link
Collaborator

Choose a reason for hiding this comment

The 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)
Copy link

Choose a reason for hiding this comment

The 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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
out << "|" << getAccessSpecifierString(var->getAccess());
out << ")\n";
out << "|" << getAccessSpecifierString(var->getAccess()) << ")\n";

}
}
}

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[](const clang::ParmVarDecl *param) {
llvm::outs() << param->getType().getAsString();
}
[&out](const clang::ParmVarDecl *param) {
out << param->getType().getAsString();
}


);
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> {
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 (Visitor, Consumer, Action)

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");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Y("data_type", "Analyzes AST and prints information about data types");
Y("data_type", "Analyzes AST and prints information about data types");

67 changes: 67 additions & 0 deletions clang/test/compiler-course/anikin_m_ast_test/test.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding tests for union covered in your pass

Copy link
Author

Choose a reason for hiding this comment

The 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() { }
};

// CHECK: BaseClass(class)
// CHECK-NEXT: |_Fields
// CHECK-NEXT: | |_ (has no fields)
// CHECK-NEXT: |_Methods
// CHECK-NEXT: | |_ (has no methods)
class BaseClass {};

// 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;
};
Loading