Symboldatabase: Improve valuetypes for containers, iterators, and smart pointers (#3398)
This commit is contained in:
parent
f946bbc249
commit
fdaeaacc40
|
@ -73,6 +73,7 @@ static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn
|
||||||
return true;
|
return true;
|
||||||
return defaultReturn;
|
return defaultReturn;
|
||||||
|
|
||||||
|
case ValueType::Type::SMART_POINTER:
|
||||||
case ValueType::Type::CONTAINER:
|
case ValueType::Type::CONTAINER:
|
||||||
case ValueType::Type::ITERATOR:
|
case ValueType::Type::ITERATOR:
|
||||||
case ValueType::Type::VOID:
|
case ValueType::Type::VOID:
|
||||||
|
|
|
@ -500,6 +500,7 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
|
||||||
if (!className)
|
if (!className)
|
||||||
return Error(ErrorCode::MISSING_ATTRIBUTE, "class-name");
|
return Error(ErrorCode::MISSING_ATTRIBUTE, "class-name");
|
||||||
SmartPointer& smartPointer = smartPointers[className];
|
SmartPointer& smartPointer = smartPointers[className];
|
||||||
|
smartPointer.name = className;
|
||||||
for (const tinyxml2::XMLElement* smartPointerNode = node->FirstChildElement(); smartPointerNode;
|
for (const tinyxml2::XMLElement* smartPointerNode = node->FirstChildElement(); smartPointerNode;
|
||||||
smartPointerNode = smartPointerNode->NextSiblingElement()) {
|
smartPointerNode = smartPointerNode->NextSiblingElement()) {
|
||||||
const std::string smartPointerNodeName = smartPointerNode->Name();
|
const std::string smartPointerNodeName = smartPointerNode->Name();
|
||||||
|
|
|
@ -431,6 +431,7 @@ public:
|
||||||
std::vector<std::string> defines; // to provide some library defines
|
std::vector<std::string> defines; // to provide some library defines
|
||||||
|
|
||||||
struct SmartPointer {
|
struct SmartPointer {
|
||||||
|
std::string name = "";
|
||||||
bool unique = false;
|
bool unique = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5717,6 +5717,17 @@ static void setAutoTokenProperties(Token * const autoTok)
|
||||||
autoTok->isStandardType(true);
|
autoTok->isStandardType(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isContainerYieldElement(Library::Container::Yield yield)
|
||||||
|
{
|
||||||
|
return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX ||
|
||||||
|
yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isContainerYieldPointer(Library::Container::Yield yield)
|
||||||
|
{
|
||||||
|
return yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT;
|
||||||
|
}
|
||||||
|
|
||||||
void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype)
|
void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype)
|
||||||
{
|
{
|
||||||
tok->setValueType(new ValueType(valuetype));
|
tok->setValueType(new ValueType(valuetype));
|
||||||
|
@ -5747,10 +5758,18 @@ void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vt1 && vt1->container && vt1->containerTypeToken && Token::Match(parent, ". %name% (") && vt1->container->getYield(parent->next()->str()) == Library::Container::Yield::ITEM) {
|
if (vt1 && vt1->container && vt1->containerTypeToken && Token::Match(parent, ". %name% (") &&
|
||||||
|
isContainerYieldElement(vt1->container->getYield(parent->next()->str()))) {
|
||||||
ValueType item;
|
ValueType item;
|
||||||
if (parsedecl(vt1->containerTypeToken, &item, mDefaultSignedness, mSettings))
|
if (parsedecl(vt1->containerTypeToken, &item, mDefaultSignedness, mSettings)) {
|
||||||
|
if (item.constness == 0)
|
||||||
|
item.constness = vt1->constness;
|
||||||
|
if (isContainerYieldPointer(vt1->container->getYield(parent->next()->str())))
|
||||||
|
item.pointer += 1;
|
||||||
|
else
|
||||||
|
item.reference = Reference::LValue;
|
||||||
setValueType(parent->tokAt(2), item);
|
setValueType(parent->tokAt(2), item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vt1 && vt1->smartPointerType && Token::Match(parent, ". %name% (") && parent->originalName() == "->" && !parent->next()->function()) {
|
if (vt1 && vt1->smartPointerType && Token::Match(parent, ". %name% (") && parent->originalName() == "->" && !parent->next()->function()) {
|
||||||
|
@ -5840,6 +5859,29 @@ void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype)
|
||||||
setValueType(parent, vt);
|
setValueType(parent, vt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Dereference iterator
|
||||||
|
if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::ITERATOR &&
|
||||||
|
valuetype.containerTypeToken) {
|
||||||
|
ValueType vt;
|
||||||
|
if (parsedecl(valuetype.containerTypeToken, &vt, mDefaultSignedness, mSettings)) {
|
||||||
|
if (vt.constness == 0)
|
||||||
|
vt.constness = valuetype.constness;
|
||||||
|
vt.reference = Reference::LValue;
|
||||||
|
setValueType(parent, vt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Dereference smart pointer
|
||||||
|
if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::SMART_POINTER &&
|
||||||
|
valuetype.smartPointerTypeToken) {
|
||||||
|
ValueType vt;
|
||||||
|
if (parsedecl(valuetype.smartPointerTypeToken, &vt, mDefaultSignedness, mSettings)) {
|
||||||
|
if (vt.constness == 0)
|
||||||
|
vt.constness = valuetype.constness;
|
||||||
|
setValueType(parent, vt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (parent->str() == "*" && Token::simpleMatch(parent->astOperand2(), "[") && valuetype.pointer > 0U) {
|
if (parent->str() == "*" && Token::simpleMatch(parent->astOperand2(), "[") && valuetype.pointer > 0U) {
|
||||||
const Token *op1 = parent->astOperand2()->astOperand1();
|
const Token *op1 = parent->astOperand2()->astOperand1();
|
||||||
while (op1 && op1->str() == "[")
|
while (op1 && op1->str() == "[")
|
||||||
|
@ -6095,7 +6137,7 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V
|
||||||
if (!valuetype->typeScope && !valuetype->smartPointerType)
|
if (!valuetype->typeScope && !valuetype->smartPointerType)
|
||||||
valuetype->type = ValueType::Type::UNKNOWN_TYPE;
|
valuetype->type = ValueType::Type::UNKNOWN_TYPE;
|
||||||
else if (valuetype->smartPointerType)
|
else if (valuetype->smartPointerType)
|
||||||
valuetype->type = ValueType::Type::NONSTD;
|
valuetype->type = ValueType::Type::SMART_POINTER;
|
||||||
else if (valuetype->typeScope->type == Scope::eEnum) {
|
else if (valuetype->typeScope->type == Scope::eEnum) {
|
||||||
const Token * enum_type = valuetype->typeScope->enumType;
|
const Token * enum_type = valuetype->typeScope->enumType;
|
||||||
if (enum_type) {
|
if (enum_type) {
|
||||||
|
@ -6193,7 +6235,7 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V
|
||||||
valuetype->smartPointer = smartPointer;
|
valuetype->smartPointer = smartPointer;
|
||||||
valuetype->smartPointerTypeToken = argTok->next();
|
valuetype->smartPointerTypeToken = argTok->next();
|
||||||
valuetype->smartPointerType = argTok->next()->type();
|
valuetype->smartPointerType = argTok->next()->type();
|
||||||
valuetype->type = ValueType::Type::NONSTD;
|
valuetype->type = ValueType::Type::SMART_POINTER;
|
||||||
type = argTok->link();
|
type = argTok->link();
|
||||||
if (type)
|
if (type)
|
||||||
type = type->next();
|
type = type->next();
|
||||||
|
@ -6478,6 +6520,13 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
|
||||||
setValueType(tok, vt);
|
setValueType(tok, vt);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (const Library::SmartPointer* sp = mSettings->library.detectSmartPointer(typeStartToken)) {
|
||||||
|
ValueType vt;
|
||||||
|
vt.type = ValueType::Type::SMART_POINTER;
|
||||||
|
vt.smartPointer = sp;
|
||||||
|
setValueType(tok, vt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string e = tok->astOperand1()->expressionString();
|
const std::string e = tok->astOperand1()->expressionString();
|
||||||
|
|
||||||
|
@ -6487,9 +6536,15 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
|
||||||
if (vt.typeScope) {
|
if (vt.typeScope) {
|
||||||
vt.smartPointerType = vt.typeScope->definedType;
|
vt.smartPointerType = vt.typeScope->definedType;
|
||||||
vt.typeScope = nullptr;
|
vt.typeScope = nullptr;
|
||||||
setValueType(tok, vt);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
if (e == "std::make_shared" && mSettings->library.smartPointers.count("std::shared_ptr") > 0)
|
||||||
|
vt.smartPointer = &mSettings->library.smartPointers.at("std::shared_ptr");
|
||||||
|
if (e == "std::make_unique" && mSettings->library.smartPointers.count("std::unique_ptr") > 0)
|
||||||
|
vt.smartPointer = &mSettings->library.smartPointers.at("std::unique_ptr");
|
||||||
|
vt.type = ValueType::Type::SMART_POINTER;
|
||||||
|
vt.smartPointerTypeToken = tok->astOperand1()->tokAt(3);
|
||||||
|
setValueType(tok, vt);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueType podtype;
|
ValueType podtype;
|
||||||
|
@ -6527,6 +6582,8 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
|
||||||
ValueType vt;
|
ValueType vt;
|
||||||
vt.type = ValueType::Type::ITERATOR;
|
vt.type = ValueType::Type::ITERATOR;
|
||||||
vt.container = cont;
|
vt.container = cont;
|
||||||
|
vt.containerTypeToken =
|
||||||
|
tok->astOperand1()->astOperand1()->valueType()->containerTypeToken;
|
||||||
setValueType(tok, vt);
|
setValueType(tok, vt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6740,6 +6797,9 @@ std::string ValueType::dump() const
|
||||||
case RECORD:
|
case RECORD:
|
||||||
ret << "valueType-type=\"record\"";
|
ret << "valueType-type=\"record\"";
|
||||||
break;
|
break;
|
||||||
|
case SMART_POINTER:
|
||||||
|
ret << "valueType-type=\"smart-pointer\"";
|
||||||
|
break;
|
||||||
case CONTAINER:
|
case CONTAINER:
|
||||||
ret << "valueType-type=\"container\"";
|
ret << "valueType-type=\"container\"";
|
||||||
break;
|
break;
|
||||||
|
@ -6904,8 +6964,8 @@ std::string ValueType::str() const
|
||||||
ret += " container(" + container->startPattern + ')';
|
ret += " container(" + container->startPattern + ')';
|
||||||
} else if (type == ValueType::Type::ITERATOR && container) {
|
} else if (type == ValueType::Type::ITERATOR && container) {
|
||||||
ret += " iterator(" + container->startPattern + ')';
|
ret += " iterator(" + container->startPattern + ')';
|
||||||
} else if (smartPointerType) {
|
} else if (type == ValueType::Type::SMART_POINTER && smartPointer) {
|
||||||
ret += " smart-pointer<" + smartPointerType->name() + ">";
|
ret += " smart-pointer(" + smartPointer->name + ")";
|
||||||
}
|
}
|
||||||
for (unsigned int p = 0; p < pointer; p++) {
|
for (unsigned int p = 0; p < pointer; p++) {
|
||||||
ret += " *";
|
ret += " *";
|
||||||
|
|
|
@ -1217,7 +1217,26 @@ enum class Reference {
|
||||||
class CPPCHECKLIB ValueType {
|
class CPPCHECKLIB ValueType {
|
||||||
public:
|
public:
|
||||||
enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign;
|
enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign;
|
||||||
enum Type { UNKNOWN_TYPE, NONSTD, RECORD, CONTAINER, ITERATOR, VOID, BOOL, CHAR, SHORT, WCHAR_T, INT, LONG, LONGLONG, UNKNOWN_INT, FLOAT, DOUBLE, LONGDOUBLE } type;
|
enum Type {
|
||||||
|
UNKNOWN_TYPE,
|
||||||
|
NONSTD,
|
||||||
|
RECORD,
|
||||||
|
SMART_POINTER,
|
||||||
|
CONTAINER,
|
||||||
|
ITERATOR,
|
||||||
|
VOID,
|
||||||
|
BOOL,
|
||||||
|
CHAR,
|
||||||
|
SHORT,
|
||||||
|
WCHAR_T,
|
||||||
|
INT,
|
||||||
|
LONG,
|
||||||
|
LONGLONG,
|
||||||
|
UNKNOWN_INT,
|
||||||
|
FLOAT,
|
||||||
|
DOUBLE,
|
||||||
|
LONGDOUBLE
|
||||||
|
} type;
|
||||||
nonneg int bits; ///< bitfield bitcount
|
nonneg int bits; ///< bitfield bitcount
|
||||||
nonneg int pointer; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc
|
nonneg int pointer; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc
|
||||||
nonneg int constness; ///< bit 0=data, bit 1=*, bit 2=**
|
nonneg int constness; ///< bit 0=data, bit 1=*, bit 2=**
|
||||||
|
|
|
@ -2058,7 +2058,9 @@ private:
|
||||||
" auto p = x.data();\n"
|
" auto p = x.data();\n"
|
||||||
" return p;\n"
|
" return p;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str());
|
ASSERT_EQUALS(
|
||||||
|
"[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
check("auto f() {\n"
|
check("auto f() {\n"
|
||||||
" std::vector<int> x;\n"
|
" std::vector<int> x;\n"
|
||||||
|
|
|
@ -7438,7 +7438,12 @@ private:
|
||||||
vector.startPattern2 = "Vector !!::";
|
vector.startPattern2 = "Vector !!::";
|
||||||
vector.type_templateArgNo = 0;
|
vector.type_templateArgNo = 0;
|
||||||
vector.arrayLike_indexOp = true;
|
vector.arrayLike_indexOp = true;
|
||||||
vector.functions["front"] = Library::Container::Function{Library::Container::Action::NO_ACTION, Library::Container::Yield::ITEM};
|
vector.functions["front"] =
|
||||||
|
Library::Container::Function{Library::Container::Action::NO_ACTION, Library::Container::Yield::ITEM};
|
||||||
|
vector.functions["data"] =
|
||||||
|
Library::Container::Function{Library::Container::Action::NO_ACTION, Library::Container::Yield::BUFFER};
|
||||||
|
vector.functions["begin"] = Library::Container::Function{Library::Container::Action::NO_ACTION,
|
||||||
|
Library::Container::Yield::START_ITERATOR};
|
||||||
set.library.containers["Vector"] = vector;
|
set.library.containers["Vector"] = vector;
|
||||||
Library::Container string;
|
Library::Container string;
|
||||||
string.startPattern = "test :: string";
|
string.startPattern = "test :: string";
|
||||||
|
@ -7447,13 +7452,29 @@ private:
|
||||||
set.library.containers["test::string"] = string;
|
set.library.containers["test::string"] = string;
|
||||||
ASSERT_EQUALS("signed int", typeOf("Vector<int> v; v[0]=3;", "[", "test.cpp", &set));
|
ASSERT_EQUALS("signed int", typeOf("Vector<int> v; v[0]=3;", "[", "test.cpp", &set));
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("{return test::string();}", "(", "test.cpp", &set));
|
ASSERT_EQUALS("container(test :: string)", typeOf("{return test::string();}", "(", "test.cpp", &set));
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("void foo(Vector<test::string> v) { for (auto s: v) { x=s+s; } }", "s", "test.cpp", &set));
|
ASSERT_EQUALS(
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("void foo(Vector<test::string> v) { for (auto s: v) { x=s+s; } }", "+", "test.cpp", &set));
|
"container(test :: string)",
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("Vector<test::string> v; x = v.front();", "(", "test.cpp", &set));
|
typeOf("void foo(Vector<test::string> v) { for (auto s: v) { x=s+s; } }", "s", "test.cpp", &set));
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("void foo(){test::string s; return \"x\"+s;}", "+", "test.cpp", &set));
|
ASSERT_EQUALS(
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("void foo(){test::string s; return s+\"x\";}", "+", "test.cpp", &set));
|
"container(test :: string)",
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("void foo(){test::string s; return 'x'+s;}", "+", "test.cpp", &set));
|
typeOf("void foo(Vector<test::string> v) { for (auto s: v) { x=s+s; } }", "+", "test.cpp", &set));
|
||||||
ASSERT_EQUALS("container(test :: string)", typeOf("void foo(){test::string s; return s+'x';}", "+", "test.cpp", &set));
|
ASSERT_EQUALS("container(test :: string) &",
|
||||||
|
typeOf("Vector<test::string> v; x = v.front();", "(", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("container(test :: string) *",
|
||||||
|
typeOf("Vector<test::string> v; x = v.data();", "(", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("signed int &", typeOf("Vector<int> v; x = v.front();", "(", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("signed int *", typeOf("Vector<int> v; x = v.data();", "(", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("signed int * *", typeOf("Vector<int*> v; x = v.data();", "(", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("iterator(Vector <)", typeOf("Vector<int> v; x = v.begin();", "(", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("signed int &", typeOf("Vector<int> v; x = *v.begin();", "*", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("container(test :: string)",
|
||||||
|
typeOf("void foo(){test::string s; return \"x\"+s;}", "+", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("container(test :: string)",
|
||||||
|
typeOf("void foo(){test::string s; return s+\"x\";}", "+", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("container(test :: string)",
|
||||||
|
typeOf("void foo(){test::string s; return 'x'+s;}", "+", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("container(test :: string)",
|
||||||
|
typeOf("void foo(){test::string s; return s+'x';}", "+", "test.cpp", &set));
|
||||||
}
|
}
|
||||||
|
|
||||||
// new
|
// new
|
||||||
|
@ -7474,7 +7495,14 @@ private:
|
||||||
ASSERT_EQUALS("", typeOf("; int x[10] = { [3]=1 };", "[ 3 ]"));
|
ASSERT_EQUALS("", typeOf("; int x[10] = { [3]=1 };", "[ 3 ]"));
|
||||||
|
|
||||||
// std::make_shared
|
// std::make_shared
|
||||||
ASSERT_EQUALS("smart-pointer<C>", typeOf("class C {}; x = std::make_shared<C>();", "("));
|
{
|
||||||
|
Settings set;
|
||||||
|
Library::SmartPointer sharedPtr;
|
||||||
|
sharedPtr.name = "std::shared_ptr";
|
||||||
|
set.library.smartPointers["std::shared_ptr"] = sharedPtr;
|
||||||
|
ASSERT_EQUALS("smart-pointer(std::shared_ptr)",
|
||||||
|
typeOf("class C {}; x = std::make_shared<C>();", "(", "test.cpp", &set));
|
||||||
|
}
|
||||||
|
|
||||||
// return
|
// return
|
||||||
{
|
{
|
||||||
|
@ -7486,6 +7514,17 @@ private:
|
||||||
sC.library.containers["C"] = c;
|
sC.library.containers["C"] = c;
|
||||||
ASSERT_EQUALS("container(C)", typeOf("C f(char *p) { char data[10]; return data; }", "return", "test.cpp", &sC));
|
ASSERT_EQUALS("container(C)", typeOf("C f(char *p) { char data[10]; return data; }", "return", "test.cpp", &sC));
|
||||||
}
|
}
|
||||||
|
// Smart pointer
|
||||||
|
{
|
||||||
|
Settings set;
|
||||||
|
Library::SmartPointer myPtr;
|
||||||
|
myPtr.name = "MyPtr";
|
||||||
|
set.library.smartPointers["MyPtr"] = myPtr;
|
||||||
|
ASSERT_EQUALS("smart-pointer(MyPtr)",
|
||||||
|
typeOf("void f() { MyPtr<int> p; return p; }", "p ;", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("signed int", typeOf("void f() { MyPtr<int> p; return *p; }", "* p ;", "test.cpp", &set));
|
||||||
|
ASSERT_EQUALS("smart-pointer(MyPtr)", typeOf("void f() {return MyPtr<int>();}", "(", "test.cpp", &set));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void variadic1() { // #7453
|
void variadic1() { // #7453
|
||||||
|
|
Loading…
Reference in New Issue