Skip to content

Commit

Permalink
new: TooLongIfSequenceRule. compare conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
jcdkiki committed Mar 19, 2024
1 parent c35c97c commit 55caf7e
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 45 deletions.
27 changes: 26 additions & 1 deletion examples/ex-if/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,33 @@ void complex_ifs()
}
}

int gcd(int a, int b);
int lcm(int a, int b);

void almost_complex_ifs()
{
int x, y;
scanf("%d %d", &x, &y);

// different function names
if (gcd(x, y) == 1) printf("GCD is 1\n");
else if (lcm(x, y) == 100) printf("LCM is 100\n");
else if (gcd(x, y) == 1337) printf("GCD is 1337\n");
else if (gcd(x, y) == 300) printf("GCD is 300\n");
if (lcm(x, y) == 512) printf("LCM is 512\n");
if (gcd(x, y) == 64) printf("GCD is 64\n");

// different binary operators
if (x > 100) printf("x is greater than 100\n");
else if (x < 0) printf("x is less than 0\n");
else if (x == 7) printf("x is 7\n");
else if (x == 9) printf("x is 9\n");
else if (x == 33) printf("x is 33\n");
else if (x == 6) printf("x is 6\n");
}

int main()
{
simple_ifs();
complex_ifs();
}
}
154 changes: 110 additions & 44 deletions rules/TooLongIfSequenceRule.cpp
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
#include "oclint/AbstractASTVisitorRule.h"
#include "oclint/RuleConfiguration.h"
#include "oclint/RuleSet.h"
#include "oclint/util/StdUtil.h"
#include <stack>

using namespace std;
using namespace clang;
using namespace oclint;

class TooLongIfSequenceRule : public AbstractASTVisitorRule<TooLongIfSequenceRule>
class TooLongIfSequenceRule : public oclint::AbstractASTVisitorRule<TooLongIfSequenceRule>
{
private:
int max_if_sequence_len;
size_t getIfComplexity(IfStmt *if_stmt)
{
Stmt *else_stmt = if_stmt->getElse();
clang::Stmt *first_if;
size_t cur_complexity;

if (else_stmt != nullptr && else_stmt->getStmtClass() == Stmt::IfStmtClass)
return 1 + getIfComplexity(reinterpret_cast<IfStmt*>(else_stmt));

return 1;
}
struct {
bool binary_operator : 1;
bool function_call : 1;
} special_check_flags;

public:
virtual void setUp() override
{
this->max_if_sequence_len = RuleConfiguration::intForKey("MAX_IF_SEQUENCE_LEN", 5);
this->max_if_sequence_len = oclint::RuleConfiguration::intForKey("MAX_IF_SEQUENCE_LEN", 5);
this->special_check_flags.binary_operator = oclint::RuleConfiguration::intForKey("CHECK_BINARY_OPERATOR", 1);
this->special_check_flags.function_call = oclint::RuleConfiguration::intForKey("CHECK_FUNCTION_CALL", 1);
}

virtual void tearDown() override {}

virtual const string name() const override
virtual const std::string name() const override
{
return "too long if sequence";
}
Expand All @@ -39,7 +35,7 @@ class TooLongIfSequenceRule : public AbstractASTVisitorRule<TooLongIfSequenceRul
return 2;
}

virtual const string category() const override
virtual const std::string category() const override
{
return "etu";
}
Expand All @@ -52,54 +48,124 @@ class TooLongIfSequenceRule : public AbstractASTVisitorRule<TooLongIfSequenceRul

virtual const std::string description() const override
{
return "...";
return "Such if sequences are difficult to analyze and may be rewritten more efficiently.";
}

virtual const std::string example() const override
{
return R"rst(
.. code-block:: cpp
return R"rst(See example in examples/ex-if)rst";
}
#endif

void example()
bool specialStatementCheck(clang::Stmt *first, clang::Stmt *second)
{
// TODO: modify the example for this rule.
}
)rst";
using clang::dyn_cast;

switch (first->getStmtClass()) {
case clang::Stmt::BinaryOperatorClass:
if (!this->special_check_flags.binary_operator) return true;
return dyn_cast<clang::BinaryOperator>(first)->getOpcode() == dyn_cast<clang::BinaryOperator>(second)->getOpcode();

case clang::Stmt::CallExprClass:
if (!this->special_check_flags.function_call) return true;
return dyn_cast<clang::CallExpr>(first)->getCalleeDecl() == dyn_cast<clang::CallExpr>(second)->getCalleeDecl();
default:
return true;
}
}
#endif

bool VisitCompoundStmt(CompoundStmt *node)
bool areStatementsSimilar(clang::Stmt *first, clang::Stmt *second)
{
Stmt *first_if = nullptr;
size_t cur_complexity = 0;
std::stack<clang::Stmt *> stack1;
std::stack<clang::Stmt *> stack2;
stack1.push(first);
stack2.push(second);

while (!stack1.empty() &&!stack2.empty())
{
clang::Stmt *stmt1 = stack1.top(); stack1.pop();
clang::Stmt *stmt2 = stack2.top(); stack2.pop();

if (stmt1->getStmtClass() != stmt2->getStmtClass())
return false;
if (!specialStatementCheck(stmt1, stmt2))
return false;

clang::Stmt::child_iterator it1 = stmt1->child_begin();
clang::Stmt::child_iterator it2 = stmt2->child_begin();
while (it1 != stmt1->child_end() && it2 != stmt2->child_end()) {
stack1.push(*it1);
stack2.push(*it2);
++it1;
++it2;
}

for (Stmt::child_iterator it = node->child_begin(); it != node->child_end(); it++) {
Stmt *stmt = (*it);
if (it1 != stmt1->child_end() || it2 != stmt2->child_end())
return false;
}

if (stmt->getStmtClass() == Stmt::IfStmtClass) {
if (first_if == nullptr)
first_if = stmt;
return true;
}

cur_complexity += getIfComplexity(dyn_cast<IfStmt>(stmt));
void endSequence()
{
if (first_if == nullptr)
return;

if (cur_complexity > this->max_if_sequence_len) {
addViolation(
this->first_if,
this,
std::to_string(this->cur_complexity) + " consecutive if statements is too many."
);
}

first_if = nullptr;
cur_complexity = 0;
}

void visitIfElseChain(clang::IfStmt *head)
{
clang::Stmt *chain = head;
while (true) {
if (first_if == nullptr)
first_if = chain;

if (chain->getStmtClass() != clang::Stmt::IfStmtClass) {
break;
}
else {
if (first_if == nullptr) continue;

if (cur_complexity > this->max_if_sequence_len) {
addViolation(first_if, this, toString<size_t>(cur_complexity) + " consecutive if statements is too many.");
}
clang::IfStmt *if_stmt = clang::dyn_cast<clang::IfStmt>(chain);
if (!areStatementsSimilar(clang::dyn_cast<clang::IfStmt>(first_if)->getCond(), if_stmt->getCond())) {
this->endSequence();
}

this->cur_complexity++;

first_if = nullptr;
cur_complexity = 0;
if (if_stmt->getElse() == nullptr) {
break;
}
chain = if_stmt->getElse();
}
}

bool VisitCompoundStmt(clang::CompoundStmt *node)
{
this->first_if = nullptr;
this->cur_complexity = 0;

if (first_if != nullptr && cur_complexity > this->max_if_sequence_len) {
addViolation(first_if, this, toString<size_t>(cur_complexity) + " consecutive if statements is too many.");
for (auto &stmt : node->children()) {
if (stmt->getStmtClass() == clang::Stmt::IfStmtClass) {
this->visitIfElseChain(clang::dyn_cast<clang::IfStmt>(stmt));
}
else {
this->endSequence();
}
}

this->endSequence();
return true;
}
};

static RuleSet rules(new TooLongIfSequenceRule());
static oclint::RuleSet rules(new TooLongIfSequenceRule());

0 comments on commit 55caf7e

Please sign in to comment.