[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/cppcheck-opensource/cppcheck/main/test/testcondition.cpp [Back]  [Original]

/*
 * Cppcheck - A tool for static C/C++ code analysis
 * Copyright (C) 2007-2026 Cppcheck team.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

#include "checkcondition.h"
#include "errortypes.h"
#include "fixture.h"
#include "helpers.h"
#include "platform.h"
#include "settings.h"

#include 
#include 

class TestCondition : public TestFixture {
public:
    TestCondition() : TestFixture("TestCondition") {}

private:
    const Settings settings0 = settingsBuilder().library("qt.cfg").library("std.cfg").severity(Severity::style).severity(Severity::warning).build();
    /*const*/ Settings settings1 = settingsBuilder().severity(Severity::style).severity(Severity::warning).build();
    const Settings settings2 = settingsBuilder(settings0).severity(Severity::performance).certainty(Certainty::inconclusive).build();

    void run() override {
        const char cfg[] = "\n"
                           "\n"
                           "    \n"
                           "";
        settings1 = settingsBuilder(settings1).libraryxml(cfg).build();

        mNewTemplate = true;
        TEST_CASE(assignAndCompare);   // assignment and comparison don't match
        TEST_CASE(mismatchingBitAnd);  // overlapping bitmasks
        TEST_CASE(comparison);         // CheckCondition::comparison test cases
        TEST_CASE(multicompare);       // mismatching comparisons
        TEST_CASE(overlappingElseIfCondition);  // overlapping conditions in if and else-if
        TEST_CASE(oppositeElseIfCondition); // opposite conditions in if and else-if

        TEST_CASE(checkBadBitmaskCheck);

        TEST_CASE(incorrectLogicOperator1);
        TEST_CASE(incorrectLogicOperator2);
        TEST_CASE(incorrectLogicOperator3);
        TEST_CASE(incorrectLogicOperator4);
        TEST_CASE(incorrectLogicOperator5); // complex expressions
        TEST_CASE(incorrectLogicOperator6); // char literals
        TEST_CASE(incorrectLogicOperator7); // opposite expressions: (expr || !expr)
        TEST_CASE(incorrectLogicOperator8); // !
        TEST_CASE(incorrectLogicOperator9);
        TEST_CASE(incorrectLogicOperator10); // enum
        TEST_CASE(incorrectLogicOperator11);
        TEST_CASE(incorrectLogicOperator12);
        TEST_CASE(incorrectLogicOperator13);
        TEST_CASE(incorrectLogicOperator14);
        TEST_CASE(incorrectLogicOperator15);
        TEST_CASE(incorrectLogicOperator16); // #10070
        TEST_CASE(incorrectLogicOperator17);
        TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError);
        TEST_CASE(incorrectLogicOp_condSwapping);
        TEST_CASE(testBug5895);
        TEST_CASE(testBug5309);

        TEST_CASE(modulo);

        TEST_CASE(oppositeInnerCondition);
        TEST_CASE(oppositeInnerConditionPointers);
        TEST_CASE(oppositeInnerConditionClass);
        TEST_CASE(oppositeInnerConditionUndeclaredVariable);
        TEST_CASE(oppositeInnerConditionAlias);
        TEST_CASE(oppositeInnerCondition2);
        TEST_CASE(oppositeInnerCondition3);
        TEST_CASE(oppositeInnerConditionAnd);
        TEST_CASE(oppositeInnerConditionOr);
        TEST_CASE(oppositeInnerConditionEmpty);
        TEST_CASE(oppositeInnerConditionFollowVar);
        TEST_CASE(oppositeInnerConditionLambda);

        TEST_CASE(identicalInnerCondition);

        TEST_CASE(identicalConditionAfterEarlyExit);
        TEST_CASE(innerConditionModified);

        TEST_CASE(overlappingInnerCondition);

        TEST_CASE(clarifyCondition1);     // if (a = b() < 0)
        TEST_CASE(clarifyCondition2);     // if (a & b == c)
        TEST_CASE(clarifyCondition3);     // if (! a & b)
        TEST_CASE(clarifyCondition4);     // ticket #3110
        TEST_CASE(clarifyCondition5);     // #3609 CWinTraits..
        TEST_CASE(clarifyCondition6);     // #3818
        TEST_CASE(clarifyCondition7);
        TEST_CASE(clarifyCondition8);

        TEST_CASE(alwaysTrue);
        TEST_CASE(alwaysTrueSymbolic);
        TEST_CASE(alwaysTrueInfer);
        TEST_CASE(alwaysTrueContainer);
        TEST_CASE(alwaysTrueLoop);
        TEST_CASE(alwaysTrueTryCatch);
        TEST_CASE(alwaysTrueSideEffect);
        TEST_CASE(alwaysTruePremiumMisra);
        TEST_CASE(multiConditionAlwaysTrue);
        TEST_CASE(duplicateCondition);

        TEST_CASE(checkInvalidTestForOverflow);
        TEST_CASE(checkConditionIsAlwaysTrueOrFalseInsideIfWhile);
        TEST_CASE(alwaysTrueFalseInLogicalOperators);
        TEST_CASE(pointerAdditionResultNotNull);
        TEST_CASE(duplicateConditionalAssign);

        TEST_CASE(checkAssignmentInCondition);
        TEST_CASE(compareOutOfTypeRange);
        TEST_CASE(knownConditionCast); // #9976
        TEST_CASE(knownConditionIncrementLoop); // #9808
        TEST_CASE(knownConditionAfterBailout); // #12526
        TEST_CASE(knownConditionIncDecOperator);
        TEST_CASE(knownConditionFloating);
    }

    struct CheckOptions
    {
        bool cpp = true;
    };

#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__)
    template
    void check_(const char* file, int line, const char (&code)[size], const CheckOptions& options = make_default_obj()) {
        check_(file, line, code, settings0, options.cpp);
    }

    template
    void check_(const char* file, int line, const char (&code)[size], const Settings& settings, bool cpp = true) {
        SimpleTokenizer2 tokenizer(settings, *this, code, cpp ? "test.cpp" : "test.c");

        // Tokenizer..
        ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line);

        CheckCondition check;
        runChecks(check, tokenizer, *this);
    }

#define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__)
    template
    void checkP_(const char* file, int line, const char (&code)[size])
    {
        SimpleTokenizer2 tokenizer(settings2, *this, code, "test.cpp");

        // Tokenizer..
        ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line);

        CheckCondition check;
        runChecks(check, tokenizer, *this);
    }

    void assignAndCompare() {
        // &
        check("void foo(int x)\n"
              "{\n"
              "    int y = x & 4;\n"
              "    if (y == 3);\n"
              "}");
        ASSERT_EQUALS("[test.cpp:3:11] -> [test.cpp:4:8]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false. [assignIfError]\n", errout_str());

        check("void foo(int x)\n"
              "{\n"
              "    int y = x & 4;\n"
              "    if (y != 3);\n"
              "}");
        ASSERT_EQUALS("[test.cpp:3:11] -> [test.cpp:4:8]: (style) Mismatching assignment and comparison, comparison 'y!=3' is always true. [assignIfError]\n", errout_str());

        // |
        check("void foo(int x) {\n"
              "    int y = x | 0x14;\n"
              "    if (y == 0x710);\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:8]: (style) Mismatching assignment and comparison, comparison 'y==0x710' is always false. [assignIfError]\n", errout_str());

        check("void foo(int x) {\n"
              "    int y = x | 0x14;\n"
              "    if (y == 0x71f);\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // various simple assignments
        check("void foo(int x) {\n"
              "    int y = (x+1) | 1;\n"
              "    if (y == 2);\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:8]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false. [assignIfError]\n", errout_str());

        check("void foo() {\n"
              "    int y = 1 | x();\n"
              "    if (y == 2);\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:8]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false. [assignIfError]\n", errout_str());

        // multiple conditions
        check("void foo(int x) {\n"
              "    int y = x & 4;\n"
              "    if ((y == 3) && (z == 1));\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:9]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false. [assignIfError]\n", errout_str());

        check("void foo(int x) {\n"
              "    int y = x & 4;\n"
              "    if ((x==123) || ((y == 3) && (z == 1)));\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:22]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false. [assignIfError]\n", errout_str());

        check("void f(int x) {\n"
              "    int y = x & 7;\n"
              "    if (setvalue(&y) && y != 8);\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // recursive checking into scopes
        check("void f(int x) {\n"
              "    int y = x & 7;\n"
              "    if (z) y=0;\n"
              "    else { if (y==8); }\n" // always false
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:4:15]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false. [assignIfError]\n", errout_str());

        // while
        check("void f(int x) {\n"
              "    int y = x & 7;\n"
              "    while (y==8);\n" // local variable => always false
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:11]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false. [assignIfError]\n", errout_str());

        check("void f(int x) {\n"
              "    extern int y; y = x & 7;\n"
              "    while (y==8);\n" // non-local variable => no error
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(int x) {\n"
              "    int a = 100;\n"
              "    while (x) {\n"
              "        int y = 16 | a;\n"
              "        while (y != 0) y--;\n"
              "    }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void g(int x);\n"
              "void f(int x) {\n"
              "    int a = 100;\n"
              "    while (x) {\n"
              "        int y = 16 | a;\n"
              "        while (y != 0) g(y);\n"
              "    }\n"
              "}");
        ASSERT_EQUALS(
            "[test.cpp:5:15] -> [test.cpp:6:15]: (style) Mismatching assignment and comparison, comparison 'y!=0' is always true. [assignIfError]\n",
            errout_str());

        check("void g(int &x);\n"
              "void f(int x) {\n"
              "    int a = 100;\n"
              "    while (x) {\n"
              "        int y = 16 | a;\n"
              "        while (y != 0) g(y);\n"
              "    }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // calling function
        check("void f(int x) {\n"
              "    int y = x & 7;\n"
              "    do_something();\n"
              "    if (y==8);\n" // local variable => always false
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:4:8]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false. [assignIfError]\n", errout_str());

        check("void f(int x) {\n"
              "    int y = x & 7;\n"
              "    do_something(&y);\n" // passing variable => no error
              "    if (y==8);\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void do_something(int);\n"
              "void f(int x) {\n"
              "    int y = x & 7;\n"
              "    do_something(y);\n"
              "    if (y==8);\n"
              "}");
        ASSERT_EQUALS("[test.cpp:3:11] -> [test.cpp:5:8]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false. [assignIfError]\n", errout_str());

        check("void f(int x) {\n"
              "    extern int y; y = x & 7;\n"
              "    do_something();\n"
              "    if (y==8);\n" // non-local variable => no error
              "}");
        ASSERT_EQUALS("", errout_str());

        // #4434 : false positive: ?:
        check("void f(int x) {\n"
              "    x = x & 1;\n"
              "    x = x & 1 ? 1 : -1;\n"
              "    if(x != -1) { }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // #4735
        check("void f() {\n"
              "    int x = *(char*)&0x12345678;\n"
              "    if (x==18) { }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // bailout: no variable info
        check("void foo(int x) {\n"
              "    y = 2 | x;\n"  // y not declared => no error
              "    if(y == 1) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // bailout: negative number
        check("void foo(int x) {\n"
              "    int y = -2 | x;\n" // negative number => no error
              "    if (y==1) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // bailout: pass variable to function
        check("void foo(int x) {\n"
              "    int y = 2 | x;\n"
              "    bar(&y);\n"  // pass variable to function => no error
              "    if (y==1) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // no crash on unary operator& (#5643)
        // #11610
        check("SdrObject* ApplyGraphicToObject() {\n"
              "    if (&rHitObject) {}\n"
              "    else if (rHitObject.IsClosedObj() && !&rHitObject) { }\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9]: (style) Condition '&rHitObject' is always true [knownConditionTrueFalse]\n"
                      "[test.cpp:3:42]: (style) Condition '!&rHitObject' is always false [knownConditionTrueFalse]\n",
                      errout_str());

        // #5695: increment
        check("void f(int a0, int n) {\n"
              "  int c = a0 & 3;\n"
              "  for (int a = 0; a < n; a++) {\n"
              "    c++;\n"
              "    if (c == 4)\n"
              "      c  = 0;\n"
              "  }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(int a) {\n" // #6662
              "  int x = a & 1;\n"
              "  while (x  [test.cpp:4:8]: (style) Mismatching assignment and comparison, comparison 'x!=5' is always true. [assignIfError]\n", errout_str());

        check("void f(int a) {\n" // #6662
              "  int x = a & 1;\n"
              "  while ((x += 4) < 10) {\n"
              "    if (x != 5) {}\n"
              "  }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f() {\n"
              "    int x = 100;\n"
              "    while (x) {\n"
              "        g(x);\n"
              "    }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void g(int x);\n"
              "void f() {\n"
              "    int x = 100;\n"
              "    while (x) {\n"
              "        g(x);\n"
              "    }\n"
              "}");
        ASSERT_EQUALS("[test.cpp:4:12]: (style) Condition 'x' is always true [knownConditionTrueFalse]\n", errout_str());

        check("void g(int & x);\n"
              "void f() {\n"
              "    int x = 100;\n"
              "    while (x) {\n"
              "        g(x);\n"
              "    }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

    }

    void mismatchingBitAnd() {
        check("void f(int a) {\n"
              "    int b = a & 0xf0;\n"
              "    b &= 1;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:5]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0). [mismatchingBitAnd]\n", errout_str());

        check("void f(int a) {\n"
              "    int b = a & 0xf0;\n"
              "    int c = b & 1;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11] -> [test.cpp:3:9]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0). [mismatchingBitAnd]\n", errout_str());

        check("void f(int a) {\n"
              "    int b = a;"
              "    switch (x) {\n"
              "    case 1: b &= 1; break;\n"
              "    case 2: b &= 2; break;\n"
              "    };\n"
              "}");
        ASSERT_EQUALS("", errout_str());
    }

    void comparison() {
        // CheckCondition::comparison test cases
        // '=='
        check("void f(int a) {\n assert( (a & 0x07) == 8U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X & 0x7) == 0x8' is always false. [comparisonError]\n",errout_str());
        check("void f(int a) {\n assert( (a & b & 4 & c ) == 3 );\n}");
        ASSERT_EQUALS("[test.cpp:2:21]: (style) Expression '(X & 0x4) == 0x3' is always false. [comparisonError]\n", errout_str());
        check("void f(int a) {\n assert( (a | 0x07) == 8U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X | 0x7) == 0x8' is always false. [comparisonError]\n",errout_str());
        check("void f(int a) {\n assert( (a & 0x07) == 7U );\n}");
        ASSERT_EQUALS("", errout_str());
        check("void f(int a) {\n assert( (a | 0x01) == -15 );\n}");
        ASSERT_EQUALS("", errout_str());
        // '!='
        check("void f(int a) {\n assert( (a & 0x07) != 8U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X & 0x7) != 0x8' is always true. [comparisonError]\n",errout_str());
        check("void f(int a) {\n assert( (a | 0x07) != 8U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X | 0x7) != 0x8' is always true. [comparisonError]\n",errout_str());
        check("void f(int a) {\n assert( (a & 0x07) != 7U );\n}");
        ASSERT_EQUALS("", errout_str());
        check("void f(int a) {\n assert( (a | 0x07) != 7U );\n}");
        ASSERT_EQUALS("", errout_str());
        // '>='
        check("void f(int a) {\n assert( (a & 0x07) >= 8U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X & 0x7) >= 0x8' is always false. [comparisonError]\n",errout_str());
        check("void f(unsigned int a) {\n assert( (a | 0x7) >= 7U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X | 0x7) >= 0x7' is always true. [comparisonError]\n",errout_str());
        check("void f(int a) {\n assert( (a & 0x07) >= 7U );\n}");
        ASSERT_EQUALS("",errout_str());
        check("void f(int a) {\n assert( (a | 0x07) >= 8U );\n}");
        ASSERT_EQUALS("",errout_str()); //correct for negative 'a'
        // '>'
        check("void f(int a) {\n assert( (a & 0x07) > 7U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X & 0x7) > 0x7' is always false. [comparisonError]\n",errout_str());
        check("void f(unsigned int a) {\n assert( (a | 0x7) > 6U );\n}");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Expression '(X | 0x7) > 0x6' is always true. [comparisonError]\n",errout_str());
        check("void f(int a) {\n assert( (a & 0x07) > 6U );\n}");
        ASSERT_EQUALS("",errout_str());
        check("void f(int a) {\n assert( (a | 0x07) > 7U );\n}");
        ASSERT_EQUALS("",errout_str()); //correct for negative 'a'
        // ' 5' is redundant since 'x == 6' is sufficient. [redundantCondition]\n", errout_str());

        // #3419
        check("void f() {\n"
              "    if ( &q != &a && &q != &b ) { }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // #3676
        check("void f(int m_x2, int w, int x) {\n"
              "    if (x + w - 1 > m_x2 || m_x2 < 0 )\n"
              "        m_x2 = x + w - 1;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(float x) {\n" // x+1 => x
              "  if (x = -1.0e20) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(float x) {\n" // x+1 => x
              "  if (x >= 1.0e20 && x  5 && x == 1;\n"
              "    c = x < 1 && x == 3;\n"
              "    d = x >= 5 && x == 1;\n"
              "    e = x  5 && x == 1. [incorrectLogicOperator]\n"
                      "[test.cpp:3:15]: (warning) Logical conjunction always evaluates to false: x < 1 && x == 3. [incorrectLogicOperator]\n"
                      "[test.cpp:4:16]: (warning) Logical conjunction always evaluates to false: x >= 5 && x == 1. [incorrectLogicOperator]\n"
                      "[test.cpp:5:16]: (warning) Logical conjunction always evaluates to false: x  2 || x+3 < 10) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical disjunction always evaluates to true: x+3 > 2 || x+3 < 10. [incorrectLogicOperator]\n", errout_str());
    }

    void incorrectLogicOperator6() { // char literals
        const Settings s = settingsBuilder(settings0).certainty(Certainty::inconclusive).build();
        check("void f(char x) {\n"
              "  if (x == '1' || x == '2') {}\n"
              "}", s);
        ASSERT_EQUALS("", errout_str());

        check("void f(char x) {\n"
              "  if (x == '1' && x == '2') {}\n"
              "}", s);
        ASSERT_EQUALS("[test.cpp:2:16]: (warning) Logical conjunction always evaluates to false: x == '1' && x == '2'. [incorrectLogicOperator]\n", errout_str());

        check("int f(char c) {\n"
              "  return (c >= 'a' && c  [test.cpp:2:25]: (style) Return value 'c>='z'' is always false [knownConditionTrueFalse]\n", errout_str());
    }

    void incorrectLogicOperator7() { // opposite expressions
        check("void f(int i) {\n"
              "  if (i || !i) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9]: (warning) Logical disjunction always evaluates to true: i || !(i). [incorrectLogicOperator]\n", errout_str());

        check("void f(int a, int b) {\n"
              "  if (a>b || a b || a b || a n && i == n. [incorrectLogicOperator]\n", errout_str());

        check("void foo(int i, const int n) { if ( i == n && i > n ) {} }");
        ASSERT_EQUALS("[test.cpp:1:44]: (warning) Logical conjunction always evaluates to false: i == n && i > n. [incorrectLogicOperator]\n", errout_str());

        check("void foo(int i, const int n) { if ( i == n && i < n ) {} }");
        ASSERT_EQUALS("[test.cpp:1:44]: (warning) Logical conjunction always evaluates to false: i == n && i < n. [incorrectLogicOperator]\n", errout_str());
    }

    void incorrectLogicOperator12() { // #8696
        check("struct A {\n"
              "    void f() const;\n"
              "};\n"
              "void foo(A a, A b) {\n"
              "  A x = b;\n"
              "  A y = b;\n"
              "  y.f();\n"
              "  if (a > x && a < y)\n"
              "    return;\n"
              "}");
        ASSERT_EQUALS(
            "[test.cpp:5:9] -> [test.cpp:6:9] -> [test.cpp:8:13]: (warning) Logical conjunction always evaluates to false: a > x && a < y. [incorrectLogicOperator]\n",
            errout_str());

        check("struct A {\n"
              "    void f();\n"
              "};\n"
              "void foo(A a, A b) {\n"
              "  A x = b;\n"
              "  A y = b;\n"
              "  y.f();\n"
              "  if (a > x && a < y)\n"
              "    return;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void foo(A a, A b) {\n"
              "  A x = b;\n"
              "  A y = b;\n"
              "  y.f();\n"
              "  if (a > x && a < y)\n"
              "    return;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void foo(A a, A b) {\n"
              "  const A x = b;\n"
              "  const A y = b;\n"
              "  y.f();\n"
              "  if (a > x && a < y)\n"
              "    return;\n"
              "}");
        ASSERT_EQUALS(
            "[test.cpp:2:15] -> [test.cpp:3:15] -> [test.cpp:5:13]: (warning) Logical conjunction always evaluates to false: a > x && a < y. [incorrectLogicOperator]\n",
            errout_str());

        check("struct A {\n"
              "    void f() const;\n"
              "};\n"
              "void foo(A a) {\n"
              "  A x = a;\n"
              "  A y = a;\n"
              "  y.f();\n"
              "  if (a > x && a < y)\n"
              "    return;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:8:9]: (style) Condition 'a>x' is always false [knownConditionTrueFalse]\n"
                      "[test.cpp:8:18]: (style) Condition 'ax' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo(A a) {\n"
              "  A x = a;\n"
              "  A y = a;\n"
              "  y.f();\n"
              "  if (a > x && a < y)\n"
              "    return;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:5:9]: (style) Condition 'a>x' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo(A a) {\n"
              "  const A x = a;\n"
              "  const A y = a;\n"
              "  y.f();\n"
              "  if (a > x && a < y)\n"
              "    return;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:5:9]: (style) Condition 'a>x' is always false [knownConditionTrueFalse]\n"
                      "[test.cpp:5:18]: (style) Condition 'a 5' is sufficient. [redundantCondition]\n", errout_str());

        check("void f(int x) {\n"
              "    if (x > 5 && x != 6)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(int x) {\n"
              "    if ((x > 5) && (x != 1))\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:17]: (style) Redundant condition: The condition 'x != 1' is redundant since 'x > 5' is sufficient. [redundantCondition]\n", errout_str());

        check("void f(int x) {\n"
              "    if ((x > 5) && (x != 6))\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(int x, bool& b) {\n"
              "    b = x > 3 || x == 4;\n"
              "    c = x < 5 || x == 4;\n"
              "    d = x >= 3 || x == 4;\n"
              "    e = x  5 || x != 1;\n"
              "    c = x < 1 || x != 3;\n"
              "    d = x >= 5 || x != 1;\n"
              "    e = x  6 && x > 5;\n"
              "    c = x > 5 || x > 6;\n"
              "    d = x < 6 && x < 5;\n"
              "    e = x < 5 || x < 6;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (style) Redundant condition: The condition 'x > 5' is redundant since 'x > 6' is sufficient. [redundantCondition]\n"
                      "[test.cpp:3:15]: (style) Redundant condition: The condition 'x > 6' is redundant since 'x > 5' is sufficient. [redundantCondition]\n"
                      "[test.cpp:4:15]: (style) Redundant condition: The condition 'x < 6' is redundant since 'x < 5' is sufficient. [redundantCondition]\n"
                      "[test.cpp:5:15]: (style) Redundant condition: The condition 'x < 5' is redundant since 'x < 6' is sufficient. [redundantCondition]\n",
                      errout_str());

        check("void f(double x, bool& b) {\n"
              "    b = x > 6.5 && x > 5.5;\n"
              "    c = x > 5.5 || x > 6.5;\n"
              "    d = x < 6.5 && x < 5.5;\n"
              "    e = x < 5.5 || x < 6.5;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:17]: (style) Redundant condition: The condition 'x > 5.5' is redundant since 'x > 6.5' is sufficient. [redundantCondition]\n"
                      "[test.cpp:3:17]: (style) Redundant condition: The condition 'x > 6.5' is redundant since 'x > 5.5' is sufficient. [redundantCondition]\n"
                      "[test.cpp:4:17]: (style) Redundant condition: The condition 'x < 6.5' is redundant since 'x < 5.5' is sufficient. [redundantCondition]\n"
                      "[test.cpp:5:17]: (style) Redundant condition: The condition 'x < 5.5' is redundant since 'x < 6.5' is sufficient. [redundantCondition]\n",
                      errout_str());

        check("void f(const char *p) {\n" // #10320
              "    if (!p || !*p || *p != 'x') {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:2:19]: (style) Redundant condition: The condition '!*p' is redundant since '*p != 'x'' is sufficient. [redundantCondition]\n",
                      errout_str());
    }

    void incorrectLogicOp_condSwapping() {
        check("void f(int x) {\n"
              "    if (x < 1 && x > 3)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3. [incorrectLogicOperator]\n", errout_str());

        check("void f(int x) {\n"
              "    if (1 > x && x > 3)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3. [incorrectLogicOperator]\n", errout_str());

        check("void f(int x) {\n"
              "    if (x < 1 && 3 < x)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3. [incorrectLogicOperator]\n", errout_str());

        check("void f(int x) {\n"
              "    if (1 > x && 3 < x)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3. [incorrectLogicOperator]\n", errout_str());

        check("void f(int x) {\n"
              "    if (x > 3 && x < 1)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1. [incorrectLogicOperator]\n", errout_str());

        check("void f(int x) {\n"
              "    if (3 < x && x < 1)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1. [incorrectLogicOperator]\n", errout_str());

        check("void f(int x) {\n"
              "    if (x > 3 && 1 > x)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1. [incorrectLogicOperator]\n", errout_str());

        check("void f(int x) {\n"
              "    if (3 < x && 1 > x)\n"
              "        a++;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1. [incorrectLogicOperator]\n", errout_str());
    }

    void modulo() {
        check("bool f(bool& b1, bool& b2, bool& b3) {\n"
              "    b1 = a % 5 == 4;\n"
              "    b2 = a % c == 100000;\n"
              "    b3 = a % 5 == c;\n"
              "    return a % 5 == 5-p;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("bool f(bool& b1, bool& b2, bool& b3, bool& b4, bool& b5) {\n"
              "    b1 = a % 5 < 5;\n"
              "    b2 = a % 5 = 5;\n"
              "    return a % 5 > 5;\n"
              "}");
        ASSERT_EQUALS(
            "[test.cpp:2:16]: (warning) Comparison of modulo result is predetermined, because it is always less than 5. [moduloAlwaysTrueFalse]\n"
            "[test.cpp:3:16]: (warning) Comparison of modulo result is predetermined, because it is always less than 5. [moduloAlwaysTrueFalse]\n"
            "[test.cpp:4:16]: (warning) Comparison of modulo result is predetermined, because it is always less than 5. [moduloAlwaysTrueFalse]\n"
            "[test.cpp:5:16]: (warning) Comparison of modulo result is predetermined, because it is always less than 5. [moduloAlwaysTrueFalse]\n"
            "[test.cpp:6:16]: (warning) Comparison of modulo result is predetermined, because it is always less than 5. [moduloAlwaysTrueFalse]\n"
            "[test.cpp:7:18]: (warning) Comparison of modulo result is predetermined, because it is always less than 5. [moduloAlwaysTrueFalse]\n",
            errout_str());

        check("void f(bool& b1, bool& b2) {\n"
              "    b1 = bar() % 5 < 889;\n"
              "    if(x[593] % 5  [test.cpp:3:17]: (warning) Opposite inner 'return' condition leads to a dead code block. [oppositeInnerCondition]\n", errout_str());

        check("void foo(int a, int b) {\n"
              "    if(a==b)\n"
              "        if(b!=a)\n"
              "            cout  [test.cpp:3:13]: (warning) Opposite inner 'if' condition leads to a dead code block. [oppositeInnerCondition]\n", errout_str());

        check("void foo(int a) {\n"
              "    if(a >= 50) {\n"
              "        if(a < 50)\n"
              "            cout = 50) {\n"
              "        if(a > 50)\n"
              "            cout  5) {\n"
              "       i = bar();\n"
              "       if(i < 5) {\n"
              "           cout 5) {\n"
              "        foo(i);\n"
              "        if(i5) {\n"
              "        foo(i);\n"
              "        if(i5) {\n"
              "        foo(i);\n"
              "        if(i [test.cpp:5:13]: (warning) Opposite inner 'if' condition leads to a dead code block. [oppositeInnerCondition]\n", errout_str());

        check("void foo(const int &i);\n"
              "void bar(int i) {\n"
              "    if(i>5) {\n"
              "        foo(i);\n"
              "        if(i [test.cpp:5:13]: (warning) Opposite inner 'if' condition leads to a dead code block. [oppositeInnerCondition]\n", errout_str());

        check("void foo(int i);\n"
              "void bar() {\n"
              "    int i; i = func();\n"
              "    if(i>5) {\n"
              "        foo(i);\n"
              "        if(i [test.cpp:6:13]: (warning) Opposite inner 'if' condition leads to a dead code block. [oppositeInnerCondition]\n", errout_str());

        check("class C { void f(int &i) const; };\n" // #7028 - variable is changed by const method
              "void foo(C c, int i) {\n"
              "  if (i==5) {\n"
              "    c.f(i);\n"
              "    if (i != 5) {}\n"
              "  }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // see linux revision 1f80c0cc
        check("int generic_write_sync(int,int,int);\n"
              "\n"
              "void cifs_writev(int i) {\n"
              "   int rc = __generic_file_aio_write();\n"
              "   if (rc > 0){\n"
              "       err = generic_write_sync(file, iocb->ki_pos - rc, rc);\n"
              "       if(rc < 0) {\n"  // 
        check("void f(int x) {\n"
              "\n"
              "  if (x>4) {\n"
              "    if (x==5) {}\n"
              "  }\n"
              "}");
        ASSERT_EQUALS("", errout_str());
        check("void f(int x) {\n"
              "\n"
              "  if (x>4) {\n"
              "    if (x>5) {}\n"
              "  }\n"
              "}");
        ASSERT_EQUALS("", errout_str());
        check("void f(int x) {\n"
              "\n"
              "  if (x>4) {\n"
              "    if (x>=5) {}\n" // 100', second condition is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        check("bool f(int x) {\n"
              "  if (x > 100) { return false; }\n"
              "  return x > 100;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:3:12]: (warning) Identical condition and return expression 'x>100', return value is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        check("void f(int x) {\n"
              "  if (x > 100) { return; }\n"
              "  if (x > 100 || y > 100) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:3:9]: (warning) Identical condition 'x>100', second condition is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        check("void f(int x) {\n"
              "  if (x > 100) { return; }\n"
              "  if (x > 100 && y > 100) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:3:9]: (warning) Identical condition 'x>100', second condition is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        check("void f(int x) {\n"
              "  if (x > 100) { return; }\n"
              "  if (abc) {}\n"
              "  if (x > 100) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:4:9]: (warning) Identical condition 'x>100', second condition is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        check("void f(int x) {\n"
              "  if (x > 100) { return; }\n"
              "  while (abc) { y = x; }\n"
              "  if (x > 100) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:4:9]: (warning) Identical condition 'x>100', second condition is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        ASSERT_THROW_INTERNAL(check("void f(int x) {\n"  // #8217 - crash for incomplete code
                                    "  if (x > 100) { return; }\n"
                                    "  X(do);\n"
                                    "  if (x > 100) {}\n"
                                    "}"),
                              SYNTAX);

        check("void f(const int *i) {\n"
              "  if (!i) return;\n"
              "  if (!num1tok) { *num1 = *num2; }\n"
              "  if (!i) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:7] -> [test.cpp:4:7]: (warning) Identical condition '!i', second condition is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        check("void C::f(Tree &coreTree) {\n" // daca
              "  if(!coreTree.build())\n"
              "    return;\n"
              "  coreTree.dostuff();\n"
              "  if(!coreTree.build()) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("struct C { void f(const Tree &coreTree); };\n"
              "void C::f(const Tree &coreTree) {\n"
              "  if(!coreTree.build())\n"
              "    return;\n"
              "  coreTree.dostuff();\n"
              "  if(!coreTree.build()) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:3:6] -> [test.cpp:6:6]: (warning) Identical condition '!coreTree.build()', second condition is always false [identicalConditionAfterEarlyExit]\n", errout_str());

        check("void f(int x) {\n" // daca: labplot
              "  switch(type) {\n"
              "  case 1:\n"
              "    if (x == 0) return 1;\n"
              "    else return 2;\n"
              "  case 2:\n"
              "    if (x == 0) return 3;\n"
              "    else return 4;\n"
              "  }\n"
              "  return 0;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("static int failed = 0;\n"
              "void f() {\n"
              "  if (failed) return;\n"
              "  checkBuffer();\n"
              "  if (failed) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // daca icu
        check("void f(const uint32_t *section, int32_t  start) {\n"
              "  if(10> ch;\n"
              "  if (ch != '|') {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        // #8924
        check("struct A {\n"
              "    void f() {\n"
              "        if (this->FileIndex >= 0) return;\n"
              "        this->FileIndex = 1 ;\n"
              "        if (this->FileIndex < 0) return;\n"
              "    }\n"
              "    int FileIndex;\n"
              "};");
        ASSERT_EQUALS("[test.cpp:5:29]: (style) Condition 'this->FileIndex0)' is equivalent to '!dead || (*it).ticks>0' [redundantCondition]\n", errout_str());

        check("void f() {\n"
              "  if (!x || (x && (2>(y-1)))) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:10]: (style) Redundant condition: x. '!x || (x && 2>(y-1))' is equivalent to '!x || 2>(y-1)' [redundantCondition]\n", errout_str());

        check("void f(bool a, bool b) {\n"
              "    if (a || (a && b)) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11]: (style) Redundant condition: a. 'a || (a && b)' is equivalent to 'a' [redundantCondition]\n", errout_str());

        check("void f(bool a, bool b) {\n"
              "    if (a && (a || b)) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11]: (style) Redundant condition: a. 'a && (a || b)' is equivalent to 'a' [redundantCondition]\n", errout_str());
    }

    // clarify conditions with bitwise operator and comparison
    void clarifyCondition2() {
        check("void f() {\n"
              "    if (x & 3 == 2) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:8]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses. [clarifyCondition]\n"
                      "[test.cpp:2:11]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses. [clarifyCondition]\n"
                      "[test.cpp:2:11]: (style) Condition 'x&3==2' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void f() {\n"
              "    if (a & fred1.x == fred2.y) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:8]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses. [clarifyCondition]\n"
                      "[test.cpp:2:11]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses. [clarifyCondition]\n"
                      , errout_str());
    }

    // clarify condition that uses ! operator and then bitwise operator
    void clarifyCondition3() {
        check("void f(int w) {\n"
              "    if(!w & 0x8000) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses. [clarifyCondition]\n", errout_str());

        check("void f(int w) {\n"
              "    if((!w) & 0x8000) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f() {\n"
              "    if (x == foo() & 2) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:20]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses. [clarifyCondition]\n", errout_str());

        check("void f() {\n"
              "    if (2 & x == foo()) {}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses. [clarifyCondition]\n", errout_str());

        check("void f() {\n"
              "    if (2 & (x == foo())) {}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(std::list &ints) { }");
        ASSERT_EQUALS("", errout_str());

        check("void f() { A a; }");
        ASSERT_EQUALS("", errout_str());

        check("void f() { a(xsecond;\n" // Declaring a reference to a boolean; & is no operator at all
              "    execute(secondExpression, &programMemory, &result, &error);\n" // Unary &
              "}");
        ASSERT_EQUALS("", errout_str());
    }

    void clarifyCondition8() {
        // don't warn when boolean result comes from function call, array index, etc
        // the operator precedence is not unknown then
        check("bool a();\n"
              "bool f(bool b) {\n"
              "    return (a() & b);\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("bool f(bool *a, bool b) {\n"
              "    return (a[10] & b);\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("struct A { bool a; };\n"
              "bool f(struct A a, bool b) {\n"
              "    return (a.a & b);\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("struct A { bool a; };\n"
              "bool f(struct A a, bool b) {\n"
              "    return (A::a & b);\n"
              "}");
        ASSERT_EQUALS("", errout_str());
    }

    void testBug5895() {
        check("void png_parse(uint64_t init, int buf_size) {\n"
              "    if (init == 0x89504e470d0a1a0a || init == 0x8a4d4e470d0a1a0a)\n"
              "        ;\n"
              "}");
        ASSERT_EQUALS("", errout_str());
    }

    void testBug5309() {
        check("extern uint64_t value;\n"
              "void foo() {\n"
              "    if( ( value >= 0x7ff0000000000001ULL )\n"
              "            && ( value x;\n"
              "  int x2 = s->x;\n"
              "  if (x1 == 10 && x2 == 10) {}\n" // 0' is always true [knownConditionTrueFalse]\n",
                      errout_str());

        check("struct S { int bar(int i) const; };\n"
              "void foo(const S& s) {\n"
              "    if (s.bar(1) == 0 && s.bar(1) > 0) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:23]: (warning) Logical conjunction always evaluates to false: s.bar(1) == 0 && s.bar(1) > 0. [incorrectLogicOperator]\n",
                      errout_str());

        check("struct B {\n" // #10618
              "    void Modify();\n"
              "    static void Static();\n"
              "    virtual void CalledByModify();\n"
              "};\n"
              "struct D : B {\n"
              "    int i{};\n"
              "    void testV();\n"
              "    void testS();\n"
              "    void CalledByModify() override { i = 0; }\n"
              "};\n"
              "void D::testV() {\n"
              "    i = 1;\n"
              "    B::Modify();\n"
              "    if (i == 1) {}\n"
              "}\n"
              "void D::testS() {\n"
              "    i = 1;\n"
              "    B::Static();\n"
              "    if (i == 1) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:20:11]: (style) Condition 'i==1' is always true [knownConditionTrueFalse]\n", errout_str());

        check("typedef struct { bool x; } s_t;\n" // #8446
              "unsigned f(bool a, bool b) {\n"
              "    s_t s;\n"
              "    const unsigned col = a ? (s.x = false) : (b = true);\n"
              "    if (!s.x) {}\n"
              "    return col;\n"
              "}\n");
        ASSERT_EQUALS("", errout_str());

        check("struct S {\n" // #11233
              "    static std::string m;\n"
              "    static void f() { m = \"abc\"; }\n"
              "    static void g() {\n"
              "        m.clear();\n"
              "        f();\n"
              "        if (m.empty()) {}\n"
              "    }\n"
              "};\n");
        ASSERT_EQUALS("", errout_str());

        // #11203
        check("void f() {\n"
              "    int i = 10;\n"
              "    if(i > 9.9){}\n"
              "    float f = 9.9f;\n"
              "    if(f < 10) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:10]: (style) Condition 'i>9.9' is always true [knownConditionTrueFalse]\n"
            "[test.cpp:5:10]: (style) Condition 'f don't warn
              "    assert(x + 100U < x);\n"
              "}");
        ASSERT_EQUALS("", errout_str());


        // x + c < x

#define MSG(EXPR, RESULT)   "[test.cpp:1:30]: (warning) Invalid test for overflow '" EXPR "'; signed integer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " RESULT ". [invalidTestForOverflow]\n"

        check("int f(int x) { return x + 10 > x; }");
        ASSERT_EQUALS(MSG("x+10>x", "true"), errout_str());

        check("int f(int x) { return x + 10 >= x; }");
        ASSERT_EQUALS(MSG("x+10>=x", "true"), errout_str());

        check("int f(int x) { return x + 10 < x; }");
        ASSERT_EQUALS(MSG("x+10= x; }");
        ASSERT_EQUALS(MSG("x-10>=x", "false"), errout_str());

        check("int f(int x) { return x - 10 < x; }");
        ASSERT_EQUALS(MSG("x-10=x", "y2U && s[0]=='4' && s[0]=='2';\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:2:35] -> [test.cpp:2:48]: (style) Return value 's[0]=='2'' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void f(int i) { if (i == 1 || 2) {} }\n"); // #12487
        ASSERT_EQUALS("[test.cpp:1:28]: (style) Condition 'i==1||2' is always true [knownConditionTrueFalse]\n", errout_str());

        check("enum E { E1 = 1, E2 = 2 };\n"
              "void f(int i) { if (i == E1 || E2) {} }\n");
        ASSERT_EQUALS("[test.cpp:2:29]: (style) Condition 'i==E1||E2' is always true [knownConditionTrueFalse]\n", errout_str());

        check("void f(bool a, bool b) {\n" // #11614
              "    if (b) {\n"
              "        bool x = !b || a;\n"
              "    }\n"
              "}\n"
              "void g(bool a, bool b) {\n"
              "    if (!b) {\n"
              "        bool x = a || b;\n"
              "    }\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:9] -> [test.cpp:3:18]: (style) Condition '!b' is always false [knownConditionTrueFalse]\n"
                      "[test.cpp:7:9] -> [test.cpp:8:23]: (style) Condition 'b' is always false [knownConditionTrueFalse]\n",
                      errout_str());
    }

    void pointerAdditionResultNotNull() {
        check("void f(char *ptr) {\n"
              "  if (ptr + 1 != 0);\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:15]: (warning) Comparison is wrong. Result of 'ptr+1' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour. [pointerAdditionResultNotNull]\n", errout_str());
    }

    void duplicateConditionalAssign() {
        setMultiline();

        check("void f(int& x, int y) {\n"
              "    if (x == y)\n"
              "        x = y;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:3:11]: style: Assignment 'x=y' is redundant with condition 'x==y'. [duplicateConditionalAssign]\n"
                      "[test.cpp:2:11]: note: Condition 'x==y'\n"
                      "[test.cpp:3:11]: note: Assignment 'x=y' is redundant\n", errout_str());

        check("void f(int& x, int y) {\n"
              "    if (x != y)\n"
              "        x = y;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:11]: style: The statement 'if (x!=y) x=y' is logically equivalent to 'x=y'. [duplicateConditionalAssign]\n"
                      "[test.cpp:3:11]: note: Assignment 'x=y'\n"
                      "[test.cpp:2:11]: note: Condition 'x!=y' is redundant\n", errout_str());

        check("void f(int& x, int y) {\n"
              "    if (x == y)\n"
              "        x = y;\n"
              "    else\n"
              "        x = 1;\n"
              "}");
        ASSERT_EQUALS("[test.cpp:3:11]: style: Assignment 'x=y' is redundant with condition 'x==y'. [duplicateConditionalAssign]\n"
                      "[test.cpp:2:11]: note: Condition 'x==y'\n"
                      "[test.cpp:3:11]: note: Assignment 'x=y' is redundant\n", errout_str());

        check("void f(int& x, int y) {\n"
              "    if (x != y)\n"
              "        x = y;\n"
              "    else\n"
              "        x = 1;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(int& x, int y) {\n"
              "    if (x == y)\n"
              "        x = y + 1;\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void g();\n"
              "void f(int& x, int y) {\n"
              "    if (x == y) {\n"
              "        x = y;\n"
              "        g();\n"
              "    }\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("bool f(bool b) {\n"
              "    if (b)\n"
              "        b = false;\n"
              "    else\n"
              "        g();\n"
              "    return b;\n"
              "}\n");
        ASSERT_EQUALS("", errout_str());

        check("void f(int& i) {\n"
              "    if (!i)\n"
              "        i = 1; \n"
              "}\n");
        ASSERT_EQUALS("", errout_str());

        check("struct S {\n" // #9406
              "    S() : b(false) {}\n"
              "    void f() {\n"
              "        if (b) b = true;\n"
              "        if (b) b = false;\n"
              "        if (!b) b = true;\n"
              "        if (!b) b = false;\n"
              "    }\n"
              "    bool b;\n"
              "};\n");
        ASSERT_EQUALS("[test.cpp:4:13]: style: The statement 'if (b) b=true' is redundant. [duplicateConditionalAssign]\n"
                      "[test.cpp:4:18]: note: Assignment 'b=true'\n"
                      "[test.cpp:4:13]: note: Condition 'b' is redundant\n"
                      "[test.cpp:5:13]: style: The statement 'if (b) b=false' is logically equivalent to 'b=false'. [duplicateConditionalAssign]\n"
                      "[test.cpp:5:18]: note: Assignment 'b=false'\n"
                      "[test.cpp:5:13]: note: Condition 'b' is redundant\n"
                      "[test.cpp:6:13]: style: The statement 'if (!b) b=true' is logically equivalent to 'b=true'. [duplicateConditionalAssign]\n"
                      "[test.cpp:6:19]: note: Assignment 'b=true'\n"
                      "[test.cpp:6:13]: note: Condition '!b' is redundant\n"
                      "[test.cpp:7:13]: style: The statement 'if (!b) b=false' is redundant. [duplicateConditionalAssign]\n"
                      "[test.cpp:7:19]: note: Assignment 'b=false'\n"
                      "[test.cpp:7:13]: note: Condition '!b' is redundant\n",
                      errout_str());
    }

    void checkAssignmentInCondition() {
        check("void f(std::string s) {\n"
              "    if (s=\"123\"){}\n"
              "}");
        ASSERT_EQUALS("[test.cpp:2:10]: (style) Suspicious assignment in condition. Condition 's=\"123\"' is always true. [assignmentInCondition]\n", errout_str());

        check("void f(std::string *p) {\n"
              "    if (p=foo()){}\n"
              "}");
        ASSERT_EQUALS("", errout_str());

        check("void f(uint32_t u) {\n" // #2490
              "    if ((u = 0x00000000) || (u = 0xffffffff)) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:2:12]: (style) Condition 'u=0x00000000' is always false [knownConditionTrueFalse]\n"
                      "[test.cpp:2:32]: (style) Condition 'u=0xffffffff' is always true [knownConditionTrueFalse]\n",
                      errout_str());
    }

    void compareOutOfTypeRange() {
        const Settings settingsUnix64 = settingsBuilder().severity(Severity::style).platform(Platform::Type::Unix64).build();

        check("void f(unsigned char c) {\n"
              "  if (c == 256) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("[test.cpp:2:12]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false. [compareValueOutOfTypeRangeError]\n", errout_str());

        check("void f(unsigned char* b, int i) {\n" // #6372
              "  if (b[i] == 256) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("[test.cpp:2:15]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false. [compareValueOutOfTypeRangeError]\n", errout_str());

        check("void f(unsigned char c) {\n"
              "  if (c == 255) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("", errout_str());

        check("void f(bool b) {\n"
              "  if (b == true) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("", errout_str());

        // #10372
        check("void f(signed char x) {\n"
              "  if (x == 0xff) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("[test.cpp:2:12]: (style) Comparing expression of type 'signed char' against value 255. Condition is always false. [compareValueOutOfTypeRangeError]\n", errout_str());

        check("void f(short x) {\n"
              "  if (x == 0xffff) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("[test.cpp:2:12]: (style) Comparing expression of type 'signed short' against value 65535. Condition is always false. [compareValueOutOfTypeRangeError]\n", errout_str());

        check("void f(int x) {\n"
              "  if (x == 0xffffffff) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("", errout_str());

        check("void f(long x) {\n"
              "  if (x == ~0L) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("", errout_str());

        check("void f(long long x) {\n"
              "  if (x == ~0LL) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("", errout_str());

        check("int f(int x) {\n"
              "    const int i = 0xFFFFFFFF;\n"
              "    if (x == i) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("", errout_str());

        check("void f() {\n"
              "  char c;\n"
              "  if ((c = foo()) != -1) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("", errout_str());

        check("void f(int x) {\n"
              "  if (x < 3000000000) {}\n"
              "}", settingsUnix64);
        ASSERT_EQUALS("[test.cpp:2:11]: (style) Comparing expression of type 'signed int' against value 3000000000. Condition is always true. [compareValueOutOfTypeRangeError]\n", errout_str());

        check("void f(const signed char i) {\n" // #8545
              "    if (i >  -129) {}\n" // warn
              "    if (i >= -128) {}\n" // warn
              "    if (i >= -127) {}\n"
              "    if (i <  +128) {}\n" // warn
              "    if (i = 0) {}\n"
              "    if (u   255) {}\n" // warn
              "    if (u <  255) {}\n"
              "    if (u >= 255) {}\n"
              "    if (u   u) {}\n"
              "    if (0   = u) {}\n"
              "    if (255 <  u) {}\n" // warn
              "    if (255 >  u) {}\n"
              "    if (255 = u) {}\n" // warn
              "}\n", settingsUnix64);
        ASSERT_EQUALS("[test.cpp:6:14]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always false. [compareValueOutOfTypeRangeError]\n"
                      "[test.cpp:9:14]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always true. [compareValueOutOfTypeRangeError]\n"
                      "[test.cpp:14:9]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always false. [compareValueOutOfTypeRangeError]\n"
                      "[test.cpp:17:9]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always true. [compareValueOutOfTypeRangeError]\n",
                      errout_str());

        check("void f(bool b) {\n" // #14037
              "    if (b != 2) {}\n"
              "}\n", settingsUnix64);
        ASSERT_EQUALS("[test.cpp:2:14]: (style) Comparing expression of type 'bool' against value 2. Condition is always true. [compareValueOutOfTypeRangeError]\n",
                      errout_str());

        check("void f(const std::uint32_t& u) {\n" // #9078
              "    if (u >= UINT32_MAX) {}\n"
              "    if (u  UINT32_MAX) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:14]: (style) Comparing expression of type 'const unsigned int &' against value 4294967295. Condition is always true. [compareValueOutOfTypeRangeError]\n"
                      "[test.cpp:4:13]: (style) Comparing expression of type 'const unsigned int &' against value 4294967295. Condition is always false. [compareValueOutOfTypeRangeError]\n",
                      errout_str());

        check("void f() {\n"
              "    long long ll = 1024 * 1024 * 1024;\n"
              "    if (ll * 8 < INT_MAX) {}\n"
              "    if (INT_MAX > ll * 8) {}\n"
              "}\n");
        ASSERT_EQUALS("", errout_str());

        check("bool f(int a, int b) {\n" // #12896
              "    if (a < INT_MIN && b > INT_MAX)\n"
              "        return true;\n"
              "    return false;\n"
              "}\n"
              "bool g(int x) {\n" // #6796
              "    return (x > INT_MAX) ? true : false;\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:2:13]: (style) Comparing expression of type 'signed int' against value -2147483648. Condition is always false. [compareValueOutOfTypeRangeError]\n"
                      "[test.cpp:2:28]: (style) Comparing expression of type 'signed int' against value 2147483647. Condition is always false. [compareValueOutOfTypeRangeError]\n"
                      "[test.cpp:7:17]: (style) Comparing expression of type 'signed int' against value 2147483647. Condition is always false. [compareValueOutOfTypeRangeError]\n",
                      errout_str());
    }

    void knownConditionCast() {
        check("void f(int i) {\n" // #9976
              "    if (i < 0 || (unsigned)i > 5) {}\n"
              "}\n");
        ASSERT_EQUALS("", errout_str());

        check("struct B {\n" // #12941
              "    virtual void f();\n"
              "};\n"
              "struct One : public B {};\n"
              "struct Two : public B {};\n"
              "void g(const B& b) {\n"
              "    const Two* two = nullptr;\n"
              "    const One* one = dynamic_cast(&b);\n"
              "    if (one == nullptr)\n"
              "        two = dynamic_cast(&b);\n"
              "    if (two) {}\n"
              "}\n");
        ASSERT_EQUALS("", errout_str());
    }

    void knownConditionIncrementLoop() { // #9808
        check("void f() {\n"
              "    int a = 0;\n"
              "    while (++a < 5) {}\n"
              "    if (a == 1) {}\n"
              "    std::cout  [test.cpp:18:13]: (style) Condition 'mS.b' is always false [knownConditionTrueFalse]\n", errout_str());
    }

    void knownConditionIncDecOperator() {
        check(
            "void f() {\n"
            "    unsigned int d = 0;\n"
            "    for (int i = 0; i < 4; ++i) {\n"
            "        if (i < 3)\n"
            "            ++d;\n"
            "        else if (--d == 0)\n"
            "            ;\n"
            "    }\n"
            "}\n");
        ASSERT_EQUALS("", errout_str());
    }

    void knownConditionFloating() {
        check("void foo() {\n"   // #11199
              "    float f = 1.0;\n"
              "    if (f > 1.0f) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:11]: (style) Condition 'f>1.0f' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo() {\n"     // #11199
              "    float f = 1.0;\n"
              "    if (f > 1.0L) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:11]: (style) Condition 'f>1.0L' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo() {\n"   // #11199
              "    float f = 1.0f;\n"
              "    if (f > 1.0) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:11]: (style) Condition 'f>1.0' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo() {\n"     // #11199
              "    float f = 1.0f;\n"
              "    if (f > 1.0L) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:11]: (style) Condition 'f>1.0L' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo() {\n"     // #11199
              "    float f = 1.0L;\n"
              "    if (f > 1.0) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:11]: (style) Condition 'f>1.0' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo() {\n"     // #11199
              "    float f = 1.0L;\n"
              "    if (f > 1.0f) {}\n"
              "}\n");
        ASSERT_EQUALS("[test.cpp:3:11]: (style) Condition 'f>1.0f' is always false [knownConditionTrueFalse]\n", errout_str());

        check("void foo() {\n"   // #11201
              "    float f = 0x1.4p+3;\n" // hex fraction 1.4 (decimal 1.25) scaled by 2^3, that is 10.0
              "    if (f > 9.9) {}\n"
              "    if (f < 9.9) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:11]: (style) Condition 'f>9.9' is always true [knownConditionTrueFalse]\n"
            "[test.cpp:4:11]: (style) Condition 'f1.01' is always false [knownConditionTrueFalse]\n",
            errout_str());

        check("void foo() {\n"
              "    float f = 1.0f;\n"
              "    if (f > 1) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:11]: (style) Condition 'f>1' is always false [knownConditionTrueFalse]\n",
            errout_str());

        check("void foo() {\n" // #13508
              "    float f = 1.0f;\n"
              "    if (f > 1.00f) {}\n"
              "}\n");
        TODO_ASSERT_EQUALS(
            "[test.cpp:3]: (style) Condition 'f>1.00f' is always false\n",
            "",
            errout_str());

        check("void foo() {\n"
              "    float f = 1.0;\n"
              "    if (f > 1) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:11]: (style) Condition 'f>1' is always false [knownConditionTrueFalse]\n",
            errout_str());

        check("void foo() {\n"// #13508
              "    float f = 1.0;\n"
              "    if (f > 1.00) {}\n"
              "}\n");
        TODO_ASSERT_EQUALS(
            "[test.cpp:3]: (style) Condition 'f>1.00' is always false\n",
            "",
            errout_str());

        check("void foo() {\n" // #13506
              "    float nf = -1.0;\n"
              "    if (nf > +1.0) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:12]: (style) Condition 'nf>+1.0' is always false [knownConditionTrueFalse]\n",
            errout_str());

        check("void foo() {\n" // #11200
              "    float f = 1.0;\n"
              "    if (f > -1.0) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:11]: (style) Condition 'f>-1.0' is always true [knownConditionTrueFalse]\n",
            errout_str());

        check("void foo() {\n" // #13508
              "    float f = 1.0;\n"
              "    if (f > 1.0) {}\n"
              "}\n");
        TODO_ASSERT_EQUALS(
            "[test.cpp:3]: (style) Condition 'f>1.0' is always true\n",
            "",
            errout_str());

        check("void foo() {\n" // #11200
              "    float pf = +1.0;\n"
              "    if (pf > -1.0) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:12]: (style) Condition 'pf>-1.0' is always true [knownConditionTrueFalse]\n",
            errout_str());

        check("void foo() {\n" // #13508
              "    float pf = +1.0;\n"
              "    if (pf > 1.0) {}\n"
              "}\n");
        TODO_ASSERT_EQUALS(
            "[test.cpp:3]: (style) Condition 'pf>1.0' is always true\n",
            "",
            errout_str());

        check("void foo() {\n" // #11200
              "    float nf = -1.0;\n"
              "    if (nf > 1.0) {}\n"
              "}\n");
        ASSERT_EQUALS(
            "[test.cpp:3:12]: (style) Condition 'nf>1.0' is always false [knownConditionTrueFalse]\n",
            errout_str());

        check("void foo() {\n" // / #13508
              "    float nf = -1.0;\n"
              "    if (nf > -1.0) {}\n"
              "}\n");
        TODO_ASSERT_EQUALS(
            "[test.cpp:3]: (style) Condition 'nf>-1.0' is always false\n",
            "",
            errout_str());
    }
};

REGISTER_TEST(TestCondition)

Web Proxy Viewer  |  New URL  |  Original Page