diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index ccca5027c..67cf97b7f 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -3649,3 +3649,168 @@ bool SymbolDatabase::isReservedName(const std::string& iName) const { return (c_keywords.find(iName) != c_keywords.cend()) || (isCPP() && (cpp_keywords.find(iName) != cpp_keywords.cend())); } + +static void setValueType(Token *tok, ValueType::Sign sign, ValueType::Type type, unsigned int pointer) +{ + tok->setValueType(new ValueType(sign,type,pointer)); + Token *parent = const_cast(tok->astParent()); + if (!parent || parent->valueType()) + return; + if (!parent->astOperand1() || !parent->astOperand1()->valueType()) + return; + + if (parent->str() == "[" && pointer > 0U) { + setValueType(parent, sign, type, pointer - 1U); + return; + } + if (parent->str() == "*" && !parent->astOperand2() && pointer > 0U) { + setValueType(parent, sign, type, pointer - 1U); + return; + } + + if (parent->astOperand2() && !parent->astOperand2()->valueType()) + return; + const ValueType *vt1 = parent->astOperand1()->valueType(); + const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr; + if (parent->isArithmeticalOp() && vt2) { + if (vt1->pointer != 0U && vt2->pointer == 0U) { + setValueType(parent, vt1->sign, vt1->type, vt1->pointer); + return; + } + + if (vt1->pointer == 0U && vt2->pointer != 0U) { + setValueType(parent, vt2->sign, vt2->type, vt2->pointer); + return; + } + + if (vt1->pointer != 0U) { // result is pointer diff + setValueType(parent, ValueType::Sign::UNSIGNED, ValueType::Type::INT, 0U); + return; + } + + if (vt1->type == ValueType::Type::DOUBLE || vt2->type == ValueType::Type::DOUBLE) { + setValueType(parent, ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U); + return; + } + if (vt1->type == ValueType::Type::FLOAT || vt2->type == ValueType::Type::FLOAT) { + setValueType(parent, ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U); + return; + } + if (vt1->isIntegral() && vt2->isIntegral()) { + ValueType::Type t = (vt1->type > vt2->type) ? vt1->type : vt2->type; + setValueType(parent, ValueType::Sign::UNKNOWN_SIGN, t, 0U); + return; + } + } +} + +void SymbolDatabase::setValueTypeInTokenList(Token *tokens) +{ + for (Token *tok = tokens; tok; tok = tok->next()) + tok->setValueType(nullptr); + + for (Token *tok = tokens; tok; tok = tok->next()) { + if (tok->isNumber()) { + if (MathLib::isFloat(tok->str())) + ::setValueType(tok, ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U); + if (MathLib::isInt(tok->str())) + ::setValueType(tok, (tok->str()[0] == '-') ? ValueType::Sign::SIGNED : ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::INT, 0U); + } else if (tok->isComparisonOp()) + ::setValueType(tok, ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U); + else if (tok->tokType() == Token::eChar) + ::setValueType(tok, ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 0U); + else if (tok->tokType() == Token::eString) + ::setValueType(tok, ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 1U); + else if (tok->str() == "(") { + // cast + if (!tok->astOperand2() && !Token::Match(tok, "( %name%")) { + ValueType::Sign s = ValueType::Sign::UNKNOWN_SIGN; + ValueType::Type t = ValueType::Type::UNKNOWN_TYPE; + unsigned int p = 0; + const Token *tok2; + for (tok2 = tok->next(); Token::Match(tok2, "%name%|*"); tok2 = tok2->next()) { + if (tok2->str() == "signed") + s = ValueType::Sign::SIGNED; + else if (tok2->str() == "unsigned") + s = ValueType::Sign::UNSIGNED; + else if (tok2->str() == "bool") + t = ValueType::Type::BOOL; + else if (tok2->str() == "char") + t = ValueType::Type::CHAR; + else if (tok2->str() == "short") + t = ValueType::Type::SHORT; + else if (tok2->str() == "int") + t = ValueType::Type::INT; + else if (tok2->str() == "long") + t = ValueType::Type::LONG; + else if (tok2->str() == "struct") + t = ValueType::Type::NONSTD; + else if (tok2->str() == "*") + p++; + } + if (tok2 && tok2->str() == ")" && t != ValueType::Type::UNKNOWN_TYPE) + ::setValueType(tok, s, t, p); + } + } else if (tok->variable()) { + const Variable *var = tok->variable(); + ValueType::Sign sign = ValueType::Sign::UNKNOWN_SIGN; + ValueType::Type type = ValueType::Type::UNKNOWN_TYPE; + unsigned int p = var->dimensions().size(); + for (const Token *typeTok = var->typeStartToken(); Token::Match(typeTok, "%name%|*|&"); typeTok = typeTok->next()) { + if (typeTok->isUnsigned()) + sign = ValueType::Sign::UNSIGNED; + else if (typeTok->isSigned()) + sign = ValueType::Sign::SIGNED; + if (typeTok->isStandardType()) { + if (typeTok->str() == "bool") + type = ValueType::Type::BOOL; + else if (typeTok->str() == "char") + type = ValueType::Type::CHAR; + else if (typeTok->str() == "short") + type = ValueType::Type::SHORT; + else if (typeTok->str() == "int") + type = ValueType::Type::INT; + else if (typeTok->str() == "long") + type = ValueType::Type::LONG; + else if (typeTok->str() == "float") + type = ValueType::Type::FLOAT; + else if (typeTok->str() == "double") + type = ValueType::Type::DOUBLE; + } + if (typeTok->str() == "*") + p++; + } + if (type >= ValueType::Type::BOOL) + ::setValueType(tok, sign, type, p); + } + } +} + +std::string ValueType::str() const +{ + std::string ret; + if (isIntegral()) { + if (sign == SIGNED) + ret = "signed "; + else if (sign == UNSIGNED) + ret = "unsigned "; + if (type == BOOL) + ret += "bool"; + else if (type == CHAR) + ret += "char"; + else if (type == SHORT) + ret += "short"; + else if (type == INT) + ret += "int"; + else if (type == LONG) + ret += "long"; + else if (type == LONGLONG) + ret += "long long"; + } else if (type == FLOAT) + ret = "float"; + else if (type == DOUBLE) + ret = "double"; + for (int p = 0; p < pointer; p++) + ret += "*"; + return ret; +} diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index f38858839..e867bd349 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1008,6 +1008,9 @@ public: bool isCPP() const; + /** Set valuetype in provided tokenlist */ + static void setValueTypeInTokenList(Token *tokens); + private: friend class Scope; friend class Function; @@ -1024,7 +1027,6 @@ private: /** Whether iName is a keyword as defined in http://en.cppreference.com/w/c/keyword and http://en.cppreference.com/w/cpp/keyword*/ bool isReservedName(const std::string& iName) const; - const Tokenizer *_tokenizer; const Settings *_settings; ErrorLogger *_errorLogger; @@ -1036,5 +1038,22 @@ private: std::list _blankTypes; }; +/** Value type */ +class ValueType { +public: + enum Sign {UNKNOWN_SIGN, SIGNED, UNSIGNED} sign; + enum Type {UNKNOWN_TYPE, NONSTD, BOOL, CHAR, SHORT, INT, LONG, LONGLONG, FLOAT, DOUBLE} type; + unsigned int pointer; // 0=>not pointer, 1=>*, 2=>**, 3=>***, etc + + ValueType(enum Sign s, enum Type t, unsigned int p) : sign(s), type(t), pointer(p) {} + + bool isIntegral() const { + return (type >= ValueType::Type::BOOL && type <= ValueType::Type::LONGLONG); + } + + std::string str() const; +}; + + //--------------------------------------------------------------------------- #endif // symboldatabaseH diff --git a/lib/token.cpp b/lib/token.cpp index acb7c25cd..c7bcc6ff7 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -49,13 +49,15 @@ Token::Token(Token **t) : _astOperand1(nullptr), _astOperand2(nullptr), _astParent(nullptr), - _originalName(nullptr) + _originalName(nullptr), + valuetype(nullptr) { } Token::~Token() { delete _originalName; + delete valuetype; } void Token::update_property_info() @@ -1508,3 +1510,12 @@ void Token::assignProgressValues(Token *tok) for (Token *tok2 = tok; tok2; tok2 = tok2->next()) tok2->_progressValue = count++ * 100 / total_count; } + +void Token::setValueType(ValueType *vt) +{ + if (vt != valuetype) { + delete valuetype; + valuetype = vt; + } +} + diff --git a/lib/token.h b/lib/token.h index 69867f1ea..71c3975db 100644 --- a/lib/token.h +++ b/lib/token.h @@ -33,6 +33,7 @@ class Scope; class Type; class Function; class Variable; +class ValueType; class Settings; /// @addtogroup Core @@ -223,6 +224,11 @@ public: **/ static std::string getCharAt(const Token *tok, std::size_t index); + const ValueType *valueType() const { + return valuetype; + } + void setValueType(ValueType *vt); + Token::Type tokType() const { return _tokType; } @@ -847,6 +853,9 @@ private: // original name like size_t std::string* _originalName; + // ValueType + ValueType *valuetype; + public: void astOperand1(Token *tok); void astOperand2(Token *tok); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index c58f39e2c..4124751fb 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1726,6 +1726,7 @@ bool Tokenizer::tokenize(std::istream &code, } list.createAst(); + SymbolDatabase::setValueTypeInTokenList(list.front()); ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings); } diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index a1235d3c0..29cd0eaa1 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -262,6 +262,8 @@ private: TEST_CASE(lambda); // ticket #5867 TEST_CASE(circularDependencies); // 6298 + + TEST_CASE(valuetype); } void array() const { @@ -2943,6 +2945,37 @@ private: " c.f();\n" "}"); } + + std::string typeOf(const char code[], const char str[]) { + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + tokenizer.getSymbolDatabase(); + const Token *tok = Token::findsimplematch(tokenizer.tokens(),str); + return tok->valueType()->str(); + } + + void valuetype() { + // Constant calculations + ASSERT_EQUALS("int", typeOf("1 + 2", "+")); + //ASSERT_EQUALS("long", typeOf("1L + 2", "+")); + //ASSERT_EQUALS("long long", typeOf("1LL + 2", "+")); + ASSERT_EQUALS("float", typeOf("1.2f + 3", "+")); + ASSERT_EQUALS("float", typeOf("1 + 2.3f", "+")); + + // char * + ASSERT_EQUALS("char*", typeOf("\"hello\" + 1", "+")); + ASSERT_EQUALS("char", typeOf("\"hello\"[1]", "[")); + ASSERT_EQUALS("char", typeOf("*\"hello\"", "*")); + + // Variable calculations + ASSERT_EQUALS("int", typeOf("int x; a = x + 1;", "+")); + ASSERT_EQUALS("float", typeOf("float x; a = x + 1;", "+")); + + // array.. + ASSERT_EQUALS("int*", typeOf("int x[10]; a = x + 1;", "+")); + ASSERT_EQUALS("int", typeOf("int x[10]; a = x[0] + 1;", "+")); + } }; REGISTER_TEST(TestSymbolDatabase)