Symboldatabase: Improve valuetypes for containers, iterators, and smart pointers (#3398)

This commit is contained in:
Paul Fultz II 2021-08-14 12:00:58 -05:00 committed by GitHub
parent f946bbc249
commit fdaeaacc40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 142 additions and 19 deletions

View File

@ -73,6 +73,7 @@ static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn
return true;
return defaultReturn;
case ValueType::Type::SMART_POINTER:
case ValueType::Type::CONTAINER:
case ValueType::Type::ITERATOR:
case ValueType::Type::VOID:

View File

@ -500,6 +500,7 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
if (!className)
return Error(ErrorCode::MISSING_ATTRIBUTE, "class-name");
SmartPointer& smartPointer = smartPointers[className];
smartPointer.name = className;
for (const tinyxml2::XMLElement* smartPointerNode = node->FirstChildElement(); smartPointerNode;
smartPointerNode = smartPointerNode->NextSiblingElement()) {
const std::string smartPointerNodeName = smartPointerNode->Name();

View File

@ -431,6 +431,7 @@ public:
std::vector<std::string> defines; // to provide some library defines
struct SmartPointer {
std::string name = "";
bool unique = false;
};

View File

@ -5717,6 +5717,17 @@ static void setAutoTokenProperties(Token * const autoTok)
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)
{
tok->setValueType(new ValueType(valuetype));
@ -5747,10 +5758,18 @@ void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype)
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;
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);
}
}
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);
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) {
const Token *op1 = parent->astOperand2()->astOperand1();
while (op1 && op1->str() == "[")
@ -6095,7 +6137,7 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V
if (!valuetype->typeScope && !valuetype->smartPointerType)
valuetype->type = ValueType::Type::UNKNOWN_TYPE;
else if (valuetype->smartPointerType)
valuetype->type = ValueType::Type::NONSTD;
valuetype->type = ValueType::Type::SMART_POINTER;
else if (valuetype->typeScope->type == Scope::eEnum) {
const Token * enum_type = valuetype->typeScope->enumType;
if (enum_type) {
@ -6193,7 +6235,7 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V
valuetype->smartPointer = smartPointer;
valuetype->smartPointerTypeToken = argTok->next();
valuetype->smartPointerType = argTok->next()->type();
valuetype->type = ValueType::Type::NONSTD;
valuetype->type = ValueType::Type::SMART_POINTER;
type = argTok->link();
if (type)
type = type->next();
@ -6478,6 +6520,13 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
setValueType(tok, vt);
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();
@ -6487,9 +6536,15 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
if (vt.typeScope) {
vt.smartPointerType = vt.typeScope->definedType;
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;
@ -6527,6 +6582,8 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
ValueType vt;
vt.type = ValueType::Type::ITERATOR;
vt.container = cont;
vt.containerTypeToken =
tok->astOperand1()->astOperand1()->valueType()->containerTypeToken;
setValueType(tok, vt);
}
}
@ -6740,6 +6797,9 @@ std::string ValueType::dump() const
case RECORD:
ret << "valueType-type=\"record\"";
break;
case SMART_POINTER:
ret << "valueType-type=\"smart-pointer\"";
break;
case CONTAINER:
ret << "valueType-type=\"container\"";
break;
@ -6904,8 +6964,8 @@ std::string ValueType::str() const
ret += " container(" + container->startPattern + ')';
} else if (type == ValueType::Type::ITERATOR && container) {
ret += " iterator(" + container->startPattern + ')';
} else if (smartPointerType) {
ret += " smart-pointer<" + smartPointerType->name() + ">";
} else if (type == ValueType::Type::SMART_POINTER && smartPointer) {
ret += " smart-pointer(" + smartPointer->name + ")";
}
for (unsigned int p = 0; p < pointer; p++) {
ret += " *";

View File

@ -1217,7 +1217,26 @@ enum class Reference {
class CPPCHECKLIB ValueType {
public:
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 pointer; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc
nonneg int constness; ///< bit 0=data, bit 1=*, bit 2=**

View File

@ -2058,7 +2058,9 @@ private:
" auto p = x.data();\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"
" std::vector<int> x;\n"

View File

@ -7438,7 +7438,12 @@ private:
vector.startPattern2 = "Vector !!::";
vector.type_templateArgNo = 0;
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;
Library::Container string;
string.startPattern = "test :: string";
@ -7447,13 +7452,29 @@ private:
set.library.containers["test::string"] = string;
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("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(Vector<test::string> v) { for (auto s: v) { x=s+s; } }", "+", "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("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));
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(
"container(test :: string)",
typeOf("void foo(Vector<test::string> v) { for (auto s: v) { x=s+s; } }", "+", "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
@ -7474,7 +7495,14 @@ private:
ASSERT_EQUALS("", typeOf("; int x[10] = { [3]=1 };", "[ 3 ]"));
// 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
{
@ -7486,6 +7514,17 @@ private:
sC.library.containers["C"] = c;
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