Add check for shadow variables

This commit is contained in:
Daniel Marjamäki 2018-10-16 20:17:27 +02:00
parent f1074ea1ab
commit 1245a036f7
5 changed files with 84 additions and 7 deletions

View File

@ -45,9 +45,11 @@ script:
# fail the entire job as soon as one of the subcommands exits non-zero to save time and resources # fail the entire job as soon as one of the subcommands exits non-zero to save time and resources
- set -e - set -e
# download clang git, compile cppcheck, run cppcheck on clang code to look for crashes in cppcheck. if this is done, terminate build # download clang git, compile cppcheck, run cppcheck on clang code to look for crashes in cppcheck. if this is done, terminate build
- if [[ "$CHECK_CLANG" == "yes" ]] && [[ "$CC" == "clang" ]]; then wget "https://github.com/llvm-mirror/clang/archive/a1f8bd3778cc5a53236a53500c6ab184e945eefa.zip" & make -j 4 & wait; unzip a1f8bd3778cc5a53236a53500c6ab184e945eefa.zip > /dev/null; touch /tmp/clang.cppcheck; cd ./clang-a1f8bd3778cc5a53236a53500c6ab184e945eefa ; ${CPPCHECK} . --max-configs=1 --enable=all --inconclusive --exception-handling --template="{callstack} ({severity}) {message} [{id}]" -iINPUTS -itest/Driver/Inputs/gen-response.c -itest/Index/index-many-logical-ops.c -itest/Sema/many-logical-ops.c --suppressions-list=../.travis_llvmcheck_suppressions -j 2 |& grep -v ".* files checked.*done" |& tee /tmp/clang.cppcheck; cd ../ ; echo "CLANG" ; ! grep "process crashed with signal\|Internal error\. compiled" /tmp/clang.cppcheck; exit; fi # DM: I can't make it work. Let's rely on daca@home instead.
# - if [[ "$CHECK_CLANG" == "yes" ]] && [[ "$CC" == "clang" ]]; then wget "https://github.com/llvm-mirror/clang/archive/a1f8bd3778cc5a53236a53500c6ab184e945eefa.zip" & make -j 4 & wait; unzip a1f8bd3778cc5a53236a53500c6ab184e945eefa.zip > /dev/null; touch /tmp/clang.cppcheck; cd ./clang-a1f8bd3778cc5a53236a53500c6ab184e945eefa ; ${CPPCHECK} . --max-configs=1 --enable=all --inconclusive --exception-handling --template="{callstack} ({severity}) {message} [{id}]" -q -iINPUTS -itest/Driver/Inputs/gen-response.c -itest/Index/index-many-logical-ops.c -itest/Sema/many-logical-ops.c -j >& /tmp/clang.cppcheck ; cd ../ ; echo "CLANG" ; ! grep "process crashed with signal\|Internal error\. compiled" /tmp/clang.cppcheck; exit; fi
# check llvm as well # check llvm as well
- if [[ "$CHECK_LLVM" == "yes" ]] && [[ "$CC" == "clang" ]]; then wget "https://github.com/llvm-mirror/llvm/archive/6fa6453210fa34c1c28bd73a431d04734549f0d6.zip" & make -j 4 & wait; unzip 6fa6453210fa34c1c28bd73a431d04734549f0d6.zip > /dev/null; touch /tmp/llvm.cppcheck; cd ./llvm-6fa6453210fa34c1c28bd73a431d04734549f0d6 ; ${CPPCHECK} . --max-configs=1 --enable=all --inconclusive --exception-handling --template="{callstack} ({severity}) {message} [{id}]" --suppressions-list=../.travis_llvmcheck_suppressions -j 2 |& grep -v ".* files checked.*done" |& tee /tmp/llvm.cppcheck; cd ../ ; echo "LLVM" ; ! grep "process crashed with signal\|Internal error\. compiled" /tmp/llvm.cppcheck; exit; fi # DM: I can't make it work. Let's rely on daca@home instead.
# - if [[ "$CHECK_LLVM" == "yes" ]] && [[ "$CC" == "clang" ]]; then wget "https://github.com/llvm-mirror/llvm/archive/6fa6453210fa34c1c28bd73a431d04734549f0d6.zip" & make -j 4 & wait; unzip 6fa6453210fa34c1c28bd73a431d04734549f0d6.zip > /dev/null; touch /tmp/llvm.cppcheck; cd ./llvm-6fa6453210fa34c1c28bd73a431d04734549f0d6 ; ${CPPCHECK} . --max-configs=1 --enable=all --inconclusive --exception-handling --template="{callstack} ({severity}) {message} [{id}]" --suppressions-list=../.travis_llvmcheck_suppressions -j 2 -q >& /tmp/llvm.cppcheck; cd ../ ; echo "LLVM" ; ! grep "process crashed with signal\|Internal error\. compiled" /tmp/llvm.cppcheck; exit; fi
# check if dmake needs to be rerun but if yes, don't fail the build but notify us. # check if dmake needs to be rerun but if yes, don't fail the build but notify us.
# to update dmake: "make dmake; ./dmake; and commit # to update dmake: "make dmake; ./dmake; and commit
- echo "If the following command fails, run 'make dmake; make run-dmake' and commit the resulting change." - echo "If the following command fails, run 'make dmake; make run-dmake' and commit the resulting change."

View File

@ -6,6 +6,7 @@ nullPointer:build/checkother.cpp
missingOverride missingOverride
noValidConfiguration noValidConfiguration
useStlAlgorithm useStlAlgorithm
shadowLocal
*:gui/test/* *:gui/test/*
*:test/test.cxx *:test/test.cxx

View File

@ -2937,3 +2937,47 @@ void CheckOther::funcArgOrderDifferent(const std::string & functionName,
reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, false); reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, false);
} }
static const Token *findShadowed(const Scope *scope, const std::string &varname, int linenr)
{
if (!scope)
return nullptr;
for (const Variable &var : scope->varlist) {
if (scope->isExecutable() && var.nameToken()->linenr() > linenr)
continue;
if (var.name() == varname)
return var.nameToken();
}
for (const Function &f : scope->functionList) {
if (f.name() == varname)
return f.tokenDef;
}
return findShadowed(scope->nestedIn, varname, linenr);
}
void CheckOther::checkShadowVariables()
{
if (!mSettings->isEnabled(Settings::STYLE))
return;
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
for (const Scope & scope : symbolDatabase->scopeList) {
if (!scope.isExecutable())
continue;
for (const Variable &var : scope.varlist) {
const Token *shadowed = findShadowed(scope.nestedIn, var.name(), var.nameToken()->linenr());
if (!shadowed)
continue;
if (scope.type == Scope::eFunction && scope.className == var.name())
continue;
shadowVariablesError(var.nameToken(), shadowed);
}
}
}
void CheckOther::shadowVariablesError(const Token *var, const Token *shadowed)
{
ErrorPath errorPath;
errorPath.push_back(ErrorPathItem(shadowed, "Shadowed declaration"));
errorPath.push_back(ErrorPathItem(var, "Shadow variable"));
const std::string &varname = var ? var->str() : "var";
reportError(errorPath, Severity::style, "shadowLocal", "$symbol:" + varname + "\nLocal variable $symbol shadows outer symbol", CWE398, false);
}

View File

@ -81,6 +81,7 @@ public:
checkOther.checkUnusedLabel(); checkOther.checkUnusedLabel();
checkOther.checkEvaluationOrder(); checkOther.checkEvaluationOrder();
checkOther.checkFuncArgNamesDifferent(); checkOther.checkFuncArgNamesDifferent();
checkOther.checkShadowVariables();
} }
/** @brief Run checks against the simplified token list */ /** @brief Run checks against the simplified token list */
@ -211,6 +212,9 @@ public:
/** @brief %Check if function declaration and definition argument names different */ /** @brief %Check if function declaration and definition argument names different */
void checkFuncArgNamesDifferent(); void checkFuncArgNamesDifferent();
/** @brief %Check for shadow variables. Less noisy than gcc/clang -Wshadow. */
void checkShadowVariables();
private: private:
// Error messages.. // Error messages..
void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result); void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result);
@ -263,6 +267,7 @@ private:
void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive);
void funcArgNamesDifferent(const std::string & functionName, size_t index, const Token* declaration, const Token* definition); void funcArgNamesDifferent(const std::string & functionName, size_t index, const Token* declaration, const Token* definition);
void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector<const Token*> & declarations, const std::vector<const Token*> & definitions); void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector<const Token*> & declarations, const std::vector<const Token*> & definitions);
void shadowVariablesError(const Token *var, const Token *shadowed);
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override {
CheckOther c(nullptr, settings, errorLogger); CheckOther c(nullptr, settings, errorLogger);
@ -324,6 +329,7 @@ private:
c.accessMovedError(nullptr, "v", nullptr, false); c.accessMovedError(nullptr, "v", nullptr, false);
c.funcArgNamesDifferent("function", 1, nullptr, nullptr); c.funcArgNamesDifferent("function", 1, nullptr, nullptr);
c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); c.redundantBitwiseOperationInSwitchError(nullptr, "varname");
c.shadowVariablesError(nullptr, nullptr);
const std::vector<const Token *> nullvec; const std::vector<const Token *> nullvec;
c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec);
@ -385,7 +391,8 @@ private:
"- redundant pointer operation on pointer like &\\*some_ptr.\n" "- redundant pointer operation on pointer like &\\*some_ptr.\n"
"- find unused 'goto' labels.\n" "- find unused 'goto' labels.\n"
"- function declaration and definition argument names different.\n" "- function declaration and definition argument names different.\n"
"- function declaration and definition argument order different.\n"; "- function declaration and definition argument order different.\n"
"- shadow variable.\n";
} }
}; };
/// @} /// @}

View File

@ -214,6 +214,8 @@ private:
TEST_CASE(funcArgNamesDifferent); TEST_CASE(funcArgNamesDifferent);
TEST_CASE(funcArgOrderDifferent); TEST_CASE(funcArgOrderDifferent);
TEST_CASE(cpp11FunctionArgInit); // #7846 - "void foo(int declaration = {}) {" TEST_CASE(cpp11FunctionArgInit); // #7846 - "void foo(int declaration = {}) {"
TEST_CASE(shadowLocal);
} }
void check(const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool runSimpleChecks=true, Settings* settings = 0) { void check(const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool runSimpleChecks=true, Settings* settings = 0) {
@ -2990,11 +2992,11 @@ private:
// #6406 - designated initializer doing bogus self assignment // #6406 - designated initializer doing bogus self assignment
check("struct callbacks {\n" check("struct callbacks {\n"
" void (*something)(void);\n" " void (*s)(void);\n"
"};\n" "};\n"
"void something(void) {}\n" "void something(void) {}\n"
"void f() {\n" "void f() {\n"
" struct callbacks ops = { .something = ops.something };\n" " struct callbacks ops = { .s = ops.s };\n"
"}\n"); "}\n");
TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant assignment of 'something' to itself.\n", "", errout.str()); TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant assignment of 'something' to itself.\n", "", errout.str());
@ -4621,7 +4623,7 @@ private:
check("template <typename T>\n" check("template <typename T>\n"
"T f() {\n" "T f() {\n"
" T a = T();\n" " T x = T();\n"
"}\n" "}\n"
"int &a = f<int&>();\n"); "int &a = f<int&>();\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
@ -5340,7 +5342,7 @@ private:
" const int a = getA + 3;\n" " const int a = getA + 3;\n"
" return 0;\n" " return 0;\n"
"}"); "}");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Local variable getA shadows outer symbol\n", errout.str());
check("class A{public:A(){}};\n" check("class A{public:A(){}};\n"
"const A& getA(){static A a;return a;}\n" "const A& getA(){static A a;return a;}\n"
@ -7384,6 +7386,27 @@ private:
)); ));
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void shadowLocal() {
check("int x;\n"
"void f() { int x; }\n");
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable x shadows outer symbol\n", errout.str());
check("int x();\n"
"void f() { int x; }\n");
ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable x shadows outer symbol\n", errout.str());
check("void f() {\n"
" if (cond) {int x;}\n" // <- not a shadow variable
" int x;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("int size() {\n"
" int size;\n" // <- not a shadow variable
"}\n");
ASSERT_EQUALS("", errout.str());
}
}; };
REGISTER_TEST(TestOther) REGISTER_TEST(TestOther)