Skip to content

Commit 6366a57

Browse files
authored
feat(unused_var): analyze global variables inside function body (#2944)
1 parent 626dcd0 commit 6366a57

2 files changed

Lines changed: 146 additions & 8 deletions

File tree

lib/checkunusedvar.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,17 +1609,33 @@ bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const To
16091609
}
16101610

16111611
bool sideEffectReturnFound = false;
1612+
std::set<const Variable*> pointersToGlobals;
16121613
for (Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd;
1613-
bodyToken = bodyToken->next()) {
1614+
bodyToken = bodyToken->next())
1615+
{
1616+
// check variable inside function body
16141617
const Variable* bodyVariable = bodyToken->variable();
16151618
if (bodyVariable) {
1616-
// check variable for side-effects
16171619
if (!isVariableWithoutSideEffects(*bodyVariable)) {
16181620
return false;
16191621
}
16201622
// check if global variable is changed
1621-
if (bodyVariable->isGlobal()) {
1622-
return false; // TODO: analyze global variable usage
1623+
if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end()) ) {
1624+
if (bodyVariable->isPointer() || bodyVariable->isArray()) {
1625+
return false; // TODO: Update astutils.cpp:1544 isVariableChanged() and remove this. Unhandled case: `*(global_arr + 1) = new_val`
1626+
}
1627+
const int depth = 20;
1628+
if (isVariableChanged(bodyToken, depth, mSettings, mTokenizer->isCPP())) {
1629+
return false;
1630+
}
1631+
// check if pointer to global variable assigned to another variable (another_var = &global_var)
1632+
if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) {
1633+
const Token* assigned_var_token = bodyToken->tokAt(-3);
1634+
if (assigned_var_token && assigned_var_token->variable())
1635+
{
1636+
pointersToGlobals.insert(assigned_var_token->variable());
1637+
}
1638+
}
16231639
}
16241640
}
16251641

test/testunusedvar.cpp

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ class TestUnusedVar : public TestFixture {
615615
"void f() {\n"
616616
" C c;\n"
617617
"}");
618-
TODO_ASSERT_EQUALS("[test.cpp:11]: (style) Unused variable: c\n", "", errout.str());
618+
ASSERT_EQUALS("[test.cpp:11]: (style) Unused variable: c\n", errout.str());
619619

620620
// changing global variable in return
621621
functionVariableUsage(
@@ -698,6 +698,23 @@ class TestUnusedVar : public TestFixture {
698698
"}");
699699
ASSERT_EQUALS("", errout.str());
700700

701+
// global variable use in function body without change
702+
functionVariableUsage(
703+
"int global = 1;\n"
704+
"int func() {\n"
705+
" int x = global + 1;\n"
706+
" return x;\n"
707+
"}\n"
708+
"class C {\n"
709+
"public:\n"
710+
" C() : x(func()) {}\n"
711+
" int x;\n"
712+
"};\n"
713+
"void f() {\n"
714+
" C c;\n"
715+
"}");
716+
ASSERT_EQUALS("[test.cpp:12]: (style) Unused variable: c\n", errout.str());
717+
701718
// changing global array variable in function body
702719
functionVariableUsage(
703720
"int x[] = {0, 1, 3};\n"
@@ -715,6 +732,38 @@ class TestUnusedVar : public TestFixture {
715732
"}");
716733
ASSERT_EQUALS("", errout.str());
717734

735+
functionVariableUsage(
736+
"int x[] = {0, 1, 3};\n"
737+
"int func() {\n"
738+
" *x = 2;\n"
739+
" return 1;\n"
740+
"}\n"
741+
"class C {\n"
742+
"public:\n"
743+
" C() : x(func()) {}\n"
744+
" int x;\n"
745+
"};\n"
746+
"void f() {\n"
747+
" C c;\n"
748+
"}");
749+
ASSERT_EQUALS("", errout.str());
750+
751+
functionVariableUsage(
752+
"int x[] = {0, 1, 3};\n"
753+
"int func() {\n"
754+
" *(x + 1) = 2;\n"
755+
" return 1;\n"
756+
"}\n"
757+
"class C {\n"
758+
"public:\n"
759+
" C() : x(func()) {}\n"
760+
" int x;\n"
761+
"};\n"
762+
"void f() {\n"
763+
" C c;\n"
764+
"}");
765+
ASSERT_EQUALS("", errout.str());
766+
718767
// changing local variable
719768
functionVariableUsage(
720769
"int func() {\n"
@@ -985,7 +1034,7 @@ class TestUnusedVar : public TestFixture {
9851034
functionVariableUsage(
9861035
"int global = 1;\n"
9871036
"int func() {\n"
988-
" int *p = &global;\n"
1037+
" int* p = &global;\n"
9891038
" *p = 0;\n"
9901039
" return 1;\n"
9911040
"}\n"
@@ -999,11 +1048,65 @@ class TestUnusedVar : public TestFixture {
9991048
"}");
10001049
ASSERT_EQUALS("", errout.str());
10011050

1002-
// global struct variable
1051+
// global variable assigning to local pointer, but not modifying
1052+
functionVariableUsage(
1053+
"int global = 1;\n"
1054+
"int func() {\n"
1055+
" int* p = &global;\n"
1056+
" (void) p;\n"
1057+
" return 1;\n"
1058+
"}\n"
1059+
"class C {\n"
1060+
"public:\n"
1061+
" C() : x(func()) {}\n"
1062+
" int x;\n"
1063+
"};\n"
1064+
"void f() {\n"
1065+
" C c;\n"
1066+
"}");
1067+
// TODO: see TODO for global vars under CheckUnusedVar::isFunctionWithoutSideEffects()
1068+
TODO_ASSERT_EQUALS("[test.cpp:13]: (style) Unused variable: c\n", "", errout.str());
1069+
1070+
// global struct variable modification
1071+
functionVariableUsage(
1072+
"struct S { int x; } s;\n"
1073+
"int func() {\n"
1074+
" s.x = 1;\n"
1075+
" return 1;\n"
1076+
"}\n"
1077+
"class C {\n"
1078+
"public:\n"
1079+
" C() : x(func()) {}\n"
1080+
" int x;\n"
1081+
"};\n"
1082+
"void f() {\n"
1083+
" C c;\n"
1084+
"}");
1085+
ASSERT_EQUALS("", errout.str());
1086+
1087+
// global struct variable without modification
10031088
functionVariableUsage(
10041089
"struct S { int x; } s;\n"
10051090
"int func() {\n"
1006-
" s.x = 1;;\n"
1091+
" int y = s.x + 1;\n"
1092+
" return y;\n"
1093+
"}\n"
1094+
"class C {\n"
1095+
"public:\n"
1096+
" C() : x(func()) {}\n"
1097+
" int x;\n"
1098+
"};\n"
1099+
"void f() {\n"
1100+
" C c;\n"
1101+
"}");
1102+
ASSERT_EQUALS("[test.cpp:12]: (style) Unused variable: c\n", errout.str());
1103+
1104+
// global pointer to struct variable modification
1105+
functionVariableUsage(
1106+
"struct S { int x; };\n"
1107+
"struct S* s = new(struct S);\n"
1108+
"int func() {\n"
1109+
" s->x = 1;\n"
10071110
" return 1;\n"
10081111
"}\n"
10091112
"class C {\n"
@@ -1015,6 +1118,25 @@ class TestUnusedVar : public TestFixture {
10151118
" C c;\n"
10161119
"}");
10171120
ASSERT_EQUALS("", errout.str());
1121+
1122+
// global pointer to struct variable without modification
1123+
functionVariableUsage(
1124+
"struct S { int x; };\n"
1125+
"struct S* s = new(struct S);\n"
1126+
"int func() {\n"
1127+
" int y = s->x + 1;\n"
1128+
" return y;\n"
1129+
"}\n"
1130+
"class C {\n"
1131+
"public:\n"
1132+
" C() : x(func()) {}\n"
1133+
" int x;\n"
1134+
"};\n"
1135+
"void f() {\n"
1136+
" C c;\n"
1137+
"}");
1138+
// TODO: see TODO for global vars under CheckUnusedVar::isFunctionWithoutSideEffects()
1139+
TODO_ASSERT_EQUALS("[test.cpp:13]: (style) Unused variable: c\n", "", errout.str());
10181140
}
10191141

10201142
// #5355 - False positive: Variable is not assigned a value.

0 commit comments

Comments
 (0)