diff --git a/Makefile b/Makefile index 0fb7ccac1..b2f2825f0 100644 --- a/Makefile +++ b/Makefile @@ -283,7 +283,7 @@ endif ###### Build -$(SRCDIR)/astutils.o: lib/astutils.cpp lib/cxx11emu.h lib/astutils.h lib/symboldatabase.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/utils.h +$(SRCDIR)/astutils.o: lib/astutils.cpp lib/cxx11emu.h lib/astutils.h lib/symboldatabase.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/utils.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/astutils.o $(SRCDIR)/astutils.cpp $(SRCDIR)/check.o: lib/check.cpp lib/cxx11emu.h lib/check.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h @@ -310,7 +310,7 @@ $(SRCDIR)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp lib/cxx11emu.h lib/ch $(SRCDIR)/checkclass.o: lib/checkclass.cpp lib/cxx11emu.h lib/checkclass.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkclass.o $(SRCDIR)/checkclass.cpp -$(SRCDIR)/checkcondition.o: lib/checkcondition.cpp lib/cxx11emu.h lib/checkcondition.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/astutils.h lib/symboldatabase.h lib/utils.h lib/checkother.h +$(SRCDIR)/checkcondition.o: lib/checkcondition.cpp lib/cxx11emu.h lib/checkcondition.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/astutils.h lib/checkother.h lib/symboldatabase.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkcondition.o $(SRCDIR)/checkcondition.cpp $(SRCDIR)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/cxx11emu.h lib/checkexceptionsafety.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h @@ -322,10 +322,10 @@ $(SRCDIR)/checkinternal.o: lib/checkinternal.cpp lib/cxx11emu.h lib/checkinterna $(SRCDIR)/checkio.o: lib/checkio.cpp lib/cxx11emu.h lib/checkio.h lib/check.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkio.o $(SRCDIR)/checkio.cpp -$(SRCDIR)/checkleakautovar.o: lib/checkleakautovar.cpp lib/cxx11emu.h lib/checkleakautovar.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/checkmemoryleak.h lib/symboldatabase.h lib/utils.h +$(SRCDIR)/checkleakautovar.o: lib/checkleakautovar.cpp lib/cxx11emu.h lib/checkleakautovar.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/checkmemoryleak.h lib/symboldatabase.h lib/utils.h lib/astutils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkleakautovar.o $(SRCDIR)/checkleakautovar.cpp -$(SRCDIR)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/cxx11emu.h lib/checkmemoryleak.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h +$(SRCDIR)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/cxx11emu.h lib/checkmemoryleak.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h lib/astutils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkmemoryleak.o $(SRCDIR)/checkmemoryleak.cpp $(SRCDIR)/checknonreentrantfunctions.o: lib/checknonreentrantfunctions.cpp lib/cxx11emu.h lib/checknonreentrantfunctions.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h @@ -355,7 +355,7 @@ $(SRCDIR)/checkstring.o: lib/checkstring.cpp lib/cxx11emu.h lib/checkstring.h li $(SRCDIR)/checktype.o: lib/checktype.cpp lib/cxx11emu.h lib/checktype.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checktype.o $(SRCDIR)/checktype.cpp -$(SRCDIR)/checkuninitvar.o: lib/checkuninitvar.cpp lib/cxx11emu.h lib/checkuninitvar.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/checknullpointer.h lib/symboldatabase.h lib/utils.h +$(SRCDIR)/checkuninitvar.o: lib/checkuninitvar.cpp lib/cxx11emu.h lib/checkuninitvar.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/astutils.h lib/checknullpointer.h lib/symboldatabase.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -std=c++0x -c -o $(SRCDIR)/checkuninitvar.o $(SRCDIR)/checkuninitvar.cpp $(SRCDIR)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp lib/cxx11emu.h lib/checkunusedfunctions.h lib/config.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/library.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/utils.h diff --git a/lib/astutils.cpp b/lib/astutils.cpp index ba37e36c8..2f58af662 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -19,6 +19,36 @@ //--------------------------------------------------------------------------- #include "astutils.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include + +static bool isChar(const Variable* var) +{ + return (var && !var->isPointer() && !var->isArray() && var->typeStartToken()->str() == "char"); +} + +static bool isSignedChar(const Variable* var) +{ + return (isChar(var) && !var->typeStartToken()->isUnsigned()); +} + +bool astIsSignedChar(const Token *tok) +{ + if (!tok) + return false; + if (tok->str() == "*" && tok->astOperand1() && !tok->astOperand2()) { + const Variable *var = tok->astOperand1()->variable(); + if (!var || !var->isPointer()) + return false; + const Token *type = var->typeStartToken(); + while (type && type->str() == "const") + type = type->next(); + return (type && type->str() == "char" && !type->isUnsigned()); + } + return isSignedChar(tok->variable()); +} bool astIsIntegral(const Token *tok, bool unknown) { @@ -100,3 +130,205 @@ bool astIsFloat(const Token *tok, bool unknown) return unknown; } + +const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok) +{ + if (!tok) + return nullptr; + + const Token *ret = nullptr; + if (tok->isComparisonOp()) { + if (tok->astOperand1() && tok->astOperand1()->str() == rhs) { + // Invert comparator + std::string s = tok->str(); + if (s[0] == '>') + s[0] = '<'; + else if (s[0] == '<') + s[0] = '>'; + if (s == comp) { + ret = tok->astOperand2(); + } + } else if (tok->str() == comp && tok->astOperand2() && tok->astOperand2()->str() == rhs) { + ret = tok->astOperand1(); + } + } else if (comp == "!=" && rhs == std::string("0")) { + ret = tok; + } else if (comp == "==" && rhs == std::string("0")) { + if (tok->str() == "!") + ret = tok->astOperand1(); + } + while (ret && ret->str() == ".") + ret = ret->astOperand2(); + if (ret && ret->varId() == 0U) + ret = nullptr; + if (vartok) + *vartok = ret; + return ret; +} + +bool isSameExpression(bool cpp, const Token *tok1, const Token *tok2, const std::set &constFunctions) +{ + if (tok1 == nullptr && tok2 == nullptr) + return true; + if (tok1 == nullptr || tok2 == nullptr) + return false; + if (cpp) { + if (tok1->str() == "." && tok1->astOperand1() && tok1->astOperand1()->str() == "this") + tok1 = tok1->astOperand2(); + if (tok2->str() == "." && tok2->astOperand1() && tok2->astOperand1()->str() == "this") + tok2 = tok2->astOperand2(); + } + if (tok1->varId() != tok2->varId() || tok1->str() != tok2->str()) { + if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) || + (Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) { + return isSameExpression(cpp, tok1->astOperand1(), tok2->astOperand2(), constFunctions) && + isSameExpression(cpp, tok1->astOperand2(), tok2->astOperand1(), constFunctions); + } + return false; + } + if (tok1->str() == "." && tok1->originalName() != tok2->originalName()) + return false; + if (tok1->isExpandedMacro() || tok2->isExpandedMacro()) + return false; + if (tok1->isName() && tok1->next()->str() == "(" && tok1->str() != "sizeof") { + if (!tok1->function() && !Token::Match(tok1->previous(), ".|::") && constFunctions.find(tok1->str()) == constFunctions.end() && !tok1->isAttributeConst() && !tok1->isAttributePure()) + return false; + else if (tok1->function() && !tok1->function()->isConst() && !tok1->function()->isAttributeConst() && !tok1->function()->isAttributePure()) + return false; + } + // templates/casts + if ((Token::Match(tok1, "%name% <") && tok1->next()->link()) || + (Token::Match(tok2, "%name% <") && tok2->next()->link())) { + + // non-const template function that is not a dynamic_cast => return false + if (Token::simpleMatch(tok1->next()->link(), "> (") && + !(tok1->function() && tok1->function()->isConst()) && + tok1->str() != "dynamic_cast") + return false; + + // some template/cast stuff.. check that the template arguments are same + const Token *t1 = tok1->next(); + const Token *t2 = tok2->next(); + const Token *end1 = t1->link(); + const Token *end2 = t2->link(); + while (t1 && t2 && t1 != end1 && t2 != end2) { + if (t1->str() != t2->str()) + return false; + t1 = t1->next(); + t2 = t2->next(); + } + if (t1 != end1 || t2 != end2) + return false; + } + if (tok1->type() == Token::eIncDecOp || tok1->isAssignmentOp()) + return false; + // bailout when we see ({..}) + if (tok1->str() == "{") + return false; + if (tok1->str() == "(" && tok1->previous() && !tok1->previous()->isName()) { // cast => assert that the casts are equal + const Token *t1 = tok1->next(); + const Token *t2 = tok2->next(); + while (t1 && t2 && t1->str() == t2->str() && (t1->isName() || t1->str() == "*")) { + t1 = t1->next(); + t2 = t2->next(); + } + if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") + return false; + } + bool noncommuative_equals = + isSameExpression(cpp, tok1->astOperand1(), tok2->astOperand1(), constFunctions); + noncommuative_equals = noncommuative_equals && + isSameExpression(cpp, tok1->astOperand2(), tok2->astOperand2(), constFunctions); + + if (noncommuative_equals) + return true; + + const bool commutative = tok1->astOperand1() && tok1->astOperand2() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); + bool commuative_equals = commutative && + isSameExpression(cpp, tok1->astOperand2(), tok2->astOperand1(), constFunctions); + commuative_equals = commuative_equals && + isSameExpression(cpp, tok1->astOperand1(), tok2->astOperand2(), constFunctions); + + return commuative_equals; +} + +bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const std::set &constFunctions) +{ + if (!cond1 || !cond2) + return false; + + if (cond1->str() == "!") { + if (cond2->str() == "!=") { + if (cond2->astOperand1() && cond2->astOperand1()->str() == "0") + return isSameExpression(cpp, cond1->astOperand1(), cond2->astOperand2(), constFunctions); + if (cond2->astOperand2() && cond2->astOperand2()->str() == "0") + return isSameExpression(cpp, cond1->astOperand1(), cond2->astOperand1(), constFunctions); + } + return isSameExpression(cpp, cond1->astOperand1(), cond2, constFunctions); + } + + if (cond2->str() == "!") + return isOppositeCond(isNot, cpp, cond2, cond1, constFunctions); + + if (!cond1->isComparisonOp() || !cond2->isComparisonOp()) + return false; + + const std::string &comp1 = cond1->str(); + + // condition found .. get comparator + std::string comp2; + if (isSameExpression(cpp, cond1->astOperand1(), cond2->astOperand1(), constFunctions) && + isSameExpression(cpp, cond1->astOperand2(), cond2->astOperand2(), constFunctions)) { + comp2 = cond2->str(); + } else if (isSameExpression(cpp, cond1->astOperand1(), cond2->astOperand2(), constFunctions) && + isSameExpression(cpp, cond1->astOperand2(), cond2->astOperand1(), constFunctions)) { + comp2 = cond2->str(); + if (comp2[0] == '>') + comp2[0] = '<'; + else if (comp2[0] == '<') + comp2[0] = '>'; + } + + // is condition opposite? + return ((comp1 == "==" && comp2 == "!=") || + (comp1 == "!=" && comp2 == "==") || + (comp1 == "<" && comp2 == ">=") || + (comp1 == "<=" && comp2 == ">") || + (comp1 == ">" && comp2 == "<=") || + (comp1 == ">=" && comp2 == "<") || + (!isNot && ((comp1 == "<" && comp2 == ">") || + (comp1 == ">" && comp2 == "<")))); +} + +bool isConstExpression(const Token *tok, const std::set &constFunctions) +{ + if (!tok) + return true; + if (tok->isName() && tok->next()->str() == "(") { + if (!tok->function() && !Token::Match(tok->previous(), ".|::") && constFunctions.find(tok->str()) == constFunctions.end()) + return false; + else if (tok->function() && !tok->function()->isConst()) + return false; + } + if (tok->type() == Token::eIncDecOp) + return false; + // bailout when we see ({..}) + if (tok->str() == "{") + return false; + return isConstExpression(tok->astOperand1(),constFunctions) && isConstExpression(tok->astOperand2(),constFunctions); +} + +bool isWithoutSideEffects(bool cpp, const Token* tok) +{ + if (!cpp) + return true; + + while (tok && tok->astOperand2() && tok->astOperand2()->str() != "(") + tok = tok->astOperand2(); + if (tok && tok->varId()) { + const Variable* var = tok->variable(); + return var && (!var->isClass() || var->isPointer() || var->isStlType()); + } + return true; +} + diff --git a/lib/astutils.h b/lib/astutils.h index aecbdb829..e5899d733 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -22,12 +22,35 @@ #define astutilsH //--------------------------------------------------------------------------- -#include "symboldatabase.h" -#include "token.h" +#include +#include +class Token; +class Tokenizer; + +/** Is expression a 'signed char' if no promotion is used */ +bool astIsSignedChar(const Token *tok); /** Is expression of integral type? */ bool astIsIntegral(const Token *tok, bool unknown); /** Is expression of floating point type? */ bool astIsFloat(const Token *tok, bool unknown); +/** Is given syntax tree a variable comparison against value */ +const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr); + +bool isSameExpression(bool cpp, const Token *tok1, const Token *tok2, const std::set &constFunctions); + +/** + * Are two conditions opposite + * @param isNot do you want to know if cond1 is !cond2 or if cond1 and cond2 are non-overlapping. true: cond1==!cond2 false: cond1==true => cond2==false + * @param cpp c++ file + * @param cond1 condition1 + * @param cond2 condition2 + */ +bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const std::set &constFunctions); + +bool isConstExpression(const Token *tok, const std::set &constFunctions); + +bool isWithoutSideEffects(bool cpp, const Token* tok); + #endif // astutilsH diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index 4da844e6f..ed2278ec3 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -301,7 +301,7 @@ bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * return false; // same expressions - if (isSameExpression(_tokenizer, cond1,cond2,constFunctions)) + if (isSameExpression(_tokenizer->isCPP(), cond1,cond2,constFunctions)) return true; // bitwise overlap for example 'x&7' and 'x==1' @@ -324,7 +324,7 @@ bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * if (!num2->isNumber() || MathLib::isNegative(num2->str())) return false; - if (!isSameExpression(_tokenizer, expr1,expr2,constFunctions)) + if (!isSameExpression(_tokenizer->isCPP(), expr1,expr2,constFunctions)) return false; const MathLib::bigint value1 = MathLib::toLongNumber(num1->str()); @@ -377,54 +377,6 @@ void CheckCondition::multiConditionError(const Token *tok, unsigned int line1) // Detect oppositing inner and outer conditions //--------------------------------------------------------------------------- -bool CheckCondition::isOppositeCond(bool isNot, const Token * const cond1, const Token * const cond2, const std::set &constFunctions) const -{ - if (!cond1 || !cond2) - return false; - - if (cond1->str() == "!") { - if (cond2->str() == "!=") { - if (cond2->astOperand1() && cond2->astOperand1()->str() == "0") - return isSameExpression(_tokenizer, cond1->astOperand1(), cond2->astOperand2(), constFunctions); - if (cond2->astOperand2() && cond2->astOperand2()->str() == "0") - return isSameExpression(_tokenizer, cond1->astOperand1(), cond2->astOperand1(), constFunctions); - } - return isSameExpression(_tokenizer, cond1->astOperand1(), cond2, constFunctions); - } - - if (cond2->str() == "!") - return isOppositeCond(isNot, cond2, cond1, constFunctions); - - if (!cond1->isComparisonOp() || !cond2->isComparisonOp()) - return false; - - const std::string &comp1 = cond1->str(); - - // condition found .. get comparator - std::string comp2; - if (isSameExpression(_tokenizer, cond1->astOperand1(), cond2->astOperand1(), constFunctions) && - isSameExpression(_tokenizer, cond1->astOperand2(), cond2->astOperand2(), constFunctions)) { - comp2 = cond2->str(); - } else if (isSameExpression(_tokenizer, cond1->astOperand1(), cond2->astOperand2(), constFunctions) && - isSameExpression(_tokenizer, cond1->astOperand2(), cond2->astOperand1(), constFunctions)) { - comp2 = cond2->str(); - if (comp2[0] == '>') - comp2[0] = '<'; - else if (comp2[0] == '<') - comp2[0] = '>'; - } - - // is condition opposite? - return ((comp1 == "==" && comp2 == "!=") || - (comp1 == "!=" && comp2 == "==") || - (comp1 == "<" && comp2 == ">=") || - (comp1 == "<=" && comp2 == ">") || - (comp1 == ">" && comp2 == "<=") || - (comp1 == ">=" && comp2 == "<") || - (!isNot && ((comp1 == "<" && comp2 == ">") || - (comp1 == ">" && comp2 == "<")))); -} - void CheckCondition::oppositeInnerCondition() { if (!_settings->isEnabled("warning")) @@ -494,7 +446,7 @@ void CheckCondition::oppositeInnerCondition() const Token *cond1 = scope->classDef->next()->astOperand2(); const Token *cond2 = ifToken->next()->astOperand2(); - if (isOppositeCond(false, cond1, cond2, _settings->library.functionpure)) + if (isOppositeCond(false, _tokenizer->isCPP(), cond1, cond2, _settings->library.functionpure)) oppositeInnerConditionError(scope->classDef, cond2); } } @@ -682,7 +634,7 @@ void CheckCondition::checkIncorrectLogicOperator() // Opposite comparisons around || or && => always true or always false if ((tok->astOperand1()->isName() || tok->astOperand2()->isName()) && - isOppositeCond(true, tok->astOperand1(), tok->astOperand2(), _settings->library.functionpure)) { + isOppositeCond(true, _tokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), _settings->library.functionpure)) { const bool alwaysTrue(tok->str() == "||"); incorrectLogicOperatorError(tok, tok->expressionString(), alwaysTrue); @@ -693,7 +645,7 @@ void CheckCondition::checkIncorrectLogicOperator() // 'A && (!A || B)' is equivalent with 'A || B' if (printStyle && (tok->str() == "||") && tok->astOperand1() && tok->astOperand2() && tok->astOperand2()->str() == "&&") { const Token* tok2 = tok->astOperand2()->astOperand1(); - if (isOppositeCond(true, tok->astOperand1(), tok2, _settings->library.functionpure)) { + if (isOppositeCond(true, _tokenizer->isCPP(), tok->astOperand1(), tok2, _settings->library.functionpure)) { redundantConditionError(tok, tok2->expressionString() + ". 'A && (!A || B)' is equivalent to 'A || B'"); continue; } @@ -721,9 +673,9 @@ void CheckCondition::checkIncorrectLogicOperator() if (!parseComparison(comp2, ¬2, &op2, &value2, &expr2)) continue; - if (isSameExpression(_tokenizer, comp1, comp2, _settings->library.functionpure)) + if (isSameExpression(_tokenizer->isCPP(), comp1, comp2, _settings->library.functionpure)) continue; // same expressions => only report that there are same expressions - if (!isSameExpression(_tokenizer, expr1, expr2, _settings->library.functionpure)) + if (!isSameExpression(_tokenizer->isCPP(), expr1, expr2, _settings->library.functionpure)) continue; const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2); diff --git a/lib/checkcondition.h b/lib/checkcondition.h index 7478c8f41..815026815 100644 --- a/lib/checkcondition.h +++ b/lib/checkcondition.h @@ -100,13 +100,6 @@ public: private: bool isOverlappingCond(const Token * const cond1, const Token * const cond2, const std::set &constFunctions) const; - /** - * Are two conditions opposite - * @param isNot do you want to know if cond1 is !cond2 or if cond1 and cond2 are non-overlapping. true: cond1==!cond2 false: cond1==true => cond2==false - * @param cond1 condition1 - * @param cond2 condition2 - */ - bool isOppositeCond(bool isNot, const Token * const cond1, const Token * const cond2, const std::set &constFunctions) const; void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result); void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2); void badBitmaskCheckError(const Token *tok); diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 2cfc1a870..d591105f9 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -24,6 +24,7 @@ #include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak #include "tokenize.h" #include "symboldatabase.h" +#include "astutils.h" #include #include @@ -307,17 +308,17 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken, } const Token *vartok = nullptr; - if (Token::isVariableComparison(tok3, "!=", "0", &vartok)) { + if (astIsVariableComparison(tok3, "!=", "0", &vartok)) { varInfo2.erase(vartok->varId()); if (notzero.find(vartok->varId()) != notzero.end()) varInfo2.clear(); - } else if (Token::isVariableComparison(tok3, "==", "0", &vartok)) { + } else if (astIsVariableComparison(tok3, "==", "0", &vartok)) { varInfo1.erase(vartok->varId()); - } else if (Token::isVariableComparison(tok3, "<", "0", &vartok)) { + } else if (astIsVariableComparison(tok3, "<", "0", &vartok)) { varInfo1.erase(vartok->varId()); - } else if (Token::isVariableComparison(tok3, ">", "0", &vartok)) { + } else if (astIsVariableComparison(tok3, ">", "0", &vartok)) { varInfo2.erase(vartok->varId()); - } else if (Token::isVariableComparison(tok3, "==", "-1", &vartok)) { + } else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) { varInfo1.erase(vartok->varId()); } } diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 1b20ea9ae..3d075bfda 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -21,6 +21,7 @@ #include "symboldatabase.h" #include "mathlib.h" #include "tokenize.h" +#include "astutils.h" #include #include @@ -525,7 +526,7 @@ static bool notvar(const Token *tok, unsigned int varid) return notvar(tok->astOperand1(),varid) || notvar(tok->astOperand2(),varid); if (tok->str() == "(" && Token::Match(tok->astOperand1(), "UNLIKELY|LIKELY")) return notvar(tok->astOperand2(), varid); - const Token *vartok = Token::isVariableComparison(tok, "==", "0"); + const Token *vartok = astIsVariableComparison(tok, "==", "0"); return vartok && (vartok->varId() == varid); } @@ -539,7 +540,7 @@ static bool ifvar(const Token *tok, unsigned int varid, const std::string &comp, if (!condition || condition->str() == "&&") return false; - const Token *vartok = Token::isVariableComparison(condition, comp, rhs); + const Token *vartok = astIsVariableComparison(condition, comp, rhs); return (vartok && vartok->varId() == varid); } diff --git a/lib/checkother.cpp b/lib/checkother.cpp index c5da3c42a..52edf5241 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -34,109 +34,6 @@ namespace { } -bool isConstExpression(const Token *tok, const std::set &constFunctions) -{ - if (!tok) - return true; - if (tok->isName() && tok->next()->str() == "(") { - if (!tok->function() && !Token::Match(tok->previous(), ".|::") && constFunctions.find(tok->str()) == constFunctions.end()) - return false; - else if (tok->function() && !tok->function()->isConst()) - return false; - } - if (tok->type() == Token::eIncDecOp) - return false; - // bailout when we see ({..}) - if (tok->str() == "{") - return false; - return isConstExpression(tok->astOperand1(),constFunctions) && isConstExpression(tok->astOperand2(),constFunctions); -} - -bool isSameExpression(const Tokenizer *tokenizer, const Token *tok1, const Token *tok2, const std::set &constFunctions) -{ - if (tok1 == nullptr && tok2 == nullptr) - return true; - if (tok1 == nullptr || tok2 == nullptr) - return false; - if (tokenizer->isCPP()) { - if (tok1->str() == "." && tok1->astOperand1() && tok1->astOperand1()->str() == "this") - tok1 = tok1->astOperand2(); - if (tok2->str() == "." && tok2->astOperand1() && tok2->astOperand1()->str() == "this") - tok2 = tok2->astOperand2(); - } - if (tok1->varId() != tok2->varId() || tok1->str() != tok2->str()) { - if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) || - (Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) { - return isSameExpression(tokenizer, tok1->astOperand1(), tok2->astOperand2(), constFunctions) && - isSameExpression(tokenizer, tok1->astOperand2(), tok2->astOperand1(), constFunctions); - } - return false; - } - if (tok1->str() == "." && tok1->originalName() != tok2->originalName()) - return false; - if (tok1->isExpandedMacro() || tok2->isExpandedMacro()) - return false; - if (tok1->isName() && tok1->next()->str() == "(" && tok1->str() != "sizeof") { - if (!tok1->function() && !Token::Match(tok1->previous(), ".|::") && constFunctions.find(tok1->str()) == constFunctions.end() && !tok1->isAttributeConst() && !tok1->isAttributePure()) - return false; - else if (tok1->function() && !tok1->function()->isConst() && !tok1->function()->isAttributeConst() && !tok1->function()->isAttributePure()) - return false; - } - // templates/casts - if ((Token::Match(tok1, "%name% <") && tok1->next()->link()) || - (Token::Match(tok2, "%name% <") && tok2->next()->link())) { - - // non-const template function that is not a dynamic_cast => return false - if (Token::simpleMatch(tok1->next()->link(), "> (") && - !(tok1->function() && tok1->function()->isConst()) && - tok1->str() != "dynamic_cast") - return false; - - // some template/cast stuff.. check that the template arguments are same - const Token *t1 = tok1->next(); - const Token *t2 = tok2->next(); - const Token *end1 = t1->link(); - const Token *end2 = t2->link(); - while (t1 && t2 && t1 != end1 && t2 != end2) { - if (t1->str() != t2->str()) - return false; - t1 = t1->next(); - t2 = t2->next(); - } - if (t1 != end1 || t2 != end2) - return false; - } - if (tok1->type() == Token::eIncDecOp || tok1->isAssignmentOp()) - return false; - // bailout when we see ({..}) - if (tok1->str() == "{") - return false; - if (tok1->str() == "(" && tok1->previous() && !tok1->previous()->isName()) { // cast => assert that the casts are equal - const Token *t1 = tok1->next(); - const Token *t2 = tok2->next(); - while (t1 && t2 && t1->str() == t2->str() && (t1->isName() || t1->str() == "*")) { - t1 = t1->next(); - t2 = t2->next(); - } - if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") - return false; - } - bool noncommuative_equals = - isSameExpression(tokenizer, tok1->astOperand1(), tok2->astOperand1(), constFunctions); - noncommuative_equals = noncommuative_equals && - isSameExpression(tokenizer, tok1->astOperand2(), tok2->astOperand2(), constFunctions); - - if (noncommuative_equals) - return true; - - const bool commutative = tok1->astOperand1() && tok1->astOperand2() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); - bool commuative_equals = commutative && - isSameExpression(tokenizer, tok1->astOperand2(), tok2->astOperand1(), constFunctions); - commuative_equals = commuative_equals && - isSameExpression(tokenizer, tok1->astOperand1(), tok2->astOperand2(), constFunctions); - - return commuative_equals; -} //---------------------------------------------------------------------------------- // The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. @@ -1572,31 +1469,6 @@ void CheckOther::passedByValueError(const Token *tok, const std::string &parname //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- -static bool isChar(const Variable* var) -{ - return (var && !var->isPointer() && !var->isArray() && var->typeStartToken()->str() == "char"); -} - -static bool isSignedChar(const Variable* var) -{ - return (isChar(var) && !var->typeStartToken()->isUnsigned()); -} - -static bool astIsSignedChar(const Token *tok) -{ - if (!tok) - return false; - if (tok->str() == "*" && tok->astOperand1() && !tok->astOperand2()) { - const Variable *var = tok->astOperand1()->variable(); - if (!var || !var->isPointer()) - return false; - const Token *type = var->typeStartToken(); - while (type && type->str() == "const") - type = type->next(); - return (type && type->str() == "char" && !type->isUnsigned()); - } - return isSignedChar(tok->variable()); -} void CheckOther::checkCharVariable() { @@ -2134,20 +2006,6 @@ namespace { } } -bool isWithoutSideEffects(const Tokenizer *tokenizer, const Token* tok) -{ - if (!tokenizer->isCPP()) - return true; - - while (tok && tok->astOperand2() && tok->astOperand2()->str() != "(") - tok = tok->astOperand2(); - if (tok && tok->varId()) { - const Variable* var = tok->variable(); - return var && (!var->isClass() || var->isPointer() || var->isStlType()); - } - return true; -} - void CheckOther::checkDuplicateExpression() { if (!_settings->isEnabled("style")) @@ -2170,8 +2028,8 @@ void CheckOther::checkDuplicateExpression() if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>")) { if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true)) continue; - if (isSameExpression(_tokenizer, tok->astOperand1(), tok->astOperand2(), _settings->library.functionpure)) { - if (isWithoutSideEffects(_tokenizer, tok->astOperand1())) { + if (isSameExpression(_tokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), _settings->library.functionpure)) { + if (isWithoutSideEffects(_tokenizer->isCPP(), tok->astOperand1())) { const bool assignment = tok->str() == "="; if (assignment) selfAssignmentError(tok, tok->astOperand1()->expressionString()); @@ -2189,16 +2047,16 @@ void CheckOther::checkDuplicateExpression() } } } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative - if (tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(_tokenizer, tok->astOperand2(), tok->astOperand1()->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer, tok->astOperand2())) + if (tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(_tokenizer->isCPP(), tok->astOperand2(), tok->astOperand1()->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer->isCPP(), tok->astOperand2())) duplicateExpressionError(tok->astOperand2(), tok->astOperand2(), tok->str()); else if (tok->astOperand2()) { const Token *ast1 = tok->astOperand1(); while (ast1 && tok->str() == ast1->str()) { - if (isSameExpression(_tokenizer, ast1->astOperand1(), tok->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer, ast1->astOperand1())) + if (isSameExpression(_tokenizer->isCPP(), ast1->astOperand1(), tok->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer->isCPP(), ast1->astOperand1())) // TODO: warn if variables are unchanged. See #5683 // Probably the message should be changed to 'duplicate expressions X in condition or something like that'. ;//duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok->str()); - else if (isSameExpression(_tokenizer, ast1->astOperand2(), tok->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer, ast1->astOperand2())) + else if (isSameExpression(_tokenizer->isCPP(), ast1->astOperand2(), tok->astOperand2(), _settings->library.functionpure) && isWithoutSideEffects(_tokenizer->isCPP(), ast1->astOperand2())) duplicateExpressionError(ast1->astOperand2(), tok->astOperand2(), tok->str()); if (!isConstExpression(ast1->astOperand2(), _settings->library.functionpure)) break; @@ -2207,7 +2065,7 @@ void CheckOther::checkDuplicateExpression() } } } else if (tok->astOperand1() && tok->astOperand2() && tok->str() == ":" && tok->astParent() && tok->astParent()->str() == "?") { - if (isSameExpression(_tokenizer, tok->astOperand1(), tok->astOperand2(), temp)) + if (isSameExpression(_tokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), temp)) duplicateExpressionTernaryError(tok); } } diff --git a/lib/checkother.h b/lib/checkother.h index 6fe208a52..1dce07750 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -28,13 +28,6 @@ class Function; class Variable; -bool isConstExpression(const Token *tok, const std::set &constFunctions); - -/** Is expressions same? */ -bool isSameExpression(const Tokenizer *tokenizer, const Token *tok1, const Token *tok2, const std::set &constFunctions); - -bool isWithoutSideEffects(const Tokenizer *tokenizer, const Token* tok); - /// @addtogroup Checks /// @{ diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 0c2ad6560..a20a86d4d 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -19,6 +19,7 @@ //--------------------------------------------------------------------------- #include "checkuninitvar.h" +#include "astutils.h" #include "mathlib.h" #include "checknullpointer.h" // CheckNullPointer::parseFunctionCall #include "symboldatabase.h" @@ -280,7 +281,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var unsigned int condVarId = 0, condVarValue = 0; const Token *condVarTok = nullptr; if (Token::simpleMatch(tok, "if (") && - Token::isVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) { + astIsVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) { std::map::const_iterator it = variableValue.find(condVarTok->varId()); if (it != variableValue.end() && it->second == NOT_ZERO) return true; // this scope is not fully analysed => return true diff --git a/lib/token.cpp b/lib/token.cpp index 0257ba688..b4524ded7 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1468,42 +1468,6 @@ const Token *Token::getValueTokenDeadPointer() const return nullptr; } - -const Token * Token::isVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok) -{ - if (!tok) - return nullptr; - - const Token *ret = nullptr; - if (tok->isComparisonOp()) { - if (tok->astOperand1() && tok->astOperand1()->str() == rhs) { - // Invert comparator - std::string s = tok->str(); - if (s[0] == '>') - s[0] = '<'; - else if (s[0] == '<') - s[0] = '>'; - if (s == comp) { - ret = tok->astOperand2(); - } - } else if (tok->str() == comp && tok->astOperand2() && tok->astOperand2()->str() == rhs) { - ret = tok->astOperand1(); - } - } else if (comp == "!=" && rhs == std::string("0")) { - ret = tok; - } else if (comp == "==" && rhs == std::string("0")) { - if (tok->str() == "!") - ret = tok->astOperand1(); - } - while (ret && ret->str() == ".") - ret = ret->astOperand2(); - if (ret && ret->varId() == 0U) - ret = nullptr; - if (vartok) - *vartok = ret; - return ret; -} - void Token::assignProgressValues(Token *tok) { unsigned int total_count = 0; diff --git a/lib/token.h b/lib/token.h index 16683db2c..34b295b50 100644 --- a/lib/token.h +++ b/lib/token.h @@ -717,9 +717,6 @@ public: const Token *getValueTokenDeadPointer() const; - /** Is given syntax tree a variable comparison against value */ - static const Token * isVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr); - private: void next(Token *nextToken) {