First working version.

This commit is contained in:
Erki 2021-02-21 17:42:42 +02:00
parent e60cd90d55
commit 7849afc4dd
6 changed files with 359 additions and 0 deletions

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# Build folders
[Bb]uild/
cmake-*/

8
CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.17)
project("C Analyzer")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
add_subdirectory(op-finder)

37
op-finder/CMakeLists.txt Normal file
View File

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.17)
find_package(Clang REQUIRED)
find_package(LLVM REQUIRED COMPONENTS Support Option Core)
# LLVM is typically compiled without RTTI. Weird linker errors ensue if
# you keep RTTI on and try to link.
if (NOT LLVM_ENABLE_RTTI)
if (MSVC)
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
else ()
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif ()
endif ()
llvm_map_components_to_libnames(llvm_libs support option core)
add_executable(llvm_proto main.cpp OperationFinder.cpp)
target_include_directories(llvm_proto
PRIVATE ${LLVM_INCLUDE_DIRS}
)
target_compile_definitions(llvm_proto
PRIVATE
${LLVM_DEFINITIONS}
)
target_link_libraries(llvm_proto
PRIVATE
${llvm_libs}
clangTooling
clangBasic
clangASTMatchers
)

View File

@ -0,0 +1,206 @@
//
// Created by erki on 16.02.21.
//
#include "OperationFinder.hpp"
#include <iostream>
#include <sstream>
using namespace clang;
using namespace clang::ast_matchers;
namespace
{
StatementMatcher AssignmentMatcher =
binaryOperation(isAssignmentOperator(),
hasLHS(expr().bind("lhs")),
hasRHS(expr().bind("rhs"))).bind("assignment");
StatementMatcher ArithmeticMatcher =
binaryOperation(hasAnyOperatorName("+", "-", "/", "*", "<", ">",
"<=", ">=", "==", "<<", ">>", "%"),
hasLHS(expr().bind("lhs")),
hasRHS(expr().bind("lhs"))).bind("arithmetic");
StatementMatcher UnaryArithmeticMatcher =
unaryOperator(hasAnyOperatorName("!", "++", "--"),
hasUnaryOperand(expr().bind("lhs"))).bind("unary_arithmetic");
StatementMatcher CompoundMatcher =
compoundStmt().bind("compound_stmt");
bool isInMainFile(const MatchFinder::MatchResult &result, const SourceLocation &loc)
{
return result.Context->getSourceManager().isWrittenInMainFile(loc);
}
template<typename TOp>
StringRef getOpcode(const TOp *op)
{
return op->getOpcodeStr(op->getOpcode());
}
}
void OperationFinder::addMatcher(MatchFinder &finder)
{
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, ArithmeticMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, AssignmentMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, CompoundMatcher), this);
finder.addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, UnaryArithmeticMatcher), this);
}
void OperationFinder::run(const MatchFinder::MatchResult &result)
{
if (result.Nodes.getNodeAs<clang::BinaryOperator>("assignment"))
{
_processAssignment(result);
}
else if (result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic"))
{
_processArithmetic(result);
}
else if (result.Nodes.getNodeAs<clang::CompoundStmt>("compound_stmt"))
{
_processCompoundStmt(result);
}
else if (result.Nodes.getNodeAs<clang::UnaryOperator>("unary_arithmetic"))
{
_processUnaryArithmetic(result);
}
}
void OperationFinder::_processAssignment(const clang::ast_matchers::MatchFinder::MatchResult& result)
{
const BinaryOperator* op = result.Nodes.getNodeAs<BinaryOperator>("assignment");
assert(op);
if (!isInMainFile(result, op->getBeginLoc()))
return;
const auto& loc = op->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Assignment: Assigned to: ";
_processExpressionTypes(op, op->getLHS(), op->getRHS());
llvm::outs() << "\n";
}
void OperationFinder::_processArithmetic(const MatchFinder::MatchResult& result)
{
const BinaryOperator* op = result.Nodes.getNodeAs<clang::BinaryOperator>("arithmetic");
assert(op);
if (!isInMainFile(result, op->getBeginLoc()))
return;
const auto& loc = op->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Arithmetic: Type: " << getOpcode(op) << " LHS: ";
_processExpressionTypes(op, op->getLHS(), op->getRHS());
llvm::outs() << "\n";
}
void OperationFinder::_processCompoundStmt(const clang::ast_matchers::MatchFinder::MatchResult& result)
{
const CompoundStmt* stmt = result.Nodes.getNodeAs<CompoundStmt>("compound_stmt");
assert(stmt);
const CompoundAssignOperator* op = dyn_cast<CompoundAssignOperator>(stmt->getStmtExprResult());
if (!op || !op->isCompoundAssignmentOp())
return;
if (!isInMainFile(result, op->getBeginLoc()))
return;
const auto& loc = op->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Compound assignment: Op code: " << getOpcode(op) << " ";
_processExpressionTypes(op, op->getLHS(), op->getRHS());
llvm::outs() << "\n";
}
void OperationFinder::_processUnaryArithmetic(const MatchFinder::MatchResult &result)
{
const UnaryOperator* op = result.Nodes.getNodeAs<UnaryOperator>("unary_arithmetic");
const Expr* lhs = result.Nodes.getNodeAs<Expr>("lhs");
assert(op);
assert(lhs);
if (!isInMainFile(result, lhs->getBeginLoc()))
return;
const auto& loc = lhs->getBeginLoc();
const auto& source_manager = *result.SourceManager;
llvm::outs() << source_manager.getFilename(loc) << ":"
<< source_manager.getSpellingLineNumber(loc) << ":"
<< source_manager.getSpellingColumnNumber(loc) << ":"
<< "Unary arithmetic: Type: " << getOpcode(op) << " LHS: ";
_processExpressionTypes(op, lhs, nullptr);
llvm::outs() << "\n";
}
void OperationFinder::_processExpressionTypes(const Expr* source, const Expr* op1, const Expr* op2)
{
auto printTypeName = [](const QualType& t) -> void
{
if (t->isTypedefNameType())
{
const TypedefType* tdt = cast<TypedefType>(t);
assert(tdt);
llvm::outs() << "Typedef type: " << t.getAsString()
<< ", underlying: " << tdt->desugar().getAsString()
<< ".";
}
else if (t->isBuiltinType())
{
llvm::outs() << "Builtin type: " << t.getAsString() << ".";
}
else
{
llvm::outs() << "Other type: " << t.getAsString() << ".";
}
};
llvm::outs() << "Expression types:\n\tExpression eval type: ";
printTypeName(source->getType());
if (op1)
{
llvm::outs() << "\n\tLHS eval type: ";
printTypeName(op1->getType());
}
if (op2)
{
llvm::outs() << "\n\tRHS eval type: ";
printTypeName(op2->getType());
}
}

View File

@ -0,0 +1,26 @@
//
// Created by erki on 16.02.21.
//
#ifndef LLVM_PROTO_OPERATIONFINDER_HPP
#define LLVM_PROTO_OPERATIONFINDER_HPP
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
class OperationFinder : public clang::ast_matchers::MatchFinder::MatchCallback
{
public:
void addMatcher(clang::ast_matchers::MatchFinder& finder);
virtual void run(const clang::ast_matchers::MatchFinder::MatchResult& result) override;
private:
void _processAssignment(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processArithmetic(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processCompoundStmt(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processUnaryArithmetic(const clang::ast_matchers::MatchFinder::MatchResult& result);
void _processExpressionTypes(const clang::Expr* source, const clang::Expr* op1, const clang::Expr* op2);
};
#endif //LLVM_PROTO_OPERATIONFINDER_HPP

46
op-finder/main.cpp Normal file
View File

@ -0,0 +1,46 @@
// Declares clang::SyntaxOnlyAction.
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
// Declares llvm::cl::extrahelp.
#include "llvm/Support/CommandLine.h"
#include "OperationFinder.hpp"
using namespace clang::tooling;
using namespace clang::ast_matchers;
using namespace llvm;
// Apply a custom category to all command-line options so that they are the
// only ones displayed.
static llvm::cl::OptionCategory MyToolCategory("opcounter options");
// CommonOptionsParser declares HelpMessage with a description of the common
// command-line options related to the compilation database and input files.
// It's nice to have this help message in all tools.
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
// A help message for this specific tool can be added afterwards.
static cl::extrahelp MoreHelp("\nMore help text...\n");
int main(int argc, const char** argv)
{
auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);
if (!ExpectedParser)
{
// Fail gracefully for unsupported options.
llvm::errs() << ExpectedParser.takeError();
return 1;
}
CommonOptionsParser& OptionsParser = ExpectedParser.get();
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());
OperationFinder op_finder;
MatchFinder finder;
op_finder.addMatcher(finder);
return Tool.run(newFrontendActionFactory(&finder).get());
}