diff --git a/lib/check64bit.cpp b/lib/check64bit.cpp index eeda1c93648..309137af737 100644 --- a/lib/check64bit.cpp +++ b/lib/check64bit.cpp @@ -45,7 +45,7 @@ static bool is32BitIntegerReturn(const Function* func, const Settings* settings) if (settings->platform.sizeof_pointer != 8) return false; const ValueType* vt = func->arg->valueType(); - return vt && vt->pointer == 0 && vt->isIntegral() && vt->typeSize(settings->platform) == 4; + return vt && vt->pointer == 0 && vt->isIntegral() && vt->getSizeOf(*settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) == 4; } void Check64BitPortability::pointerassignment() diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 337f2b1de3f..57c46492bc0 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -96,7 +96,7 @@ static int getMinFormatStringOutputLength(const std::vector ¶m std::string digits_string; bool i_d_x_f_found = false; int parameterLength = 0; - int inputArgNr = formatStringArgNr; + nonneg int inputArgNr = formatStringArgNr; for (std::size_t i = 1; i + 1 < formatString.length(); ++i) { if (formatString[i] == '\\') { if (i < formatString.length() - 1 && formatString[i + 1] == '0') @@ -229,7 +229,8 @@ static bool getDimensionsEtc(const Token * const arrayToken, const Settings &set Dimension dim; dim.known = value->isKnown(); dim.tok = nullptr; - const MathLib::bigint typeSize = array->valueType()->typeSize(settings.platform, array->valueType()->pointer > 1); + const auto sizeOf = array->valueType()->pointer > 1 ? ValueType::SizeOf::Pointer : ValueType::SizeOf::Pointee; + const size_t typeSize = array->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, sizeOf); if (typeSize == 0) return false; dim.num = value->intvalue / typeSize; @@ -585,7 +586,7 @@ ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const if (var->isPointerArray()) v.intvalue = dim * mSettings->platform.sizeof_pointer; else { - const MathLib::bigint typeSize = bufTok->valueType()->typeSize(mSettings->platform); + const size_t typeSize = bufTok->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); v.intvalue = dim * typeSize; } @@ -929,7 +930,7 @@ bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Settings &settings, const { if (!offset) return false; - if (!argtok->valueType() || argtok->valueType()->typeSize(settings.platform) == 0) + if (!argtok->valueType() || argtok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee) == 0) return false; const Token *indexTok = nullptr; if (type == 1 && Token::Match(argtok, "%name% [") && argtok->astParent() == argtok->next() && !Token::simpleMatch(argtok->linkAt(1), "] [")) @@ -942,7 +943,7 @@ bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Settings &settings, const return false; if (!indexTok->hasKnownIntValue()) return false; - offset->value = indexTok->getKnownIntValue() * argtok->valueType()->typeSize(settings.platform); + offset->value = indexTok->getKnownIntValue() * argtok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); return true; } @@ -1102,7 +1103,7 @@ void CheckBufferOverrun::objectIndex() continue; } if (obj->valueType() && var->valueType() && (obj->isCast() || (obj->isCpp() && isCPPCast(obj)) || obj->valueType()->pointer)) { // allow cast to a different type - const auto varSize = var->valueType()->typeSize(mSettings->platform); + const auto varSize = var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); if (varSize == 0) continue; if (obj->valueType()->type != var->valueType()->type) { diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 03aa85f0a3c..34a734fecaa 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -3459,9 +3459,7 @@ void CheckClass::checkReturnByReference() const bool isView = isContainer && var->valueType()->container->view; bool warn = isContainer && !isView; if (!warn && !isView) { - const std::size_t size = ValueFlow::getSizeOf(*var->valueType(), - *mSettings, - ValueFlow::Accuracy::LowerBound); + const std::size_t size = var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); if (size > 2 * mSettings->platform.sizeof_pointer) warn = true; } diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 5953dce4c96..83f7227091c 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -1149,6 +1149,12 @@ void CheckLeakAutoVar::leakIfAllocated(const Token *vartok, } } +static bool isSafeCast(const ValueType* vt, const Settings& settings) +{ + const size_t sizeOf = vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); + return sizeOf == 0 || sizeOf >= settings.platform.sizeof_pointer; +} + void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope) { const std::map &alloctype = varInfo.alloctype; @@ -1182,8 +1188,7 @@ void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndO while (tok3 && tok3->isCast() && (!tok3->valueType() || tok3->valueType()->pointer || - (tok3->valueType()->typeSize(mSettings->platform) == 0) || - (tok3->valueType()->typeSize(mSettings->platform) >= mSettings->platform.sizeof_pointer))) + isSafeCast(tok3->valueType(), *mSettings))) tok3 = tok3->astOperand2() ? tok3->astOperand2() : tok3->astOperand1(); if (tok3 && tok3->varId() == varid) tok2 = tok3->next(); diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 355b0d809d7..7f49257d1a3 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1071,8 +1071,8 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) const Variable* argvar = tok->function()->getArgumentVar(argnr); if (!argvar || !argvar->valueType()) continue; - const MathLib::bigint argSize = argvar->valueType()->typeSize(mSettings->platform, /*p*/ true); - if (argSize <= 0 || argSize >= mSettings->platform.sizeof_pointer) + const size_t argSize = argvar->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); + if (argSize == 0 || argSize >= mSettings->platform.sizeof_pointer) continue; } functionCallLeak(arg, arg->str(), functionName); diff --git a/lib/checkother.cpp b/lib/checkother.cpp index eed6dfe0bf1..e1728788ecb 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1524,7 +1524,7 @@ static bool isLargeContainer(const Variable* var, const Settings& settings) return false; } const ValueType vtElem = ValueType::parseDecl(vt->containerTypeToken, settings); - const auto elemSize = std::max(ValueFlow::getSizeOf(vtElem, settings, ValueFlow::Accuracy::LowerBound), 1); + const auto elemSize = std::max(vtElem.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer), 1); const auto arraySize = var->dimension(0) * elemSize; return arraySize > maxByValueSize; } @@ -1564,7 +1564,7 @@ void CheckOther::checkPassByReference() // Ensure that it is a large object. if (!var->type()->classScope) inconclusive = true; - else if (!var->valueType() || ValueFlow::getSizeOf(*var->valueType(), *mSettings, ValueFlow::Accuracy::LowerBound) <= 2 * mSettings->platform.sizeof_pointer) + else if (!var->valueType() || var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) <= 2 * mSettings->platform.sizeof_pointer) continue; } else @@ -3327,7 +3327,8 @@ void CheckOther::checkRedundantCopy() const Token* varTok = fScope->bodyEnd->tokAt(-2); if (varTok->variable() && !varTok->variable()->isGlobal() && (!varTok->variable()->type() || !varTok->variable()->type()->classScope || - (varTok->variable()->valueType() && ValueFlow::getSizeOf(*varTok->variable()->valueType(), *mSettings, ValueFlow::Accuracy::LowerBound) > 2 * mSettings->platform.sizeof_pointer))) + (varTok->variable()->valueType() && + varTok->variable()->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) > 2 * mSettings->platform.sizeof_pointer))) redundantCopyError(startTok, startTok->str()); } } @@ -3447,7 +3448,7 @@ void CheckOther::checkIncompleteArrayFill() if (size == 0 && var->valueType()->pointer) size = mSettings->platform.sizeof_pointer; else if (size == 0 && var->valueType()) - size = ValueFlow::getSizeOf(*var->valueType(), *mSettings, ValueFlow::Accuracy::LowerBound); + size = var->valueType()->getSizeOf(*mSettings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); const Token* tok3 = tok->next()->astOperand2()->astOperand1()->astOperand1(); if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { if (printWarning) @@ -4430,8 +4431,7 @@ static UnionMember parseUnionMember(const Variable &var, if (var.isArray()) { size = var.dimension(0); } else if (vt != nullptr) { - size = ValueFlow::getSizeOf(*vt, settings, - ValueFlow::Accuracy::ExactOrZero); + size = vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } return UnionMember(nameToken->str(), size); } @@ -4523,7 +4523,7 @@ static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigin bufToken = expr->astOperand1()->astOperand1(); offsetToken = expr->astOperand1()->astOperand2(); if (expr->astOperand1()->valueType()) - elementSize = ValueFlow::getSizeOf(*expr->astOperand1()->valueType(), settings, ValueFlow::Accuracy::LowerBound); + elementSize = expr->astOperand1()->valueType()->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); } else if (Token::Match(expr, "+|-") && expr->isBinaryOp()) { const bool pointer1 = (expr->astOperand1()->valueType() && expr->astOperand1()->valueType()->pointer > 0); const bool pointer2 = (expr->astOperand2()->valueType() && expr->astOperand2()->valueType()->pointer > 0); @@ -4532,13 +4532,13 @@ static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigin offsetToken = expr->astOperand2(); auto vt = *expr->astOperand1()->valueType(); --vt.pointer; - elementSize = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::LowerBound); + elementSize = vt.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); } else if (!pointer1 && pointer2) { bufToken = expr->astOperand2(); offsetToken = expr->astOperand1(); auto vt = *expr->astOperand2()->valueType(); --vt.pointer; - elementSize = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::LowerBound); + elementSize = vt.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); } else { return false; } @@ -4547,7 +4547,7 @@ static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigin *offset = 0; auto vt = *expr->valueType(); --vt.pointer; - elementSize = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::LowerBound); + elementSize = vt.getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); if (elementSize > 0) { *offset *= elementSize; if (sizeValue) diff --git a/lib/checktype.cpp b/lib/checktype.cpp index 92d19c1e794..68b6580d270 100644 --- a/lib/checktype.cpp +++ b/lib/checktype.cpp @@ -322,12 +322,8 @@ static bool checkTypeCombination(ValueType src, ValueType tgt, const Settings& s src.reference = Reference::None; tgt.reference = Reference::None; - const std::size_t sizeSrc = ValueFlow::getSizeOf(src, - settings, - ValueFlow::Accuracy::ExactOrZero); - const std::size_t sizeTgt = ValueFlow::getSizeOf(tgt, - settings, - ValueFlow::Accuracy::ExactOrZero); + const std::size_t sizeSrc = src.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); + const std::size_t sizeTgt = tgt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (!(sizeSrc > 0 && sizeTgt > 0 && sizeSrc < sizeTgt)) return false; diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index 9f3bf593755..87877fd629e 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -1586,7 +1586,7 @@ static void setValues(const Tokenizer &tokenizer, const SymbolDatabase *symbolDa return v * dim.num; }); if (var.valueType()) - typeSize += mul * var.valueType()->typeSize(settings.platform, true); + typeSize += mul * var.valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } scope.definedType->sizeOf = typeSize; } @@ -1594,8 +1594,8 @@ static void setValues(const Tokenizer &tokenizer, const SymbolDatabase *symbolDa for (auto *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { ValueType vt = ValueType::parseDecl(tok->tokAt(2), settings); - const MathLib::bigint sz = vt.typeSize(settings.platform, true); - if (sz <= 0) + const size_t sz = vt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); + if (sz == 0) continue; long long mul = 1; for (const Token *arrtok = tok->linkAt(1)->previous(); arrtok; arrtok = arrtok->previous()) { diff --git a/lib/ctu.cpp b/lib/ctu.cpp index 833a4dfda4b..aadba39d3c7 100644 --- a/lib/ctu.cpp +++ b/lib/ctu.cpp @@ -368,7 +368,7 @@ CTU::FileInfo *CTU::getFileInfo(const Tokenizer &tokenizer) functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); - const auto typeSize = argtok->valueType()->typeSize(tokenizer.getSettings().platform); + const auto typeSize = argtok->valueType()->getSizeOf(tokenizer.getSettings(), ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); functionCall.callArgValue.value = typeSize > 0 ? argtok->variable()->dimension(0) * typeSize : -1; functionCall.warning = false; fileInfo->functionCalls.push_back(std::move(functionCall)); @@ -382,7 +382,7 @@ CTU::FileInfo *CTU::getFileInfo(const Tokenizer &tokenizer) functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); - functionCall.callArgValue.value = argtok->astOperand1()->valueType()->typeSize(tokenizer.getSettings().platform); + functionCall.callArgValue.value = argtok->astOperand1()->valueType()->getSizeOf(tokenizer.getSettings(), ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee); functionCall.warning = false; fileInfo->functionCalls.push_back(std::move(functionCall)); } diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index b75c9bfed4d..47145f2191d 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -8438,40 +8439,187 @@ bool ValueType::isVolatile(nonneg int indirect) const return false; return volatileness & (1 << (pointer - indirect)); } -MathLib::bigint ValueType::typeSize(const Platform &platform, bool p) const + +namespace { + struct Result + { + size_t total; + bool success; + }; +} + +template +static Result accumulateStructMembers(const Scope* scope, F f, ValueType::Accuracy accuracy) { - if (p && pointer) - return platform.sizeof_pointer; + size_t total = 0; + std::set anonScopes; + for (const Variable& var : scope->varlist) { + if (var.isStatic()) + continue; + const MathLib::bigint bits = var.nameToken() ? var.nameToken()->bits() : -1; + if (const ValueType* vt = var.valueType()) { + if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope) + return {0, false}; + const MathLib::bigint dim = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), MathLib::bigint(1), [](MathLib::bigint i1, const Dimension& dim) { + return i1 * dim.num; + }); + if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union + const auto ret = anonScopes.insert(var.nameToken()->scope()); + if (ret.second) + total = f(total, *vt, dim, bits); + } + else + total = f(total, *vt, dim, bits); + } + if (accuracy == ValueType::Accuracy::ExactOrZero && total == 0 && bits == -1) + return {0, false}; + } + return {total, true}; +} - if (typeScope && typeScope->definedType && typeScope->definedType->sizeOf) - return typeScope->definedType->sizeOf; - switch (type) { - case ValueType::Type::BOOL: - return platform.sizeof_bool; - case ValueType::Type::CHAR: +static size_t bitCeil(size_t x) +{ + if (x <= 1) return 1; - case ValueType::Type::SHORT: + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + return x + 1; +} + +static size_t getAlignOf(const ValueType& vt, const Settings& settings, ValueType::Accuracy accuracy, ValueType::SizeOf sizeOf, int maxRecursion = 0) +{ + if (maxRecursion == settings.vfOptions.maxAlignOfRecursion) { + // TODO: add bailout message + return 0; + } + if ((vt.pointer && sizeOf == ValueType::SizeOf::Pointer) || vt.reference != Reference::None || vt.isPrimitive()) { + auto align = vt.getSizeOf(settings, accuracy, ValueType::SizeOf::Pointer); + return align == 0 ? 0 : bitCeil(align); + } + if (vt.type == ValueType::Type::RECORD && vt.typeScope) { + auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/, MathLib::bigint /*bits*/) { + size_t a = getAlignOf(vt2, settings, accuracy, ValueType::SizeOf::Pointer, ++maxRecursion); + return std::max(max, a); + }; + Result result = accumulateStructMembers(vt.typeScope, accHelper, accuracy); + size_t total = result.total; + if (const Type* dt = vt.typeScope->definedType) { + total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { + if (bi.type && bi.type->classScope) + v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; + return v; + }); + } + return result.success ? std::max(1, total) : total; + } + if (vt.type == ValueType::Type::CONTAINER) + return settings.platform.sizeof_pointer; // Just guess + return 0; +} + +size_t ValueType::getSizeOf( const Settings& settings, Accuracy accuracy, SizeOf sizeOf, int maxRecursion) const +{ + if (maxRecursion == settings.vfOptions.maxSizeOfRecursion) { + // TODO: add bailout message + return 0; + } + const auto& platform = settings.platform; + if (sizeOf == SizeOf::Pointer && (pointer || reference != Reference::None)) + return platform.sizeof_pointer; + if (type == ValueType::Type::BOOL || type == ValueType::Type::CHAR) + return 1; + if (type == ValueType::Type::SHORT) return platform.sizeof_short; - case ValueType::Type::WCHAR_T: + if (type == ValueType::Type::WCHAR_T) return platform.sizeof_wchar_t; - case ValueType::Type::INT: + if (type == ValueType::Type::INT) return platform.sizeof_int; - case ValueType::Type::LONG: + if (type == ValueType::Type::LONG) return platform.sizeof_long; - case ValueType::Type::LONGLONG: + if (type == ValueType::Type::LONGLONG) return platform.sizeof_long_long; - case ValueType::Type::FLOAT: + if (type == ValueType::Type::FLOAT) return platform.sizeof_float; - case ValueType::Type::DOUBLE: + if (type == ValueType::Type::DOUBLE) return platform.sizeof_double; - case ValueType::Type::LONGDOUBLE: + if (type == ValueType::Type::LONGDOUBLE) return platform.sizeof_long_double; - default: - break; + if (type == ValueType::Type::CONTAINER) + return 3 * platform.sizeof_pointer; // Just guess + if (type == ValueType::Type::RECORD && typeScope) { + size_t currentBitCount = 0; + size_t currentBitfieldAlloc = 0; + auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint nBits) -> size_t { + const size_t charBit = settings.platform.char_bit; + size_t n = vt2.getSizeOf(settings, accuracy, SizeOf::Pointer, ++maxRecursion); + size_t a = getAlignOf(vt2, settings, accuracy, SizeOf::Pointer); + if (n == 0 || a == 0) + return accuracy == Accuracy::ExactOrZero ? 0 : total; + if (nBits == 0) { + if (currentBitfieldAlloc == 0) { + nBits = n * charBit; + } else { + nBits = (currentBitfieldAlloc * charBit) - currentBitCount; + } + } + if (nBits > 0) { + size_t ret = total; + if (currentBitfieldAlloc == 0) { + currentBitfieldAlloc = n; + currentBitCount = 0; + } else if (currentBitCount + nBits > charBit * currentBitfieldAlloc) { + ret += currentBitfieldAlloc; + currentBitfieldAlloc = n; + currentBitCount = 0; + } + while (nBits > charBit * currentBitfieldAlloc) { + ret += currentBitfieldAlloc; + nBits -= charBit * currentBitfieldAlloc; + } + currentBitCount += nBits; + return ret; + } + n *= dim; + size_t padding = (a - (total % a)) % a; + if (currentBitCount > 0) { + bool fitsInBitfield = currentBitCount + (n * charBit) <= currentBitfieldAlloc * charBit; + bool isAligned = currentBitCount % (charBit * a) == 0; + if (vt2.isIntegral() && fitsInBitfield && isAligned) { + currentBitCount += charBit * n; + return total; + } + n += currentBitfieldAlloc; + currentBitfieldAlloc = 0; + currentBitCount = 0; + } + return typeScope->type == ScopeType::eUnion ? std::max(total, n) : total + padding + n; + }; + Result result = accumulateStructMembers(typeScope, accHelper, accuracy); + size_t total = result.total; + if (currentBitCount > 0) + total += currentBitfieldAlloc; + if (const ::Type* dt = typeScope->definedType) { + total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const ::Type::BaseInfo& bi) { + if (bi.type && bi.type->classScope) + v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; + return v; + }); + } + if (accuracy == Accuracy::ExactOrZero && total == 0 && !result.success) + return 0; + total = std::max(size_t{1}, total); + size_t align = getAlignOf(*this, settings, accuracy, sizeOf); + if (align == 0) + return accuracy == Accuracy::ExactOrZero ? 0 : total; + total += (align - (total % align)) % align; + return total; } - - // Unknown invalid size return 0; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 50f37fc1ebf..a0e6190db73 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1312,7 +1312,15 @@ class CPPCHECKLIB ValueType { bool isVolatile(nonneg int indirect = 0) const; - MathLib::bigint typeSize(const Platform &platform, bool p=false) const; + enum class Accuracy : std::uint8_t { + ExactOrZero, + LowerBound, + }; + enum class SizeOf : std::uint8_t { + Pointer, + Pointee, + }; + size_t getSizeOf(const Settings& settings, Accuracy accuracy, SizeOf sizeOf, int maxRecursion = 0) const; /// Check if type is the same ignoring const and references bool isTypeEqual(const ValueType* that) const; diff --git a/lib/token.cpp b/lib/token.cpp index 0c64222801d..68e7838b6be 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -784,7 +784,7 @@ nonneg int Token::getStrSize(const Token *tok, const Settings &settings) if (tok->valueType()) { ValueType vt(*tok->valueType()); vt.pointer = 0; - sizeofType = ValueFlow::getSizeOf(vt, settings, ValueFlow::Accuracy::ExactOrZero); + sizeofType = vt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } return getStrArraySize(tok) * sizeofType; } diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 8eb5735c986..a4e24f5ca37 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -423,187 +423,6 @@ void ValueFlow::combineValueProperties(const ValueFlow::Value &value1, const Val result.path = value1.path; } -namespace { - struct Result - { - size_t total; - bool success; - }; -} - -template -static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accuracy accuracy) -{ - size_t total = 0; - std::set anonScopes; - for (const Variable& var : scope->varlist) { - if (var.isStatic()) - continue; - const MathLib::bigint bits = var.nameToken() ? var.nameToken()->bits() : -1; - if (const ValueType* vt = var.valueType()) { - if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope) - return {0, false}; - const MathLib::bigint dim = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), MathLib::bigint(1), [](MathLib::bigint i1, const Dimension& dim) { - return i1 * dim.num; - }); - if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union - const auto ret = anonScopes.insert(var.nameToken()->scope()); - if (ret.second) - total = f(total, *vt, dim, bits); - } - else - total = f(total, *vt, dim, bits); - } - if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0 && bits == -1) - return {0, false}; - } - return {total, true}; -} - -static size_t bitCeil(size_t x) -{ - if (x <= 1) - return 1; - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x |= x >> 32; - return x + 1; -} - -static size_t getAlignOf(const ValueType& vt, const Settings& settings, ValueFlow::Accuracy accuracy, int maxRecursion = 0) -{ - if (maxRecursion == settings.vfOptions.maxAlignOfRecursion) { - // TODO: add bailout message - return 0; - } - if (vt.pointer || vt.reference != Reference::None || vt.isPrimitive()) { - auto align = ValueFlow::getSizeOf(vt, settings, accuracy); - return align == 0 ? 0 : bitCeil(align); - } - if (vt.type == ValueType::Type::RECORD && vt.typeScope) { - auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/, MathLib::bigint /*bits*/) { - size_t a = getAlignOf(vt2, settings, accuracy, ++maxRecursion); - return std::max(max, a); - }; - Result result = accumulateStructMembers(vt.typeScope, accHelper, accuracy); - size_t total = result.total; - if (const Type* dt = vt.typeScope->definedType) { - total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { - if (bi.type && bi.type->classScope) - v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; - return v; - }); - } - return result.success ? std::max(1, total) : total; - } - if (vt.type == ValueType::Type::CONTAINER) - return settings.platform.sizeof_pointer; // Just guess - return 0; -} - -size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accuracy accuracy, int maxRecursion) -{ - if (maxRecursion == settings.vfOptions.maxSizeOfRecursion) { - // TODO: add bailout message - return 0; - } - if (vt.pointer || vt.reference != Reference::None) - return settings.platform.sizeof_pointer; - if (vt.type == ValueType::Type::BOOL || vt.type == ValueType::Type::CHAR) - return 1; - if (vt.type == ValueType::Type::SHORT) - return settings.platform.sizeof_short; - if (vt.type == ValueType::Type::WCHAR_T) - return settings.platform.sizeof_wchar_t; - if (vt.type == ValueType::Type::INT) - return settings.platform.sizeof_int; - if (vt.type == ValueType::Type::LONG) - return settings.platform.sizeof_long; - if (vt.type == ValueType::Type::LONGLONG) - return settings.platform.sizeof_long_long; - if (vt.type == ValueType::Type::FLOAT) - return settings.platform.sizeof_float; - if (vt.type == ValueType::Type::DOUBLE) - return settings.platform.sizeof_double; - if (vt.type == ValueType::Type::LONGDOUBLE) - return settings.platform.sizeof_long_double; - if (vt.type == ValueType::Type::CONTAINER) - return 3 * settings.platform.sizeof_pointer; // Just guess - if (vt.type == ValueType::Type::RECORD && vt.typeScope) { - size_t currentBitCount = 0; - size_t currentBitfieldAlloc = 0; - auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, MathLib::bigint bits) -> size_t { - const size_t charBit = settings.platform.char_bit; - size_t n = ValueFlow::getSizeOf(vt2, settings,accuracy, ++maxRecursion); - size_t a = getAlignOf(vt2, settings, accuracy); - if (n == 0 || a == 0) - return accuracy == Accuracy::ExactOrZero ? 0 : total; - if (bits == 0) { - if (currentBitfieldAlloc == 0) { - bits = n * charBit; - } else { - bits = (currentBitfieldAlloc * charBit) - currentBitCount; - } - } - if (bits > 0) { - size_t ret = total; - if (currentBitfieldAlloc == 0) { - currentBitfieldAlloc = n; - currentBitCount = 0; - } else if (currentBitCount + bits > charBit * currentBitfieldAlloc) { - ret += currentBitfieldAlloc; - currentBitfieldAlloc = n; - currentBitCount = 0; - } - while (bits > charBit * currentBitfieldAlloc) { - ret += currentBitfieldAlloc; - bits -= charBit * currentBitfieldAlloc; - } - currentBitCount += bits; - return ret; - } - n *= dim; - size_t padding = (a - (total % a)) % a; - if (currentBitCount > 0) { - bool fitsInBitfield = currentBitCount + (n * charBit) <= currentBitfieldAlloc * charBit; - bool isAligned = currentBitCount % (charBit * a) == 0; - if (vt2.isIntegral() && fitsInBitfield && isAligned) { - currentBitCount += charBit * n; - return total; - } - n += currentBitfieldAlloc; - currentBitfieldAlloc = 0; - currentBitCount = 0; - } - return vt.typeScope->type == ScopeType::eUnion ? std::max(total, n) : total + padding + n; - }; - Result result = accumulateStructMembers(vt.typeScope, accHelper, accuracy); - size_t total = result.total; - if (currentBitCount > 0) - total += currentBitfieldAlloc; - if (const Type* dt = vt.typeScope->definedType) { - total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { - if (bi.type && bi.type->classScope) - v += accumulateStructMembers(bi.type->classScope, accHelper, accuracy).total; - return v; - }); - } - if (accuracy == Accuracy::ExactOrZero && total == 0 && !result.success) - return 0; - total = std::max(size_t{1}, total); - size_t align = getAlignOf(vt, settings, accuracy); - if (align == 0) - return accuracy == Accuracy::ExactOrZero ? 0 : total; - total += (align - (total % align)) % align; - return total; - } - return 0; -} - static void valueFlowNumber(TokenList &tokenlist, const Settings& settings) { for (Token *tok = tokenlist.front(); tok;) { @@ -3683,8 +3502,8 @@ static bool isTruncated(const ValueType* src, const ValueType* dst, const Settin if (src->smartPointer && dst->smartPointer) return false; if ((src->isIntegral() && dst->isIntegral()) || (src->isFloat() && dst->isFloat())) { - const size_t srcSize = ValueFlow::getSizeOf(*src, settings, ValueFlow::Accuracy::LowerBound); - const size_t dstSize = ValueFlow::getSizeOf(*dst, settings, ValueFlow::Accuracy::LowerBound); + const size_t srcSize = src->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); + const size_t dstSize = dst->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer); if (srcSize > dstSize) return true; if (srcSize == dstSize && src->sign != dst->sign) @@ -4207,10 +4026,10 @@ static std::list truncateValues(std::list va if (!dst || !dst->isIntegral()) return values; - const size_t sz = ValueFlow::getSizeOf(*dst, settings, ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (src) { - const size_t osz = ValueFlow::getSizeOf(*src, settings, ValueFlow::Accuracy::ExactOrZero); + const size_t osz = src->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (osz >= sz && dst->sign == ValueType::Sign::SIGNED && src->sign == ValueType::Sign::UNSIGNED) { values.remove_if([&](const ValueFlow::Value& value) { if (!value.isIntValue()) @@ -7078,8 +6897,9 @@ static void valueFlowDynamicBufferSize(const TokenList& tokenlist, const SymbolD if (!typeTok || !typeTok->varId()) typeTok = newTok->astParent()->previous(); // hack for "int** z = ..." if (typeTok && typeTok->valueType()) { - const MathLib::bigint typeSize = typeTok->valueType()->typeSize(settings.platform, typeTok->valueType()->pointer > 1); - if (typeSize >= 0) + const auto sizeOf = typeTok->valueType()->pointer > 1 ? ValueType::SizeOf::Pointer : ValueType::SizeOf::Pointee; + const size_t typeSize = typeTok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, sizeOf); + if (typeSize > 0 || numElem == 0) sizeValue = numElem * typeSize; } } diff --git a/lib/valueflow.h b/lib/valueflow.h index 85a8e49a9e7..6fe4d052fdf 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -61,16 +61,6 @@ namespace ValueFlow { std::string eitherTheConditionIsRedundant(const Token *condition); - enum class Accuracy : std::uint8_t { - ExactOrZero, - LowerBound, - }; - - size_t getSizeOf(const ValueType &vt, - const Settings &settings, - Accuracy accuracy, - int maxRecursion = 0); - const Value* findValue(const std::list& values, const Settings& settings, const std::function &pred); diff --git a/lib/vf_analyzers.cpp b/lib/vf_analyzers.cpp index 104563b113b..5f5568663e4 100644 --- a/lib/vf_analyzers.cpp +++ b/lib/vf_analyzers.cpp @@ -355,9 +355,7 @@ struct ValueFlowAnalyzer : Analyzer { /* Truncate value */ const ValueType *dst = tok->valueType(); if (dst) { - const size_t sz = ValueFlow::getSizeOf(*dst, - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (sz > 0 && sz < sizeof(MathLib::biguint)) { MathLib::bigint newvalue = ValueFlow::truncateIntValue(value->intvalue, sz, dst->sign); diff --git a/lib/vf_common.cpp b/lib/vf_common.cpp index 0498a7e303d..56a6b364c44 100644 --- a/lib/vf_common.cpp +++ b/lib/vf_common.cpp @@ -114,7 +114,7 @@ namespace ValueFlow { const ValueType &valueType = ValueType::parseDecl(typeTok, settings); - return getSizeOf(valueType, settings, ValueFlow::Accuracy::ExactOrZero); + return valueType.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } // Handle various constants.. @@ -125,7 +125,7 @@ namespace ValueFlow MathLib::bigint signedValue = MathLib::toBigNumber(tok); const ValueType* vt = tok->valueType(); if (vt && vt->sign == ValueType::UNSIGNED && signedValue < 0 - && getSizeOf(*vt, settings, ValueFlow::Accuracy::ExactOrZero) + && vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) < sizeof(MathLib::bigint)) { MathLib::bigint minValue{}, maxValue{}; if (getMinMaxValues(tok->valueType(), settings.platform, minValue, maxValue)) @@ -160,9 +160,7 @@ namespace ValueFlow (tok->next()->astOperand2()->valueType()->pointer == 0 || // <- TODO this is a bailout, abort when there are array->pointer conversions (tok->next()->astOperand2()->variable() && !tok->next()->astOperand2()->variable()->isArray())) && !tok->next()->astOperand2()->valueType()->isEnum()) { // <- TODO this is a bailout, handle enum with non-int types - const size_t sz = getSizeOf(*tok->next()->astOperand2()->valueType(), - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = tok->next()->astOperand2()->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); if (sz) { Value value(sz); value.setKnown(); @@ -181,7 +179,7 @@ namespace ValueFlow } if (Token::simpleMatch(tok, "sizeof ( *")) { const ValueType *vt = tok->tokAt(2)->valueType(); - const size_t sz = vt ? getSizeOf(*vt, settings, ValueFlow::Accuracy::ExactOrZero) + const size_t sz = vt ? vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) : 0; if (sz > 0) { Value value(sz); @@ -243,9 +241,7 @@ namespace ValueFlow if (var->type()->classScope && var->type()->classScope->enumType) size = getSizeOfType(var->type()->classScope->enumType, settings); } else if (var->valueType()) { - size = getSizeOf(*var->valueType(), - settings, - ValueFlow::Accuracy::ExactOrZero); + size = var->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); } else if (!var->type()) { size = getSizeOfType(var->typeStartToken(), settings); } @@ -294,7 +290,7 @@ namespace ValueFlow } } else if (!tok2->type()) { const ValueType& vt = ValueType::parseDecl(tok2, settings); - size_t sz = getSizeOf(vt, settings, ValueFlow::Accuracy::ExactOrZero); + size_t sz = vt.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); const Token* brac = tok2->astParent(); while (Token::simpleMatch(brac, "[")) { const Token* num = brac->astOperand2(); diff --git a/lib/vf_settokenvalue.cpp b/lib/vf_settokenvalue.cpp index 0140b45ae06..4d39d35daae 100644 --- a/lib/vf_settokenvalue.cpp +++ b/lib/vf_settokenvalue.cpp @@ -83,8 +83,8 @@ namespace ValueFlow // If the sign is the same there is no truncation if (vt1->sign == vt2->sign) return value; - const size_t n1 = getSizeOf(*vt1, settings, ValueFlow::Accuracy::ExactOrZero); - const size_t n2 = getSizeOf(*vt2, settings, ValueFlow::Accuracy::ExactOrZero); + const size_t n1 = vt1->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); + const size_t n2 = vt2->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); ValueType::Sign sign = ValueType::Sign::UNSIGNED; if (n1 < n2) sign = vt2->sign; @@ -225,7 +225,7 @@ namespace ValueFlow { // Skip setting values that are too big since its ambiguous if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) - && getSizeOf(*tok->valueType(), settings, ValueFlow::Accuracy::LowerBound) + && tok->valueType()->getSizeOf(settings, ValueType::Accuracy::LowerBound, ValueType::SizeOf::Pointer) >= sizeof(MathLib::bigint)) return; @@ -379,8 +379,8 @@ namespace ValueFlow const ValueType &valueType = ValueType::parseDecl(castType, settings); if (value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) && valueType.sign == ValueType::SIGNED && tok->valueType() - && getSizeOf(*tok->valueType(), settings, ValueFlow::Accuracy::ExactOrZero) - >= getSizeOf(valueType, settings, ValueFlow::Accuracy::ExactOrZero)) + && tok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer) + >= valueType.getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer)) return; setTokenValueCast(parent, valueType, value, settings); } @@ -642,9 +642,7 @@ namespace ValueFlow if (v.isIntValue() || v.isSymbolicValue()) { const ValueType *dst = tok->valueType(); if (dst) { - const size_t sz = ValueFlow::getSizeOf(*dst, - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); MathLib::bigint newvalue = ValueFlow::truncateIntValue(v.intvalue + 1, sz, dst->sign); if (v.bound != ValueFlow::Value::Bound::Point) { if (newvalue < v.intvalue) { @@ -674,9 +672,7 @@ namespace ValueFlow if (v.isIntValue() || v.isSymbolicValue()) { const ValueType *dst = tok->valueType(); if (dst) { - const size_t sz = ValueFlow::getSizeOf(*dst, - settings, - ValueFlow::Accuracy::ExactOrZero); + const size_t sz = dst->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer); MathLib::bigint newvalue = ValueFlow::truncateIntValue(v.intvalue - 1, sz, dst->sign); if (v.bound != ValueFlow::Value::Bound::Point) { if (newvalue > v.intvalue) { diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index b9a8a4e63df..ba687ad9983 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -2350,7 +2350,7 @@ class TestMemleakNoVar : public TestFixture { "void x() {\n" " set_error(strdup(p));\n" "}"); - TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Allocation with strdup, set_error doesn't release it.\n", "", errout_str()); + ASSERT_EQUALS("[test.cpp:5:15]: (error) Allocation with strdup, set_error doesn't release it. [leakNoVarFunctionCall]\n", errout_str()); check("void f()\n" "{\n" diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 8762c5141d6..79ad6e45b30 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -1726,6 +1726,22 @@ class TestValueFlow : public TestFixture { ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(-1, values.back().intvalue); ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Impossible, values.back().valueKind); + + code = "struct E;\n" + "struct B {\n" + " E* e;\n" + " B* b;\n" + "};\n" + "struct D : B {};\n" + "struct E : B {\n" + " B* be;\n" + "};\n" + "int f() {\n" + " return sizeof(D);\n" + "}"; + values = tokenValues(code, "( D )"); + ASSERT_EQUALS(1U, values.size()); + TODO_ASSERT_EQUALS(2 * settings.platform.sizeof_pointer, 1, values.back().intvalue); } void valueFlowComma() @@ -7457,6 +7473,32 @@ class TestValueFlow : public TestFixture { "}"; ASSERT_EQUALS(true, testValueOfX(code, 4U, 100, ValueFlow::Value::ValueType::BUFFER_SIZE)); + code = "struct A {};\n" // #14305 + "void* f() {\n" + " A* x = new A();\n" + " return x;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 4U, 1, ValueFlow::Value::ValueType::BUFFER_SIZE)); + + code = "struct A {};\n" + "void* f() {\n" + " void* x = new A;\n" + " return x;\n" + "}"; + { + auto values = tokenValues(code, "x ; }"); + ASSERT_EQUALS(1, values.size()); + ASSERT(values.front().isSymbolicValue()); + // TODO: add BUFFER_SIZE value = 1 + } + + code = "struct B { int32_t i; };\n" + "void* f() {\n" + " B* x = new B();\n" + " return x;\n" + "}"; + ASSERT_EQUALS(true, testValueOfX(code, 4U, 4, ValueFlow::Value::ValueType::BUFFER_SIZE)); + settings = settingsOld; }