From 40dbddb8a69b5a8386630a268881df0bb6f84f0c Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Thu, 28 Dec 2023 22:31:32 +0300 Subject: [PATCH 01/10] new: separate rules from oclint; tester.py --- CMakeLists.txt | 146 ++++++++++++++++++ README.md | 51 ++---- build-oclint.sh | 8 + examples/{ex1.c => ex1/main.c} | 0 examples/{ex2.c => ex2/main.c} | 0 examples/{ex3.c => ex3/main.c} | 0 examples/ex4/Makefile | 12 ++ examples/ex4/include/obfuscation.h | 1 + examples/ex4/include/stuff.h | 6 + examples/ex4/src/main.c | 10 ++ examples/ex4/src/stuff.c | 38 +++++ examples/ex5/main.c | 7 + install | 7 - oclint-rules/rules/etu/CMakeLists.txt | 3 - oclint-rules/test/etu/CMakeLists.txt | 3 - ...TooManyConsecutiveIfStatementsRuleTest.cpp | 15 -- .../TooManyConsecutiveIfStatementsRule.cpp | 0 tester.py | 85 ++++++++++ 18 files changed, 329 insertions(+), 63 deletions(-) create mode 100644 CMakeLists.txt create mode 100755 build-oclint.sh rename examples/{ex1.c => ex1/main.c} (100%) rename examples/{ex2.c => ex2/main.c} (100%) rename examples/{ex3.c => ex3/main.c} (100%) create mode 100644 examples/ex4/Makefile create mode 100644 examples/ex4/include/obfuscation.h create mode 100644 examples/ex4/include/stuff.h create mode 100644 examples/ex4/src/main.c create mode 100644 examples/ex4/src/stuff.c create mode 100644 examples/ex5/main.c delete mode 100755 install delete mode 100644 oclint-rules/rules/etu/CMakeLists.txt delete mode 100644 oclint-rules/test/etu/CMakeLists.txt delete mode 100644 oclint-rules/test/etu/TooManyConsecutiveIfStatementsRuleTest.cpp rename {oclint-rules/rules/etu => rules}/TooManyConsecutiveIfStatementsRule.cpp (100%) create mode 100644 tester.py diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b6c74ba --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,146 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.13.4) +PROJECT(OCLINT_EXTENSIONS) + +SET(LLVM_ROOT ${CMAKE_SOURCE_DIR}/oclint/build/llvm-install) + +SET(CMAKE_DISABLE_SOURCE_CHANGES ON) +SET(CMAKE_DISABLE_IN_SOURCE_BUILD ON) +set(CMAKE_MACOSX_RPATH ON) +SET(CMAKE_BUILD_TYPE None) + +IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + SET(CMAKE_CXX_FLAGS "-fcolor-diagnostics") +ENDIF() +SET(CMAKE_CXX_FLAGS "-std=c++14 ${CMAKE_CXX_LINKER_FLAGS} -fno-rtti -fPIC ${CMAKE_CXX_FLAGS}") +SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_CXX_LINKER_FLAGS} -fno-rtti") + +IF(OCLINT_BUILD_TYPE STREQUAL "Release") + SET(CMAKE_CXX_FLAGS "-O3 -DNDEBUG ${CMAKE_CXX_FLAGS}") + SET(CMAKE_SHARED_LINKER_FLAGS "-s ${CMAKE_SHARED_LINKER_FLAGS}") +ELSE() + SET(CMAKE_CXX_FLAGS "-O0 -g ${CMAKE_CXX_FLAGS}") + SET(CMAKE_SHARED_LINKER_FLAGS "-g ${CMAKE_SHARED_LINKER_FLAGS}") +ENDIF() + +SET(OCLINT_VERSION_RELEASE "22.02") + +IF(NOT EXISTS ${LLVM_ROOT}/include/llvm) + MESSAGE(FATAL_ERROR "LLVM_ROOT (${LLVM_ROOT}) is not a valid LLVM install. Could not find ${LLVM_ROOT}/include/llvm") +ENDIF() +MESSAGE("LLVM_ROOT: ${LLVM_ROOT}") +IF(EXISTS ${LLVM_ROOT}/lib/cmake/llvm) + SET(LLVM_DIR ${LLVM_ROOT}/lib/cmake/llvm) +ELSE() + SET(LLVM_DIR ${LLVM_ROOT}/share/llvm/cmake) +ENDIF() +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${LLVM_DIR}") +INCLUDE(LLVMConfig) + +INCLUDE_DIRECTORIES( ${LLVM_INCLUDE_DIRS} ) +LINK_DIRECTORIES( ${LLVM_LIBRARY_DIRS} ) +ADD_DEFINITIONS( ${LLVM_DEFINITIONS} ) + +STRING(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLVM_VERSION_RELEASE ${LLVM_PACKAGE_VERSION}) + +MESSAGE(STATUS "Found LLVM LLVM_PACKAGE_VERSION: ${LLVM_PACKAGE_VERSION} - LLVM_VERSION_RELEASE: ${LLVM_VERSION_RELEASE}") +MESSAGE(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") +LLVM_MAP_COMPONENTS_TO_LIBNAMES(REQ_LLVM_LIBRARIES asmparser bitreader instrumentation mcparser option support frontendopenmp) + +SET(CLANG_LIBRARIES + clangToolingCore + clangTooling + clangFrontend + clangDriver + clangSerialization + clangParse + clangSema + clangAnalysis + clangEdit + clangASTMatchers + clangAST + clangLex + clangBasic) + +IF(TEST_BUILD) + ENABLE_TESTING() + IF(NOT APPLE) + ADD_DEFINITIONS( + --coverage + ) + ENDIF() + + INCLUDE_DIRECTORIES( + ${GOOGLETEST_SRC}/googlemock/include + ${GOOGLETEST_SRC}/googletest/include + ) + LINK_DIRECTORIES( + ${GOOGLETEST_BUILD} + ${GOOGLETEST_BUILD}/lib + ) + SET(GTEST_LIBS gmock gtest) + + # Find CUDA + FIND_PROGRAM(NVIDIA_NVCC_BIN "nvcc") + IF (NVIDIA_NVCC_BIN) + MESSAGE(STATUS "Enable tests for CUDA rules.") + SET(TEST_CUDA TRUE) + ELSE() + SET(TEST_CUDA FALSE) + ENDIF() + + # Setup the path for profile_rt library + STRING(TOLOWER ${CMAKE_SYSTEM_NAME} COMPILER_RT_SYSTEM_NAME) + LINK_DIRECTORIES(${LLVM_LIBRARY_DIRS}/clang/${LLVM_VERSION_RELEASE}/lib/${COMPILER_RT_SYSTEM_NAME}) + IF(APPLE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + ELSEIF(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") + SET(PROFILE_RT_LIBS clang_rt.profile-aarch64 --coverage) + ELSE() + SET(PROFILE_RT_LIBS clang_rt.profile-x86_64 --coverage) + ENDIF() +ENDIF() + +IF(DOC_GEN_BUILD) + SET(CMAKE_CXX_FLAGS "-DDOCGEN ${CMAKE_CXX_FLAGS}") +ENDIF() + + + + +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/oclint/oclint-core/include + ${CMAKE_SOURCE_DIR}/oclint/oclint-metrics/include + ${CMAKE_SOURCE_DIR}/oclint/oclint-rules/include + ${CMAKE_SOURCE_DIR}/oclint/oclint-rules + ) +LINK_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/oclint/build/oclint-core/lib + ${CMAKE_SOURCE_DIR}/oclint/build/oclint-metrics/lib + ${CMAKE_SOURCE_DIR}/oclint/build/oclint-rules/lib/helper + ${CMAKE_SOURCE_DIR}/oclint/build/oclint-rules/lib/util + ) + +MACRO(build_dynamic_rule name) + ADD_LIBRARY(${name}Rule SHARED ${CMAKE_SOURCE_DIR}/rules/${name}Rule.cpp) + + #SET_TARGET_PROPERTIES(${name}Rule + # PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build + # RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build + # ) + + TARGET_LINK_LIBRARIES(${name}Rule OCLintAbstractRule) + + TARGET_LINK_LIBRARIES(${name}Rule + clangASTMatchers + ) + + TARGET_LINK_LIBRARIES(${name}Rule + OCLintMetric + OCLintHelper + OCLintUtil + OCLintCore + ) + install(TARGETS ${name}Rule DESTINATION lib/oclint/rules) +ENDMACRO(build_dynamic_rule) + +BUILD_DYNAMIC_RULE(TooManyConsecutiveIfStatements) \ No newline at end of file diff --git a/README.md b/README.md index 46f6f2b..e200b7c 100644 --- a/README.md +++ b/README.md @@ -7,45 +7,26 @@ ## Установка, сборка и использование -### Создание заготовок для новых правил внутри репозитория oclint - -Для начала склонируйте куда-нибудь [репозиторий oclint](https://github.com/oclint/oclint). - -Далее необходимо добавить "заготовки" для новых правил. Другими словами, необходимо создать исходники для новых правил и внести их в CMakeLists oclint'а. Новые правила относятся к группе "etu". Для этого выполните следующее: - -```bash -cd oclint-scripts -./scaffoldRule -t ASTVisitor -c etu -n"too many consecutive if statements" -p 2 TooManyConsecutiveIfStatementsRule -``` - -### Копирование исходников с новыми правилами в репозиторий oclint - -Из репозитория oclint_extensions выполните: +Требования и зависимости: +* [Требования и зависимости oclint](https://oclint-docs.readthedocs.io/en/stable/intro/build.html) +* CMake — для сборки +* Python — для tester.py +* bear: `sudo apt install bear` — генератор compile_commands.json +* ... ?? ```bash -cp -r ./oclint-rules YOUR_OCLINT_DIRECTORY_GOES_HERE +./build-oclint.sh +mkdir build +cd build +cmake ../ +cmake --build . +sudo cmake --install . ``` -### Сборка и установка +## Использование -Вернитесь в репозиторий oclint. Выполните следующее: -```bash -cd oclint-scripts -./make -release # этот скрипт начнет скачивание oclint-json-compilation-database размером в 514 МБ. имейте в виду - -cd .. -cd build/oclint-release -sudo cp ./bin/oclint* /usr/local/bin/ -sudo cp -rp ./lib/* /usr/local/lib/ -``` - -### Использование +tester.py принимает в качестве параметра путь до работы студента (например, Ivanov_Ivan_cw/src), генерирует compile_commands.json и проверяет каждый файл с помощью oclint. ``` -oclint --rule=TooManyConsecutiveIfStatements examples/ex*.c -``` - -## P.S. - -Очень странный способ установки всего этого. В теории можно сделать форк oclint'а, ноооооо сомнительно. Должен же быть какой-то более простой способ. -Критикуйте, предлагайте. \ No newline at end of file +python3 tester.py examples/ex1 +``` \ No newline at end of file diff --git a/build-oclint.sh b/build-oclint.sh new file mode 100755 index 0000000..4fa6f3e --- /dev/null +++ b/build-oclint.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +if [ ! -d oclint ]; then + git clone https://github.com/oclint/oclint.git +fi + +cd oclint/oclint-scripts +./make \ No newline at end of file diff --git a/examples/ex1.c b/examples/ex1/main.c similarity index 100% rename from examples/ex1.c rename to examples/ex1/main.c diff --git a/examples/ex2.c b/examples/ex2/main.c similarity index 100% rename from examples/ex2.c rename to examples/ex2/main.c diff --git a/examples/ex3.c b/examples/ex3/main.c similarity index 100% rename from examples/ex3.c rename to examples/ex3/main.c diff --git a/examples/ex4/Makefile b/examples/ex4/Makefile new file mode 100644 index 0000000..6c27f65 --- /dev/null +++ b/examples/ex4/Makefile @@ -0,0 +1,12 @@ +.PHONY: all + +all: prog + +prog: stuff.o main.o + gcc stuff.o main.o + +stuff.o: src/stuff.c include/obfuscation.h include/stuff.h + gcc -c -I ./include src/stuff.c + +main.o: src/main.c include/stuff.h + gcc -c -I ./include src/main.c \ No newline at end of file diff --git a/examples/ex4/include/obfuscation.h b/examples/ex4/include/obfuscation.h new file mode 100644 index 0000000..6724ae3 --- /dev/null +++ b/examples/ex4/include/obfuscation.h @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/examples/ex4/include/stuff.h b/examples/ex4/include/stuff.h new file mode 100644 index 0000000..ec15875 --- /dev/null +++ b/examples/ex4/include/stuff.h @@ -0,0 +1,6 @@ +#ifndef TEST_STUFF_H +#define TEST_STUFF_H + +void do_stuff(int x); + +#endif \ No newline at end of file diff --git a/examples/ex4/src/main.c b/examples/ex4/src/main.c new file mode 100644 index 0000000..8717854 --- /dev/null +++ b/examples/ex4/src/main.c @@ -0,0 +1,10 @@ +#include +#include "stuff.h" + +int main() +{ + int x = 0; + scanf("%d", &x); + + do_stuff(x); +} \ No newline at end of file diff --git a/examples/ex4/src/stuff.c b/examples/ex4/src/stuff.c new file mode 100644 index 0000000..9389813 --- /dev/null +++ b/examples/ex4/src/stuff.c @@ -0,0 +1,38 @@ +#include "obfuscation.h" +#include "stuff.h" + +void do_stuff(int x) { + if (x == 0) { + puts("Zero"); + } + else if (x == 1) { + puts("One"); + } + else if (x == 2) { + puts("Two"); + } + else if (x == 3) { + puts("Three"); + } + if (x == 4) { + puts("Four"); + } + else if (x == 5) { + puts("Five"); + } + if (x == 6) { + puts("Six"); + } + if (x == 7) { + puts("Seven"); + } + else if (x == 8) { + puts("Eight"); + } + else if (x == 9) { + puts("Nine"); + } + else { + puts("Something else"); + } +} \ No newline at end of file diff --git a/examples/ex5/main.c b/examples/ex5/main.c new file mode 100644 index 0000000..56688be --- /dev/null +++ b/examples/ex5/main.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + printf("OK!!!!\n"); + return 0; +} \ No newline at end of file diff --git a/install b/install deleted file mode 100755 index 70206d0..0000000 --- a/install +++ /dev/null @@ -1,7 +0,0 @@ -if [ $# -eq 0 ]; then - echo "usage: ./install [OCINT_REPO_DIRECTORY]" - exit -fi - -cp "$1"/build/oclint-release/bin/oclint* /usr/local/bin/ -cp -rp "$1"/build/oclint-release/lib/* /usr/local/lib/ \ No newline at end of file diff --git a/oclint-rules/rules/etu/CMakeLists.txt b/oclint-rules/rules/etu/CMakeLists.txt deleted file mode 100644 index 5f00b4c..0000000 --- a/oclint-rules/rules/etu/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ - -SET(BuildTooManyConsecutiveIfStatementsRule TooManyConsecutiveIfStatements) -BUILD_DYNAMIC_RULES("${BuildTooManyConsecutiveIfStatementsRule}") diff --git a/oclint-rules/test/etu/CMakeLists.txt b/oclint-rules/test/etu/CMakeLists.txt deleted file mode 100644 index 410df54..0000000 --- a/oclint-rules/test/etu/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ - -BUILD_TEST(TooManyConsecutiveIfStatementsRuleTest -TooManyConsecutiveIfStatementsRuleTest.cpp) diff --git a/oclint-rules/test/etu/TooManyConsecutiveIfStatementsRuleTest.cpp b/oclint-rules/test/etu/TooManyConsecutiveIfStatementsRuleTest.cpp deleted file mode 100644 index 27bab3f..0000000 --- a/oclint-rules/test/etu/TooManyConsecutiveIfStatementsRuleTest.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "TestRuleOnCode.h" -#include "rules/etu/TooManyConsecutiveIfStatementsRule.cpp" - -TEST(TooManyConsecutiveIfStatementsRuleTest, PropertyTest) -{ - TooManyConsecutiveIfStatementsRule rule; - EXPECT_EQ(2, rule.priority()); - EXPECT_EQ("too many consecutive if statements", rule.name()); - EXPECT_EQ("etu", rule.category()); -} - -TEST(TooManyConsecutiveIfStatementsRuleTest, FirstFailingTest) -{ - EXPECT_FALSE("Start writing a new test"); -} diff --git a/oclint-rules/rules/etu/TooManyConsecutiveIfStatementsRule.cpp b/rules/TooManyConsecutiveIfStatementsRule.cpp similarity index 100% rename from oclint-rules/rules/etu/TooManyConsecutiveIfStatementsRule.cpp rename to rules/TooManyConsecutiveIfStatementsRule.cpp diff --git a/tester.py b/tester.py new file mode 100644 index 0000000..f392dc2 --- /dev/null +++ b/tester.py @@ -0,0 +1,85 @@ +import sys +import json +import os +import shutil +from enum import Enum + +class CompilationType(Enum): + Invalid = 0 + SingleFile = 1 + Makefile = 2 + +def guess_compilation_type(): + files = [] + makefile_exists = False + + content = os.listdir() + for x in content: + if os.path.isdir(x): + continue + + filename, extension = os.path.splitext(x) + if extension in ['.c', '.cpp']: + files.append(x) + + if filename == 'Makefile' and extension == '': + makefile_exists = True + + if makefile_exists: + return (CompilationType.Makefile, files) + if len(files) == 1: + return (CompilationType.SingleFile, files) + return (CompilationType.Invalid, files) + +def main(): + if len(sys.argv) != 2: + print("Usage: python3 tester.py TARGET_DIRECTORY") + return + + path = sys.argv[1] + + if not os.path.exists(path): + print("Error: directory does not exist.") + return + + os.chdir(path) + + if os.path.exists('compile_commands.json'): + print('Error: "compile_commands.json" already exists.') + return + + if os.path.exists('.tester_tmp'): + print('Error: ".tester_tmp" already exists.') + return + + os.mkdir('.tester_tmp') + os.system('cp -rf * ./.tester_tmp/') + os.chdir('.tester_tmp') + + comp_type, files = guess_compilation_type() + match comp_type: + case CompilationType.Invalid: + print("Error: Invalid compilation type.") + return + case CompilationType.SingleFile: + cmds = [ { "directory": os.getcwd(), "arguments": ["gcc", files[0]], "file": files[0] } ] + with open('../compile_commands.json', 'w', encoding='utf-8') as f: + json.dump(cmds, f) + case CompilationType.Makefile: + os.system("bear -- make > /dev/null") + + cmds = json.load(open('compile_commands.json')) + files = [] + for x in cmds: + files.append(x['file']) + os.system("mv compile_commands.json ../") + + for file in files: + os.system(f'/bin/sh -c "oclint --rule=TooManyConsecutiveIfStatements "{file}" | grep etu"') + + os.chdir('..') + os.remove('compile_commands.json') + os.system("rm -rf .tester_tmp") + +if __name__ == '__main__': + main() \ No newline at end of file From f3702e937530a4f7096f4f1e7b58488cf0f3802e Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Thu, 28 Dec 2023 22:45:59 +0300 Subject: [PATCH 02/10] new: reset oclint to 22.02 --- build-oclint.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build-oclint.sh b/build-oclint.sh index 4fa6f3e..9529ca8 100755 --- a/build-oclint.sh +++ b/build-oclint.sh @@ -1,7 +1,10 @@ #!/bin/bash if [ ! -d oclint ]; then - git clone https://github.com/oclint/oclint.git + git clone https://github.com/oclint/oclint.git oclint + cd oclint + git reset --hard d776db51c8574df406b2b0dc1b43b0b9b2d86d34 + cd .. fi cd oclint/oclint-scripts From 30cb8b08841dc92a8e975099673e87a57cd02b65 Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Thu, 28 Dec 2023 23:20:48 +0300 Subject: [PATCH 03/10] new: change ex3 to c++ --- examples/ex3/main.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 examples/ex3/main.cpp diff --git a/examples/ex3/main.cpp b/examples/ex3/main.cpp new file mode 100644 index 0000000..c569cad --- /dev/null +++ b/examples/ex3/main.cpp @@ -0,0 +1,42 @@ +#include +using namespace std; + +int main() +{ + int x = 0; + cin >> x; + + if (x == 0) { + cout << "Zero"; + } + else if (x == 1) { + cout << "One"; + } + else if (x == 2) { + cout << "Two"; + } + else if (x == 3) { + cout << "Three"; + } + if (x == 4) { + cout << "Four"; + } + else if (x == 5) { + cout << "Five"; + } + if (x == 6) { + cout << "Six"; + } + if (x == 7) { + cout << "Seven"; + } + else if (x == 8) { + cout << "Eight"; + } + else if (x == 9) { + cout << "Nine"; + } + else { + cout << "Something else"; + } +} \ No newline at end of file From ec8ad1d8bedcae9a44eae5afce67f4c2b9f0eb52 Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Thu, 28 Dec 2023 23:55:40 +0300 Subject: [PATCH 04/10] new: rm ex3/main.c; TooLongIfSequenceRule; MAX_IF_SEQUENCE_LEN --- .gitignore | 2 + CMakeLists.txt | 2 +- README.md | 7 +--- examples/ex3/main.c | 41 ------------------- ...entsRule.cpp => TooLongIfSequenceRule.cpp} | 13 ++++-- tester.py | 2 +- 6 files changed, 14 insertions(+), 53 deletions(-) create mode 100644 .gitignore delete mode 100644 examples/ex3/main.c rename rules/{TooManyConsecutiveIfStatementsRule.cpp => TooLongIfSequenceRule.cpp} (86%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39fc9f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +oclint +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index b6c74ba..8ef0814 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,4 +143,4 @@ MACRO(build_dynamic_rule name) install(TARGETS ${name}Rule DESTINATION lib/oclint/rules) ENDMACRO(build_dynamic_rule) -BUILD_DYNAMIC_RULE(TooManyConsecutiveIfStatements) \ No newline at end of file +BUILD_DYNAMIC_RULE(TooLongIfSequence) \ No newline at end of file diff --git a/README.md b/README.md index e200b7c..75bceae 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,4 @@ -# oclint_extensions - -## Что это? - -Кастомные правила для oclint. Список реализованных правил: -* слишком много последовательных конструкций if (TooManyConsecutiveIfStatementsRule) +# oclint_extensions — кастомные правила для oclint ## Установка, сборка и использование diff --git a/examples/ex3/main.c b/examples/ex3/main.c deleted file mode 100644 index dc36117..0000000 --- a/examples/ex3/main.c +++ /dev/null @@ -1,41 +0,0 @@ -#include - -int main() -{ - int x = 0; - scanf("%d", &x); - - if (x == 0) { - puts("Zero"); - } - else if (x == 1) { - puts("One"); - } - else if (x == 2) { - puts("Two"); - } - else if (x == 3) { - puts("Three"); - } - if (x == 4) { - puts("Four"); - } - else if (x == 5) { - puts("Five"); - } - if (x == 6) { - puts("Six"); - } - if (x == 7) { - puts("Seven"); - } - else if (x == 8) { - puts("Eight"); - } - else if (x == 9) { - puts("Nine"); - } - else { - puts("Something else"); - } -} \ No newline at end of file diff --git a/rules/TooManyConsecutiveIfStatementsRule.cpp b/rules/TooLongIfSequenceRule.cpp similarity index 86% rename from rules/TooManyConsecutiveIfStatementsRule.cpp rename to rules/TooLongIfSequenceRule.cpp index d931ed4..97fca3d 100644 --- a/rules/TooManyConsecutiveIfStatementsRule.cpp +++ b/rules/TooLongIfSequenceRule.cpp @@ -7,9 +7,10 @@ using namespace std; using namespace clang; using namespace oclint; -class TooManyConsecutiveIfStatementsRule : public AbstractASTVisitorRule +class TooLongIfSequenceRule : public AbstractASTVisitorRule { private: + int max_if_sequence_len; size_t getIfComplexity(IfStmt *if_stmt) { Stmt *else_stmt = if_stmt->getElse(); @@ -21,12 +22,16 @@ class TooManyConsecutiveIfStatementsRule : public AbstractASTVisitorRulemax_if_sequence_len = RuleConfiguration::intForKey("MAX_IF_SEQUENCE_LEN", 5); + } + virtual void tearDown() override {} virtual const string name() const override { - return "too many consecutive if statements"; + return "too long if sequence"; } virtual int priority() const override @@ -97,4 +102,4 @@ class TooManyConsecutiveIfStatementsRule : public AbstractASTVisitorRule Date: Mon, 1 Jan 2024 03:35:16 +0300 Subject: [PATCH 05/10] add .oclint to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 39fc9f5..2e2a0f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ oclint -build \ No newline at end of file +build +.oclint From 2ba6a7bebfb53c3ebbf54ee42af58fea9ac4ab1d Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Mon, 1 Jan 2024 03:48:19 +0300 Subject: [PATCH 06/10] fix: MAX_IF_SEQUENCE_LEN --- rules/TooLongIfSequenceRule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/TooLongIfSequenceRule.cpp b/rules/TooLongIfSequenceRule.cpp index 97fca3d..c18b1ff 100644 --- a/rules/TooLongIfSequenceRule.cpp +++ b/rules/TooLongIfSequenceRule.cpp @@ -85,7 +85,7 @@ class TooLongIfSequenceRule : public AbstractASTVisitorRule 5) { + if (cur_complexity > this->max_if_sequence_len) { addViolation(first_if, this, toString(cur_complexity) + " consecutive if statements is too many."); } @@ -94,7 +94,7 @@ class TooLongIfSequenceRule : public AbstractASTVisitorRule 5) { + if (first_if != nullptr && cur_complexity > this->max_if_sequence_len) { addViolation(first_if, this, toString(cur_complexity) + " consecutive if statements is too many."); } From bbe0a0d3a86545e018b859443a82d1eebb955b1f Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Mon, 1 Jan 2024 04:37:42 +0300 Subject: [PATCH 07/10] add compile_commands.json to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2e2a0f2..d20373b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ oclint build .oclint +compile_commands.json \ No newline at end of file From efe2f3c7f32b04b7b75aace9bbc052bbf20afac3 Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Mon, 1 Jan 2024 05:00:57 +0300 Subject: [PATCH 08/10] rule: TooComplexCondition --- CMakeLists.txt | 3 +- examples/ex6/main.c | 16 ++++++ rules/TooComplexConditionRule.cpp | 92 +++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 examples/ex6/main.c create mode 100644 rules/TooComplexConditionRule.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ef0814..7789e39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,4 +143,5 @@ MACRO(build_dynamic_rule name) install(TARGETS ${name}Rule DESTINATION lib/oclint/rules) ENDMACRO(build_dynamic_rule) -BUILD_DYNAMIC_RULE(TooLongIfSequence) \ No newline at end of file +BUILD_DYNAMIC_RULE(TooLongIfSequence) +BUILD_DYNAMIC_RULE(TooComplexCondition) \ No newline at end of file diff --git a/examples/ex6/main.c b/examples/ex6/main.c new file mode 100644 index 0000000..021c6e6 --- /dev/null +++ b/examples/ex6/main.c @@ -0,0 +1,16 @@ +#include + +int main() +{ + int a, b, c, d, e, f; + scanf("%d %d %d %d %d %d", &a, &b, &c, &d, &e, &f); + + if (((a == b || b == c) && (d == e || e == f) || !(d == c)) && b != e) { + puts("Congrats! You found the secret code."); + } + else { + puts("Better luck next time."); + } + + return 0; +} \ No newline at end of file diff --git a/rules/TooComplexConditionRule.cpp b/rules/TooComplexConditionRule.cpp new file mode 100644 index 0000000..ede8d36 --- /dev/null +++ b/rules/TooComplexConditionRule.cpp @@ -0,0 +1,92 @@ +#include "oclint/AbstractASTVisitorRule.h" +#include "oclint/RuleConfiguration.h" +#include "oclint/RuleSet.h" +#include "oclint/util/StdUtil.h" +#include "clang/AST/Expr.h" + +using namespace std; +using namespace clang; +using namespace oclint; + +class TooComplexConditionRule : public AbstractASTVisitorRule +{ +private: + int max_condition_complexity; + size_t getStmtComplexity(Stmt *stmt) + { + size_t result = 0; + + Stmt::StmtClass sclass = stmt->getStmtClass(); + if (sclass == Stmt::BinaryOperatorClass) { + BinaryOperator *binop = reinterpret_cast(stmt); + result += binop->isLogicalOp(); + } + + for (Stmt::child_iterator child = stmt->child_begin(); child != stmt->child_end(); child++) { + result += getStmtComplexity(*child); + } + + return result; + } + +public: + virtual void setUp() override + { + this->max_condition_complexity = RuleConfiguration::intForKey("MAX_CONDITION_COMPLEXITY", 4); + } + + virtual void tearDown() override {} + + virtual const string name() const override + { + return "too complex condition"; + } + + virtual int priority() const override + { + return 2; + } + + virtual const string category() const override + { + return "etu"; + } + +#ifdef DOCGEN + virtual const std::string since() const override + { + return "22.02 etu"; + } + + virtual const std::string description() const override + { + return "..."; + } + + virtual const std::string example() const override + { + return R"rst( +.. code-block:: cpp + + void example() + { + // TODO: modify the example for this rule. + } + )rst"; + } +#endif + + bool VisitIfStmt(IfStmt *node) + { + Expr *condition = node->getCond(); + size_t complexity = getStmtComplexity(reinterpret_cast(condition)); + + if (complexity > this->max_condition_complexity) { + addViolation(node, this, toString(complexity) + " logical operators is too complex."); + } + + return true; + } +}; + +static RuleSet rules(new TooComplexConditionRule()); From fe600cf09c12d0b750cbcacb2657aced413baed3 Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Sat, 10 Feb 2024 16:42:28 +0300 Subject: [PATCH 09/10] update according to 'new' branch --- CMakeLists.txt | 3 +- README.md | 31 ++++--- build-oclint.sh | 6 +- do-everything.sh | 18 ++++ do-rules.sh | 16 ++++ examples/ex-goto/main.cpp | 14 +++ examples/ex-if/main.c | 81 +++++++++++++++++ examples/{ex4 => ex-makefile}/Makefile | 11 ++- .../include/obfuscation.h | 0 examples/{ex4 => ex-makefile}/include/stuff.h | 0 examples/{ex4 => ex-makefile}/src/main.c | 0 examples/{ex4 => ex-makefile}/src/stuff.c | 0 examples/ex-sizeof/main.c | 87 +++++++++++++++++++ examples/ex1/main.c | 41 --------- examples/ex2/main.c | 41 --------- examples/ex3/main.cpp | 42 --------- examples/ex5/main.c | 7 -- examples/ex6/main.c | 16 ---- examples/test-gcc.sh | 11 +++ examples/test-makefile.sh | 22 +++++ install-deps.sh | 2 + install-oclint.sh | 2 + tester.py | 85 ------------------ 23 files changed, 284 insertions(+), 252 deletions(-) create mode 100755 do-everything.sh create mode 100755 do-rules.sh create mode 100644 examples/ex-goto/main.cpp create mode 100644 examples/ex-if/main.c rename examples/{ex4 => ex-makefile}/Makefile (56%) rename examples/{ex4 => ex-makefile}/include/obfuscation.h (100%) rename examples/{ex4 => ex-makefile}/include/stuff.h (100%) rename examples/{ex4 => ex-makefile}/src/main.c (100%) rename examples/{ex4 => ex-makefile}/src/stuff.c (100%) create mode 100644 examples/ex-sizeof/main.c delete mode 100644 examples/ex1/main.c delete mode 100644 examples/ex2/main.c delete mode 100644 examples/ex3/main.cpp delete mode 100644 examples/ex5/main.c delete mode 100644 examples/ex6/main.c create mode 100755 examples/test-gcc.sh create mode 100755 examples/test-makefile.sh create mode 100755 install-deps.sh create mode 100755 install-oclint.sh delete mode 100644 tester.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 7789e39..1609fbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ LINK_DIRECTORIES( ${CMAKE_SOURCE_DIR}/oclint/build/oclint-metrics/lib ${CMAKE_SOURCE_DIR}/oclint/build/oclint-rules/lib/helper ${CMAKE_SOURCE_DIR}/oclint/build/oclint-rules/lib/util + ${CMAKE_SOURCE_DIR}/oclint/build/oclint-rules/lib ) MACRO(build_dynamic_rule name) @@ -144,4 +145,4 @@ MACRO(build_dynamic_rule name) ENDMACRO(build_dynamic_rule) BUILD_DYNAMIC_RULE(TooLongIfSequence) -BUILD_DYNAMIC_RULE(TooComplexCondition) \ No newline at end of file +BUILD_DYNAMIC_RULE(TooComplexCondition) diff --git a/README.md b/README.md index 75bceae..69022e6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ -# oclint_extensions — кастомные правила для oclint +# OCLint extensions -## Установка, сборка и использование +## Как собрать и установить? -Требования и зависимости: -* [Требования и зависимости oclint](https://oclint-docs.readthedocs.io/en/stable/intro/build.html) -* CMake — для сборки -* Python — для tester.py -* bear: `sudo apt install bear` — генератор compile_commands.json -* ... ?? +В одну строку: +``` +sudo ./install-deps.sh && ./build-oclint.sh && ./install-oclint.sh && mkdir build && cd build && cmake ../ && cmake --build . && sudo cmake --install . && cd .. +``` +То же самое, только в человекочитаемом виде: ```bash +sudo ./install-deps.sh + ./build-oclint.sh +./install-oclint.sh + mkdir build cd build cmake ../ @@ -18,10 +21,10 @@ cmake --build . sudo cmake --install . ``` -## Использование - -tester.py принимает в качестве параметра путь до работы студента (например, Ivanov_Ivan_cw/src), генерирует compile_commands.json и проверяет каждый файл с помощью oclint. +Больше информации в [Wiki](https://github.com/moevm/oclint_extensions/wiki/Сборка-и-установка) -``` -python3 tester.py examples/ex1 -``` \ No newline at end of file +Папка `examples` содержит примеры "проектов" с плохим кодом, а также скрипты `test-gcc.sh` и +`test-makefile.sh`. В качестве первого аргумента они принимают путь до папки с исходным кодом, +а последующие аргументы передаются в OCLint, позволяя изменять его поведение. Первый скрипт +предназначем для проектов, состоящих из одного файла с исходным кодом, а второй скрипт - для +проектов с использованием Makefile. diff --git a/build-oclint.sh b/build-oclint.sh index 9529ca8..c471773 100755 --- a/build-oclint.sh +++ b/build-oclint.sh @@ -1,11 +1,13 @@ #!/bin/bash +OCLINT_22_02_COMMIT=d776db51c8574df406b2b0dc1b43b0b9b2d86d34 + if [ ! -d oclint ]; then git clone https://github.com/oclint/oclint.git oclint cd oclint - git reset --hard d776db51c8574df406b2b0dc1b43b0b9b2d86d34 + git reset --hard $OCLINT_22_02_COMMIT cd .. fi cd oclint/oclint-scripts -./make \ No newline at end of file +./make diff --git a/do-everything.sh b/do-everything.sh new file mode 100755 index 0000000..74a1eb0 --- /dev/null +++ b/do-everything.sh @@ -0,0 +1,18 @@ +#!/usr/bin/bash + +OLD_PATH=$(pwd) +SCRIPT_PATH=$(dirname "$0") + +cd $SCRIPT_PATH + +sudo ./install-deps.sh +./build-oclint.sh +./install-oclint.sh +mkdir build +cd build +cmake ../ +cmake --build . +sudo cmake --install . +cd .. + +cd $OLD_PATH diff --git a/do-rules.sh b/do-rules.sh new file mode 100755 index 0000000..385ba7f --- /dev/null +++ b/do-rules.sh @@ -0,0 +1,16 @@ +#!/usr/bin/bash + +OLD_PATH=$(pwd) +SCRIPT_PATH=$(dirname "$0") + +cd $SCRIPT_PATH + +[[ -d build ]] || (mkdir build && cd build && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ../ && cd ..) + +cd build +cmake --build . +sudo cmake --install . +cp compile_commands.json ../ +cd .. + +cd $OLD_PATH diff --git a/examples/ex-goto/main.cpp b/examples/ex-goto/main.cpp new file mode 100644 index 0000000..51721a9 --- /dev/null +++ b/examples/ex-goto/main.cpp @@ -0,0 +1,14 @@ +#include +using namespace std; + +int main() +{ + int x; + cin >> x; + + if (x != 2) goto failed; + cout << "Congrats! You typed 2!" << endl; + +failed: + return 0; +} diff --git a/examples/ex-if/main.c b/examples/ex-if/main.c new file mode 100644 index 0000000..d92e44f --- /dev/null +++ b/examples/ex-if/main.c @@ -0,0 +1,81 @@ +#include + +void simple_ifs() +{ + int option = 0; + scanf("%d", &option); + + if (option == 0) { + puts("Zero"); + } + if (option == 1) { + puts("One"); + } + if (option == 2) { + puts("Two"); + } + if (option == 3) { + puts("Three"); + } + if (option == 4) { + puts("Four"); + } + if (option == 5) { + puts("Five"); + } + if (option == 6) { + puts("Six"); + } + if (option == 7) { + puts("Seven"); + } + if (option == 8) { + puts("Eight"); + } + if (option == 9) { + puts("Nine"); + } + else { + puts("Something else"); + } +} + +void complex_ifs() +{ + int x = 0; + int param1 = 0; + float param2 = 0.0; + float param3 = 0.0; + float param4 = 0.0; + int opt = 0; + int value = -1; + scanf("%d %d %f %f %f %d %d", &x, ¶m1, ¶m2, ¶m3, ¶m4, &opt, &value); + + if (x == 0) { + puts("Zero"); + } + else if (param1 > 100){ + puts("Too large"); + } + else if (param2 < 0.0){ + puts("Negative"); + } + else if( param3 * param2 > 1e+5){ + puts("Invalid"); + } + else if (param4 / 2 < param1){ + puts("Unique case"); + } + else if (opt < 0 || opt > 6){ + puts("Invalid option"); + } + else if (value == -1){ + puts("By default"); + } +} + +int main() +{ + simple_ifs(); + complex_ifs(); +} diff --git a/examples/ex4/Makefile b/examples/ex-makefile/Makefile similarity index 56% rename from examples/ex4/Makefile rename to examples/ex-makefile/Makefile index 6c27f65..040d802 100644 --- a/examples/ex4/Makefile +++ b/examples/ex-makefile/Makefile @@ -1,12 +1,17 @@ -.PHONY: all +.PHONY: all clean all: prog prog: stuff.o main.o - gcc stuff.o main.o + gcc -o prog stuff.o main.o stuff.o: src/stuff.c include/obfuscation.h include/stuff.h gcc -c -I ./include src/stuff.c main.o: src/main.c include/stuff.h - gcc -c -I ./include src/main.c \ No newline at end of file + gcc -c -I ./include src/main.c + +clean: + rm -f stuff.o + rm -f main.o + rm -f prog diff --git a/examples/ex4/include/obfuscation.h b/examples/ex-makefile/include/obfuscation.h similarity index 100% rename from examples/ex4/include/obfuscation.h rename to examples/ex-makefile/include/obfuscation.h diff --git a/examples/ex4/include/stuff.h b/examples/ex-makefile/include/stuff.h similarity index 100% rename from examples/ex4/include/stuff.h rename to examples/ex-makefile/include/stuff.h diff --git a/examples/ex4/src/main.c b/examples/ex-makefile/src/main.c similarity index 100% rename from examples/ex4/src/main.c rename to examples/ex-makefile/src/main.c diff --git a/examples/ex4/src/stuff.c b/examples/ex-makefile/src/stuff.c similarity index 100% rename from examples/ex4/src/stuff.c rename to examples/ex-makefile/src/stuff.c diff --git a/examples/ex-sizeof/main.c b/examples/ex-sizeof/main.c new file mode 100644 index 0000000..e65df9b --- /dev/null +++ b/examples/ex-sizeof/main.c @@ -0,0 +1,87 @@ +#include +#include + +int intcmp(const void* a, const void* b) { + int ia = *(const int*)a; + int ib = *(const int*)b; + return ia < ib; +} + +#define BLOCK_SIZE 8 + +int forgot_parens() +{ + size_t capacity = BLOCK_SIZE; + size_t size = 0; + + char *str = (char *)malloc(capacity * sizeof(char)); + while (1) { + char c = getchar(); + if (c == EOF) break; + + if (size == capacity) { + str = (char *)realloc(str, capacity + BLOCK_SIZE * sizeof(char)); + capacity += BLOCK_SIZE; + } + + str[size++] = c; + } +} + +int wrong_way() +{ + int len = 0; + int *arr = NULL; + + scanf("%d", &len); + arr = (int*)malloc(4 * len); // this is bad + + for (int i = 0; i < len; i++) { + scanf("%d", &arr[i]); + } + + qsort(arr, len, sizeof(int), intcmp); + + len /= 2; + arr = (int*)realloc(arr, len * 4); // this is bad + + for (int i = 0; i < len; i++) { + printf("%d", arr[i]); + } + putchar('\n'); + + free(arr); + return 0; +} + +int right_way() +{ + int len = 0; + int *arr = NULL; + + scanf("%d", &len); + arr = (int*)malloc(sizeof(int) * len); + + for (int i = 0; i < len; i++) { + scanf("%d", &arr[i]); + } + + qsort(arr, len, sizeof(int), intcmp); + + len /= 2; + arr = (int*)realloc(arr, len * sizeof(int)); + + for (int i = 0; i < len; i++) { + printf("%d", arr[i]); + } + putchar('\n'); + + free(arr); + return 0; +} + +int main() +{ + int result = right_way(); + return right_way(); +} \ No newline at end of file diff --git a/examples/ex1/main.c b/examples/ex1/main.c deleted file mode 100644 index 31d7535..0000000 --- a/examples/ex1/main.c +++ /dev/null @@ -1,41 +0,0 @@ -#include - -int main() -{ - int x = 0; - scanf("%d", &x); - - if (x == 0) { - puts("Zero"); - } - if (x == 1) { - puts("One"); - } - if (x == 2) { - puts("Two"); - } - if (x == 3) { - puts("Three"); - } - if (x == 4) { - puts("Four"); - } - if (x == 5) { - puts("Five"); - } - if (x == 6) { - puts("Six"); - } - if (x == 7) { - puts("Seven"); - } - if (x == 8) { - puts("Eight"); - } - if (x == 9) { - puts("Nine"); - } - else { - puts("Something else"); - } -} \ No newline at end of file diff --git a/examples/ex2/main.c b/examples/ex2/main.c deleted file mode 100644 index acb4c0f..0000000 --- a/examples/ex2/main.c +++ /dev/null @@ -1,41 +0,0 @@ -#include - -int main() -{ - int x = 0; - scanf("%d", &x); - - if (x == 0) { - puts("Zero"); - } - else if (x == 1) { - puts("One"); - } - else if (x == 2) { - puts("Two"); - } - else if (x == 3) { - puts("Three"); - } - else if (x == 4) { - puts("Four"); - } - else if (x == 5) { - puts("Five"); - } - else if (x == 6) { - puts("Six"); - } - else if (x == 7) { - puts("Seven"); - } - else if (x == 8) { - puts("Eight"); - } - else if (x == 9) { - puts("Nine"); - } - else { - puts("Something else"); - } -} \ No newline at end of file diff --git a/examples/ex3/main.cpp b/examples/ex3/main.cpp deleted file mode 100644 index c569cad..0000000 --- a/examples/ex3/main.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -using namespace std; - -int main() -{ - int x = 0; - cin >> x; - - if (x == 0) { - cout << "Zero"; - } - else if (x == 1) { - cout << "One"; - } - else if (x == 2) { - cout << "Two"; - } - else if (x == 3) { - cout << "Three"; - } - if (x == 4) { - cout << "Four"; - } - else if (x == 5) { - cout << "Five"; - } - if (x == 6) { - cout << "Six"; - } - if (x == 7) { - cout << "Seven"; - } - else if (x == 8) { - cout << "Eight"; - } - else if (x == 9) { - cout << "Nine"; - } - else { - cout << "Something else"; - } -} \ No newline at end of file diff --git a/examples/ex5/main.c b/examples/ex5/main.c deleted file mode 100644 index 56688be..0000000 --- a/examples/ex5/main.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int main() -{ - printf("OK!!!!\n"); - return 0; -} \ No newline at end of file diff --git a/examples/ex6/main.c b/examples/ex6/main.c deleted file mode 100644 index 021c6e6..0000000 --- a/examples/ex6/main.c +++ /dev/null @@ -1,16 +0,0 @@ -#include - -int main() -{ - int a, b, c, d, e, f; - scanf("%d %d %d %d %d %d", &a, &b, &c, &d, &e, &f); - - if (((a == b || b == c) && (d == e || e == f) || !(d == c)) && b != e) { - puts("Congrats! You found the secret code."); - } - else { - puts("Better luck next time."); - } - - return 0; -} \ No newline at end of file diff --git a/examples/test-gcc.sh b/examples/test-gcc.sh new file mode 100755 index 0000000..d6d96bf --- /dev/null +++ b/examples/test-gcc.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ $# -eq 0 ]; then + echo "specify directory" + exit 1 +fi + +WORK_DIRECTORY=$1 +SRC_FILE=$(find $WORK_DIRECTORY -name "*.c" && find $WORK_DIRECTORY -name "*.cpp") + +oclint $SRC_FILE ${@:2} -- diff --git a/examples/test-makefile.sh b/examples/test-makefile.sh new file mode 100755 index 0000000..7a253a8 --- /dev/null +++ b/examples/test-makefile.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ $# -eq 0 ]; then + echo "specify directory" + exit 1 +fi + +WORK_DIRECTORY=$1 +TMP_DIRECTORY=./tester-tmp + +mkdir $TMP_DIRECTORY +cp -r $WORK_DIRECTORY/* $TMP_DIRECTORY + +bear -- make -C $TMP_DIRECTORY > /dev/null + +oclint ${@:2} \ + $(find $TMP_DIRECTORY -name "*.c") \ + $(find $TMP_DIRECTORY -name "*.h") \ + $(find $TMP_DIRECTORY -name "*.cpp") + +rm -rf $TMP_DIRECTORY +rm -rf compile_commands.json diff --git a/install-deps.sh b/install-deps.sh new file mode 100755 index 0000000..8837907 --- /dev/null +++ b/install-deps.sh @@ -0,0 +1,2 @@ +#!/usr/bin/bash +apt install ninja-build cmake bear diff --git a/install-oclint.sh b/install-oclint.sh new file mode 100755 index 0000000..0979975 --- /dev/null +++ b/install-oclint.sh @@ -0,0 +1,2 @@ +cp -r oclint/build/oclint-release/lib/* /usr/local/lib/ +cp -r oclint/build/oclint-release/bin/* /usr/local/bin/ diff --git a/tester.py b/tester.py deleted file mode 100644 index e2d0fa0..0000000 --- a/tester.py +++ /dev/null @@ -1,85 +0,0 @@ -import sys -import json -import os -import shutil -from enum import Enum - -class CompilationType(Enum): - Invalid = 0 - SingleFile = 1 - Makefile = 2 - -def guess_compilation_type(): - files = [] - makefile_exists = False - - content = os.listdir() - for x in content: - if os.path.isdir(x): - continue - - filename, extension = os.path.splitext(x) - if extension in ['.c', '.cpp']: - files.append(x) - - if filename == 'Makefile' and extension == '': - makefile_exists = True - - if makefile_exists: - return (CompilationType.Makefile, files) - if len(files) == 1: - return (CompilationType.SingleFile, files) - return (CompilationType.Invalid, files) - -def main(): - if len(sys.argv) != 2: - print("Usage: python3 tester.py TARGET_DIRECTORY") - return - - path = sys.argv[1] - - if not os.path.exists(path): - print("Error: directory does not exist.") - return - - os.chdir(path) - - if os.path.exists('compile_commands.json'): - print('Error: "compile_commands.json" already exists.') - return - - if os.path.exists('.tester_tmp'): - print('Error: ".tester_tmp" already exists.') - return - - os.mkdir('.tester_tmp') - os.system('cp -rf * ./.tester_tmp/') - os.chdir('.tester_tmp') - - comp_type, files = guess_compilation_type() - match comp_type: - case CompilationType.Invalid: - print("Error: Invalid compilation type.") - return - case CompilationType.SingleFile: - cmds = [ { "directory": os.getcwd(), "arguments": ["gcc", files[0]], "file": files[0] } ] - with open('../compile_commands.json', 'w', encoding='utf-8') as f: - json.dump(cmds, f) - case CompilationType.Makefile: - os.system("bear -- make > /dev/null") - - cmds = json.load(open('compile_commands.json')) - files = [] - for x in cmds: - files.append(x['file']) - os.system("mv compile_commands.json ../") - - for file in files: - os.system(f'/bin/sh -c "oclint --rule=TooLongIfSequence "{file}" | grep etu"') - - os.chdir('..') - os.remove('compile_commands.json') - os.system("rm -rf .tester_tmp") - -if __name__ == '__main__': - main() \ No newline at end of file From e5d32be2d1695ddab38547b4c98daad4b312e4f0 Mon Sep 17 00:00:00 2001 From: Gorsky Kirill Date: Sat, 10 Feb 2024 16:43:14 +0300 Subject: [PATCH 10/10] update TooComplexCondition + example --- examples/ex-condition/main.c | 16 ++++ rules/TooComplexConditionRule.cpp | 154 +++++++++++++++++------------- 2 files changed, 101 insertions(+), 69 deletions(-) create mode 100644 examples/ex-condition/main.c diff --git a/examples/ex-condition/main.c b/examples/ex-condition/main.c new file mode 100644 index 0000000..ad1b81c --- /dev/null +++ b/examples/ex-condition/main.c @@ -0,0 +1,16 @@ +#include + +int main() +{ + int a, b, c, d, e, f; + scanf("%d %d %d %d %d %d", &a, &b, &c, &d, &e, &f); + + if ((((a*d == b || b == c*f) && (d == e || (e - a) == f)) || !(d/(e*e + 1) == c)) && b != e) { + puts("Congrats! You found the secret code."); + } + else { + puts("Better luck next time."); + } + + return 0; +} diff --git a/rules/TooComplexConditionRule.cpp b/rules/TooComplexConditionRule.cpp index ede8d36..2321637 100644 --- a/rules/TooComplexConditionRule.cpp +++ b/rules/TooComplexConditionRule.cpp @@ -1,92 +1,108 @@ #include "oclint/AbstractASTVisitorRule.h" #include "oclint/RuleConfiguration.h" #include "oclint/RuleSet.h" -#include "oclint/util/StdUtil.h" #include "clang/AST/Expr.h" +#include -using namespace std; -using namespace clang; -using namespace oclint; - -class TooComplexConditionRule : public AbstractASTVisitorRule -{ -private: - int max_condition_complexity; - size_t getStmtComplexity(Stmt *stmt) +namespace oclint { + class TooComplexConditionRule : public oclint::AbstractASTVisitorRule { - size_t result = 0; - - Stmt::StmtClass sclass = stmt->getStmtClass(); - if (sclass == Stmt::BinaryOperatorClass) { - BinaryOperator *binop = reinterpret_cast(stmt); - result += binop->isLogicalOp(); + private: + int max_condition_complexity; + size_t getStmtComplexity(clang::Stmt *stmt) + { + std::stack stack; + size_t result = 0; + + stack.push(stmt); + + while (!stack.empty()) { + clang::Stmt *cur = stack.top(); + stack.pop(); + + clang::Stmt::StmtClass sclass = cur->getStmtClass(); + if (sclass == clang::Stmt::BinaryOperatorClass) { + auto *binop = clang::dyn_cast(cur); + result += binop->isLogicalOp(); + } + + for (auto &child : cur->children()) { + stack.push(child); + } + } + + return result; } - for (Stmt::child_iterator child = stmt->child_begin(); child != stmt->child_end(); child++) { - result += getStmtComplexity(*child); + public: + virtual void setUp() override + { + this->max_condition_complexity = RuleConfiguration::intForKey("MAX_CONDITION_COMPLEXITY", 4); } - return result; - } + virtual void tearDown() override {} -public: - virtual void setUp() override - { - this->max_condition_complexity = RuleConfiguration::intForKey("MAX_CONDITION_COMPLEXITY", 4); - } + virtual const std::string name() const override + { + return "too complex condition"; + } - virtual void tearDown() override {} + virtual int priority() const override + { + return 2; + } - virtual const string name() const override - { - return "too complex condition"; - } + virtual const std::string category() const override + { + return "etu"; + } - virtual int priority() const override - { - return 2; - } + #ifdef DOCGEN + virtual const std::string since() const override + { + return "22.02 etu"; + } - virtual const string category() const override - { - return "etu"; - } + virtual const std::string description() const override + { + return "Too complex conditions are hard to read."; + } -#ifdef DOCGEN - virtual const std::string since() const override - { - return "22.02 etu"; - } + virtual const std::string example() const override + { + return R"rst( + .. code-block:: cpp - virtual const std::string description() const override - { - return "..."; - } + void example() + { + int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0; + scanf("%d %d %d %d %d %d", &a, &b, &c, &d, &e, &f); - virtual const std::string example() const override - { - return R"rst( -.. code-block:: cpp + if (((a == b || b == c) && (d == e || e == f) || !(d == c)) && b != e) { + puts("Congrats! You found the secret code."); + } + else { + puts("Better luck next time."); + } - void example() - { - // TODO: modify the example for this rule. - } - )rst"; - } -#endif + return 0; + } + )rst"; + } + #endif - bool VisitIfStmt(IfStmt *node) - { - Expr *condition = node->getCond(); - size_t complexity = getStmtComplexity(reinterpret_cast(condition)); + bool VisitIfStmt(clang::IfStmt *node) + { + clang::Expr *condition = node->getCond(); + size_t complexity = getStmtComplexity(clang::dyn_cast(condition)); - if (complexity > this->max_condition_complexity) { - addViolation(node, this, toString(complexity) + " logical operators is too complex."); - } + if (complexity > this->max_condition_complexity) { + addViolation(node, this, std::to_string(complexity) + " logical operators is too complex."); + } - return true; - } -}; + return true; + } + }; +} -static RuleSet rules(new TooComplexConditionRule()); +static oclint::RuleSet rules(new oclint::TooComplexConditionRule());