SymbolDatabase: Add type information in AST
This commit is contained in:
parent
3bc21b8138
commit
9b253612ca
|
@ -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<Token *>(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;
|
||||
}
|
||||
|
|
|
@ -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<Type> _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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1726,6 +1726,7 @@ bool Tokenizer::tokenize(std::istream &code,
|
|||
}
|
||||
|
||||
list.createAst();
|
||||
SymbolDatabase::setValueTypeInTokenList(list.front());
|
||||
ValueFlow::setValues(&list, _symbolDatabase, _errorLogger, _settings);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue