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