diff --git a/lib/astutils.cpp b/lib/astutils.cpp index e2e19b6bd91..44ca89d02f5 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -2688,7 +2688,7 @@ bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, }; tok2 = skipRedundantPtrOp(tok2, tok2->astParent()); - if (tok2->astParent() && tok2->astParent()->isAssignmentOp()) { + if (tok2->astParent() && tok2->astParent()->isAssignmentOp() && (indirect == 0 || tok2->astParent()->str() == "=")) { if (astIsLHS(tok2)) return true; // Check if assigning to a non-const lvalue diff --git a/lib/vf_analyzers.cpp b/lib/vf_analyzers.cpp index ccadc3ef623..96503b86b08 100644 --- a/lib/vf_analyzers.cpp +++ b/lib/vf_analyzers.cpp @@ -248,7 +248,8 @@ struct ValueFlowAnalyzer : Analyzer { if (inconclusive) return Action::Read | Action::Inconclusive; if (isVariableChanged(tok, getIndirect(tok), getSettings())) { - if (Token::Match(tok->astParent(), "*|[|.|++|--")) + if (Token::Match(tok->astParent(), "*|[|.|++|--") || + (Token::Match(tok->astParent(), "%assign%") && tok->astParent()->str().size() > 1)) // compound assignment return Action::Read | Action::Invalid; // Check if its assigned to the same value if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) && diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 093dbe94dfc..3f688744e8b 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -4933,7 +4933,7 @@ class TestNullPointer : public TestFixture { " p += a;\n" " *p = 0;\n" "}\n"); - ASSERT_EQUALS("", errout_str()); + ASSERT_EQUALS("[test.cpp:2:7]: (error) Pointer addition with NULL pointer. [nullPointerArithmetic]\n", errout_str()); check("int f(int *p = 0) {\n" " if (p == 0) {\n" diff --git a/test/testother.cpp b/test/testother.cpp index d6025e9a640..453644da4ec 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -5087,6 +5087,17 @@ class TestOther : public TestFixture { "void g(__attribute__((unused)) int *p) {}\n" "void h(int *p) {}\n"); ASSERT_EQUALS("[test.cpp:3:13]: (style) Parameter 'p' can be declared as pointer to const [constParameterPointer]\n", errout_str()); + + check("void f(int *r, int s, int N) {\n" + " int *p = nullptr;\n" + " for (int j = 0; j < N; j++) {\n" + " if (p) {}\n" + " p = r;\n" + " r += s;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:10]: (style) Variable 'p' can be declared as pointer to const [constVariablePointer]\n", + errout_str()); } void constArray() { diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 6e742d31bcc..1f569be0804 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -6861,6 +6861,15 @@ class TestUninitVar : public TestFixture { " return a[0];\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + valueFlowUninit("void f(int i) {\n" + " int x, y;\n" + " x += i;\n" + " y <<= i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:5]: (error) Uninitialized variable: x [uninitvar]\n" + "[test.cpp:4:5]: (error) Uninitialized variable: y [uninitvar]\n", + errout_str()); } void valueFlowUninitBreak() { // Do not show duplicate warnings about the same uninitialized value diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 1e953af1535..2ff57a8a84c 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -1112,6 +1112,22 @@ class TestValueFlow : public TestFixture { " g(std::move(x)).f(1);\n" "}\n"; ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MoveKind::MovedVariable)); + + code = "void f(std::string x) {\n" // #15155 + " std::string a{ std::move(x) };\n" + " x += \"abc\";\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfX(code, 3U, ValueFlow::Value::MoveKind::MovedVariable)); + + code = "struct S {\n" + " S& operator<<=(int i);\n" + " std::string m;\n" + "};\n" + "void f(S x) {\n" + " S a{ std::move(x) };\n" + " x <<= 5;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 7U, ValueFlow::Value::MoveKind::MovedVariable)); } void valueFlowCalculations() {