Skip to content

Commit fb5e6d8

Browse files
committed
Fixed cppcheck-opensource#6960 (New check: enum variable is assigned mismatching value)
1 parent f0fcb85 commit fb5e6d8

3 files changed

Lines changed: 108 additions & 1 deletion

File tree

lib/checktype.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,71 @@ void CheckType::longCastReturnError(const Token *tok)
300300
"int result is returned as long value. If the return value is long to avoid loss of information, then you have loss of information.\n"
301301
"int result is returned as long value. If the return value is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.");
302302
}
303+
304+
static const ValueFlow::Value *mismatchingValue(const ValueType *enumType, const std::list<ValueFlow::Value> &values)
305+
{
306+
if (!enumType || !enumType->typeScope || enumType->typeScope->type != Scope::eEnum)
307+
return nullptr;
308+
const Scope * const enumScope = enumType->typeScope;
309+
for (std::list<ValueFlow::Value>::const_iterator it = values.begin(); it != values.end(); ++it) {
310+
if (it->tokvalue)
311+
continue;
312+
bool found = false;
313+
for (unsigned int i = 0; i < enumScope->enumeratorList.size(); ++i) {
314+
if (enumScope->enumeratorList[i].value_known && enumScope->enumeratorList[i].value == it->intvalue) {
315+
found = true;
316+
break;
317+
}
318+
}
319+
if (!found)
320+
return &(*it);
321+
}
322+
return nullptr;
323+
}
324+
325+
void CheckType::checkEnumMismatch()
326+
{
327+
if (!_settings->isEnabled("style"))
328+
return;
329+
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
330+
// Assigning mismatching value to enum variable
331+
if (tok->str() == "=") {
332+
if (!tok->astOperand1() || !tok->astOperand2())
333+
continue;
334+
335+
const ValueFlow::Value *v = mismatchingValue(tok->astOperand1()->valueType(), tok->astOperand2()->values);
336+
if (v)
337+
enumMismatchAssignError(tok, *v);
338+
}
339+
340+
// Comparing enum variable against mismatching value
341+
else if (tok->isComparisonOp()) {
342+
if (!tok->astOperand1() || !tok->astOperand2())
343+
continue;
344+
345+
const ValueFlow::Value * const v1 = mismatchingValue(tok->astOperand1()->valueType(), tok->astOperand2()->values);
346+
if (v1)
347+
enumMismatchCompareError(tok, *v1);
348+
349+
const ValueFlow::Value * const v2 = mismatchingValue(tok->astOperand2()->valueType(), tok->astOperand1()->values);
350+
if (v2)
351+
enumMismatchCompareError(tok, *v2);
352+
}
353+
}
354+
}
355+
356+
void CheckType::enumMismatchAssignError(const Token *tok, const ValueFlow::Value &value)
357+
{
358+
reportError(tok,
359+
Severity::style,
360+
"enumMismatch",
361+
"Assigning mismatching value " + MathLib::toString(value.intvalue) + " to enum variable.");
362+
}
363+
364+
void CheckType::enumMismatchCompareError(const Token *tok, const ValueFlow::Value &value)
365+
{
366+
reportError(tok,
367+
Severity::style,
368+
"enumMismatch",
369+
"Comparing mismatching value " + MathLib::toString(value.intvalue) + " with enum variable.");
370+
}

lib/checktype.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class CPPCHECKLIB CheckType : public Check {
5050
checkType.checkIntegerOverflow();
5151
checkType.checkSignConversion();
5252
checkType.checkLongCast();
53+
checkType.checkEnumMismatch();
5354
}
5455

5556
/** @brief Run checks against the simplified token list */
@@ -70,6 +71,9 @@ class CPPCHECKLIB CheckType : public Check {
7071

7172
/** @brief %Check for implicit long cast of int result */
7273
void checkLongCast();
74+
75+
/** @brief %Check for mismatching enum usage */
76+
void checkEnumMismatch();
7377
private:
7478

7579
// Error messages..
@@ -78,6 +82,8 @@ class CPPCHECKLIB CheckType : public Check {
7882
void signConversionError(const Token *tok, const bool constvalue);
7983
void longCastAssignError(const Token *tok);
8084
void longCastReturnError(const Token *tok);
85+
void enumMismatchAssignError(const Token *tok, const ValueFlow::Value &value);
86+
void enumMismatchCompareError(const Token *tok, const ValueFlow::Value &value);
8187

8288
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
8389
CheckType c(nullptr, settings, errorLogger);
@@ -86,6 +92,7 @@ class CPPCHECKLIB CheckType : public Check {
8692
c.signConversionError(nullptr, false);
8793
c.longCastAssignError(nullptr);
8894
c.longCastReturnError(nullptr);
95+
c.enumMismatchAssignError(nullptr, ValueFlow::Value(1000));
8996
}
9097

9198
static std::string myName() {
@@ -98,7 +105,8 @@ class CPPCHECKLIB CheckType : public Check {
98105
"- signed integer overflow (only enabled when --platform is used)\n"
99106
"- dangerous sign conversion, when signed value can be negative\n"
100107
"- possible loss of information when assigning int result to long variable\n"
101-
"- possible loss of information when returning int result as long return value\n";
108+
"- possible loss of information when returning int result as long return value\n"
109+
"- enum usage with mismatching values\n";
102110
}
103111
};
104112
/// @}

test/testtype.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class TestType : public TestFixture {
3939
TEST_CASE(signConversion);
4040
TEST_CASE(longCastAssign);
4141
TEST_CASE(longCastReturn);
42+
TEST_CASE(enumMismatchAssign);
43+
TEST_CASE(enumMismatchCompare);
4244
}
4345

4446
void check(const char code[], Settings* settings = 0) {
@@ -199,6 +201,35 @@ class TestType : public TestFixture {
199201
"}\n", &settings);
200202
ASSERT_EQUALS("", errout.str());
201203
}
204+
205+
void enumMismatchAssign() {
206+
Settings settings;
207+
settings.addEnabled("style");
208+
209+
check("enum ABC {A,B,C};\n"
210+
"void f() {\n"
211+
" enum ABC abc = 5;\n"
212+
"}", &settings);
213+
ASSERT_EQUALS("[test.cpp:3]: (style) Assigning mismatching value 5 to enum variable.\n", errout.str());
214+
}
215+
216+
void enumMismatchCompare() {
217+
Settings settings;
218+
settings.addEnabled("style");
219+
220+
check("enum ABC {A,B,C};\n"
221+
"void f(enum ABC abc) {\n"
222+
" if (abc==5) {}\n"
223+
"}", &settings);
224+
ASSERT_EQUALS("[test.cpp:3]: (style) Comparing mismatching value 5 with enum variable.\n", errout.str());
225+
226+
check("enum ABC {A,B,C};\n"
227+
"void f(enum ABC abc) {\n"
228+
" if (5==abc) {}\n"
229+
"}", &settings);
230+
ASSERT_EQUALS("[test.cpp:3]: (style) Comparing mismatching value 5 with enum variable.\n", errout.str());
231+
}
232+
202233
};
203234

204235
REGISTER_TEST(TestType)

0 commit comments

Comments
 (0)