First working version.
This commit is contained in:
parent
e60cd90d55
commit
7849afc4dd
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal 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
8
CMakeLists.txt
Normal 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
37
op-finder/CMakeLists.txt
Normal 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
|
||||
)
|
||||
206
op-finder/OperationFinder.cpp
Normal file
206
op-finder/OperationFinder.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
26
op-finder/OperationFinder.hpp
Normal file
26
op-finder/OperationFinder.hpp
Normal 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
46
op-finder/main.cpp
Normal 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());
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user