Add check for shadow variables
This commit is contained in:
parent
f1074ea1ab
commit
1245a036f7
|
@ -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."
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue