From 0fadf9ed25011df85477bba347a84fb56ccae905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Thu, 31 Aug 2023 11:54:46 +0200 Subject: [PATCH] sped up `Tokenizer::dump()` (#5009) Scanning the `cli` folder with `DISABLE_VALUEFLOW=1` `Tokenizer::dump()` will consume almost 25% of the total Ir count when an addon is specified. This is mainly caused by the usage of `std::ostream`. Encountered while profiling #4958. --- lib/cppcheck.cpp | 20 +- lib/errorlogger.cpp | 20 +- lib/symboldatabase.cpp | 406 +++++++++++++++++++++++++++-------------- lib/token.cpp | 140 +++++++++----- lib/tokenize.cpp | 251 +++++++++++++++++-------- lib/utils.h | 73 ++++++++ test/testutils.cpp | 55 ++++++ 7 files changed, 681 insertions(+), 284 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index fe9b60050..af13e04f7 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -315,8 +315,8 @@ static void createDumpFile(const Settings& settings, break; } - fdump << "" << std::endl; - fdump << "" << std::endl; + fdump << "\n"; + fdump << "\n"; fdump << " \n"; + << "/>" << '\n'; } static std::string executeAddon(const AddonInfo &addonInfo, @@ -571,16 +571,16 @@ unsigned int CppCheck::check(const std::string &path) std::string dumpFile; createDumpFile(mSettings, path, fdump, dumpFile); if (fdump.is_open()) { - fdump << "" << std::endl; + fdump << "\n"; for (const ErrorMessage& errmsg: compilerWarnings) fdump << " \n"; - fdump << " " << std::endl; - fdump << " " << std::endl; - fdump << " " << std::endl; - fdump << " " << std::endl; + fdump << " \n"; + fdump << " \n"; + fdump << " \n"; + fdump << " \n"; tokenizer.dump(fdump); - fdump << "" << std::endl; - fdump << "" << std::endl; + fdump << "\n"; + fdump << "\n"; fdump.close(); } diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 5b69bd489..f70e2e3ea 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -731,36 +731,36 @@ std::string ErrorMessage::FileLocation::stringify() const std::string ErrorLogger::toxml(const std::string &str) { - std::ostringstream xml; + std::string xml; for (const unsigned char c : str) { switch (c) { case '<': - xml << "<"; + xml += "<"; break; case '>': - xml << ">"; + xml += ">"; break; case '&': - xml << "&"; + xml += "&"; break; case '\"': - xml << """; + xml += """; break; case '\'': - xml << "'"; + xml += "'"; break; case '\0': - xml << "\\0"; + xml += "\\0"; break; default: if (c >= ' ' && c <= 0x7f) - xml << c; + xml += c; else - xml << 'x'; + xml += 'x'; break; } } - return xml.str(); + return xml; } std::string ErrorLogger::plistHeader(const std::string &version, const std::vector &files) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 01d2a94f1..04505d02b 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -3615,26 +3615,50 @@ bool Variable::arrayDimensions(const Settings* settings, bool& isContainer) return arr; } +static std::string scopeTypeToString(Scope::ScopeType type) +{ + switch (type) { + case Scope::ScopeType::eGlobal: + return "Global"; + case Scope::ScopeType::eClass: + return "Class"; + case Scope::ScopeType::eStruct: + return "Struct"; + case Scope::ScopeType::eUnion: + return "Union"; + case Scope::ScopeType::eNamespace: + return "Namespace"; + case Scope::ScopeType::eFunction: + return "Function"; + case Scope::ScopeType::eIf: + return "If"; + case Scope::ScopeType::eElse: + return "Else"; + case Scope::ScopeType::eFor: + return "For"; + case Scope::ScopeType::eWhile: + return "While"; + case Scope::ScopeType::eDo: + return "Do"; + case Scope::ScopeType::eSwitch: + return "Switch"; + case Scope::ScopeType::eTry: + return "Try"; + case Scope::ScopeType::eCatch: + return "Catch"; + case Scope::ScopeType::eUnconditional: + return "Unconditional"; + case Scope::ScopeType::eLambda: + return "Lambda"; + case Scope::ScopeType::eEnum: + return "Enum"; + } + return "Unknown"; +} + static std::ostream & operator << (std::ostream & s, Scope::ScopeType type) { - s << (type == Scope::eGlobal ? "Global" : - type == Scope::eClass ? "Class" : - type == Scope::eStruct ? "Struct" : - type == Scope::eUnion ? "Union" : - type == Scope::eNamespace ? "Namespace" : - type == Scope::eFunction ? "Function" : - type == Scope::eIf ? "If" : - type == Scope::eElse ? "Else" : - type == Scope::eFor ? "For" : - type == Scope::eWhile ? "While" : - type == Scope::eDo ? "Do" : - type == Scope::eSwitch ? "Switch" : - type == Scope::eTry ? "Try" : - type == Scope::eCatch ? "Catch" : - type == Scope::eUnconditional ? "Unconditional" : - type == Scope::eLambda ? "Lambda" : - type == Scope::eEnum ? "Enum" : - "Unknown"); + s << scopeTypeToString(type); return s; } @@ -4010,137 +4034,223 @@ void SymbolDatabase::printOut(const char *title) const void SymbolDatabase::printXml(std::ostream &out) const { - out << std::setiosflags(std::ios::boolalpha); + std::string outs; std::set variables; // Scopes.. - out << " " << std::endl; + outs += " \n"; for (std::list::const_iterator scope = scopeList.cbegin(); scope != scopeList.cend(); ++scope) { - out << " type << "\""; - if (!scope->className.empty()) - out << " className=\"" << ErrorLogger::toxml(scope->className) << "\""; - if (scope->bodyStart) - out << " bodyStart=\"" << scope->bodyStart << '\"'; - if (scope->bodyEnd) - out << " bodyEnd=\"" << scope->bodyEnd << '\"'; - if (scope->nestedIn) - out << " nestedIn=\"" << scope->nestedIn << "\""; - if (scope->function) - out << " function=\"" << scope->function << "\""; - if (scope->definedType) - out << " definedType=\"" << scope->definedType << "\""; + outs += " type); + outs += "\""; + if (!scope->className.empty()) { + outs += " className=\""; + outs += ErrorLogger::toxml(scope->className); + outs += "\""; + } + if (scope->bodyStart) { + outs += " bodyStart=\""; + outs += ptr_to_string(scope->bodyStart); + outs += '\"'; + } + if (scope->bodyEnd) { + outs += " bodyEnd=\""; + outs += ptr_to_string(scope->bodyEnd); + outs += '\"'; + } + if (scope->nestedIn) { + outs += " nestedIn=\""; + outs += ptr_to_string(scope->nestedIn); + outs += "\""; + } + if (scope->function) { + outs += " function=\""; + outs += ptr_to_string(scope->function); + outs += "\""; + } + if (scope->definedType) { + outs += " definedType=\""; + outs += ptr_to_string(scope->definedType); + outs += "\""; + } if (scope->functionList.empty() && scope->varlist.empty()) - out << "/>" << std::endl; + outs += "/>\n"; else { - out << '>' << std::endl; + outs += ">\n"; if (!scope->functionList.empty()) { - out << " " << std::endl; + outs += " \n"; for (std::list::const_iterator function = scope->functionList.cbegin(); function != scope->functionList.cend(); ++function) { - out << " token - << "\" tokenDef=\"" << function->tokenDef - << "\" name=\"" << ErrorLogger::toxml(function->name()) << '\"'; - out << " type=\"" << (function->type == Function::eConstructor? "Constructor" : - function->type == Function::eCopyConstructor ? "CopyConstructor" : - function->type == Function::eMoveConstructor ? "MoveConstructor" : - function->type == Function::eOperatorEqual ? "OperatorEqual" : - function->type == Function::eDestructor ? "Destructor" : - function->type == Function::eFunction ? "Function" : - function->type == Function::eLambda ? "Lambda" : - "Unknown") << '\"'; + outs += " token); + outs += "\" tokenDef=\""; + outs += ptr_to_string(function->tokenDef); + outs += "\" name=\""; + outs += ErrorLogger::toxml(function->name()); + outs += '\"'; + outs += " type=\""; + outs += (function->type == Function::eConstructor? "Constructor" : + function->type == Function::eCopyConstructor ? "CopyConstructor" : + function->type == Function::eMoveConstructor ? "MoveConstructor" : + function->type == Function::eOperatorEqual ? "OperatorEqual" : + function->type == Function::eDestructor ? "Destructor" : + function->type == Function::eFunction ? "Function" : + function->type == Function::eLambda ? "Lambda" : + "Unknown"); + outs += '\"'; if (function->nestedIn->definedType) { if (function->hasVirtualSpecifier()) - out << " hasVirtualSpecifier=\"true\""; + outs += " hasVirtualSpecifier=\"true\""; else if (function->isImplicitlyVirtual()) - out << " isImplicitlyVirtual=\"true\""; + outs += " isImplicitlyVirtual=\"true\""; + } + if (function->access == AccessControl::Public || function->access == AccessControl::Protected || function->access == AccessControl::Private) { + outs += " access=\""; + outs += accessControlToString(function->access); + outs +="\""; } - if (function->access == AccessControl::Public || function->access == AccessControl::Protected || function->access == AccessControl::Private) - out << " access=\"" << accessControlToString(function->access) << "\""; if (function->isInlineKeyword()) - out << " isInlineKeyword=\"true\""; + outs += " isInlineKeyword=\"true\""; if (function->isStatic()) - out << " isStatic=\"true\""; + outs += " isStatic=\"true\""; if (function->isAttributeNoreturn()) - out << " isAttributeNoreturn=\"true\""; - if (const Function* overriddenFunction = function->getOverriddenFunction()) - out << " overriddenFunction=\"" << overriddenFunction << "\""; + outs += " isAttributeNoreturn=\"true\""; + if (const Function* overriddenFunction = function->getOverriddenFunction()) { + outs += " overriddenFunction=\""; + outs += ptr_to_string(overriddenFunction); + outs += "\""; + } if (function->argCount() == 0U) - out << "/>" << std::endl; + outs += "/>\n"; else { - out << ">" << std::endl; + outs += ">\n"; for (unsigned int argnr = 0; argnr < function->argCount(); ++argnr) { const Variable *arg = function->getArgumentVar(argnr); - out << " " << std::endl; + outs += " \n"; variables.insert(arg); } - out << " " << std::endl; + outs += " \n"; } } - out << " " << std::endl; + outs += " \n"; } if (!scope->varlist.empty()) { - out << " " << std::endl; - for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) - out << " " << std::endl; - out << " " << std::endl; + outs += " \n"; + for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { + outs += " \n"; + } + outs += " \n"; } - out << " " << std::endl; + outs += " \n"; } } - out << " " << std::endl; + outs += " \n"; if (!typeList.empty()) { - out << " \n"; + outs += " \n"; for (const Type& type:typeList) { - out << " \n"; + outs += "/>\n"; continue; } - out << ">\n"; + outs += ">\n"; for (const Type::BaseInfo& baseInfo: type.derivedFrom) { - out << " \n"; + outs += " \n"; + outs += " \n"; } - out << " \n"; + outs += " \n"; } // Variables.. for (const Variable *var : mVariableList) variables.insert(var); - out << " " << std::endl; + outs += " \n"; for (const Variable *var : variables) { if (!var) continue; - out << " nameToken() << '\"'; - out << " typeStartToken=\"" << var->typeStartToken() << '\"'; - out << " typeEndToken=\"" << var->typeEndToken() << '\"'; - out << " access=\"" << accessControlToString(var->mAccess) << '\"'; - out << " scope=\"" << var->scope() << '\"'; - if (var->valueType()) - out << " constness=\"" << var->valueType()->constness << '\"'; - out << " isArray=\"" << var->isArray() << '\"'; - out << " isClass=\"" << var->isClass() << '\"'; - out << " isConst=\"" << var->isConst() << '\"'; - out << " isExtern=\"" << var->isExtern() << '\"'; - out << " isPointer=\"" << var->isPointer() << '\"'; - out << " isReference=\"" << var->isReference() << '\"'; - out << " isStatic=\"" << var->isStatic() << '\"'; - out << " isVolatile=\"" << var->isVolatile() << '\"'; - out << "/>" << std::endl; + outs += " nameToken()); + outs += '\"'; + outs += " typeStartToken=\""; + outs += ptr_to_string(var->typeStartToken()); + outs += '\"'; + outs += " typeEndToken=\""; + outs += ptr_to_string(var->typeEndToken()); + outs += '\"'; + outs += " access=\""; + outs += accessControlToString(var->mAccess); + outs += '\"'; + outs += " scope=\""; + outs += ptr_to_string(var->scope()); + outs += '\"'; + if (var->valueType()) { + outs += " constness=\""; + outs += std::to_string(var->valueType()->constness); + outs += '\"'; + } + outs += " isArray=\""; + outs += bool_to_string(var->isArray()); + outs += '\"'; + outs += " isClass=\""; + outs += bool_to_string(var->isClass()); + outs += '\"'; + outs += " isConst=\""; + outs += bool_to_string(var->isConst()); + outs += '\"'; + outs += " isExtern=\""; + outs += bool_to_string(var->isExtern()); + outs += '\"'; + outs += " isPointer=\""; + outs += bool_to_string(var->isPointer()); + outs += '\"'; + outs += " isReference=\""; + outs += bool_to_string(var->isReference()); + outs += '\"'; + outs += " isStatic=\""; + outs += bool_to_string(var->isStatic()); + outs += '\"'; + outs += " isVolatile=\""; + outs += bool_to_string(var->isVolatile()); + outs += '\"'; + outs += "/>\n"; } - out << " " << std::endl; - out << std::resetiosflags(std::ios::boolalpha); + outs += " \n"; + + out << outs; } //--------------------------------------------------------------------------- @@ -7443,64 +7553,67 @@ bool ValueType::fromLibraryType(const std::string &typestr, const Settings &sett std::string ValueType::dump() const { - std::ostringstream ret; + std::string ret; switch (type) { case UNKNOWN_TYPE: return ""; case NONSTD: - ret << "valueType-type=\"nonstd\""; + ret += "valueType-type=\"nonstd\""; break; case POD: - ret << "valueType-type=\"pod\""; + ret += "valueType-type=\"pod\""; break; case RECORD: - ret << "valueType-type=\"record\""; + ret += "valueType-type=\"record\""; break; case SMART_POINTER: - ret << "valueType-type=\"smart-pointer\""; + ret += "valueType-type=\"smart-pointer\""; break; - case CONTAINER: - ret << "valueType-type=\"container\""; - ret << " valueType-containerId=\"" << container << "\""; + case CONTAINER: { + ret += "valueType-type=\"container\""; + ret += " valueType-containerId=\""; + ret += ptr_to_string(container); + ret += "\""; break; + } case ITERATOR: - ret << "valueType-type=\"iterator\""; + ret += "valueType-type=\"iterator\""; break; case VOID: - ret << "valueType-type=\"void\""; + ret += "valueType-type=\"void\""; break; case BOOL: - ret << "valueType-type=\"bool\""; + ret += "valueType-type=\"bool\""; break; case CHAR: - ret << "valueType-type=\"char\""; + ret += "valueType-type=\"char\""; break; case SHORT: - ret << "valueType-type=\"short\""; + ret += "valueType-type=\"short\""; break; case WCHAR_T: - ret << "valueType-type=\"wchar_t\""; + ret += "valueType-type=\"wchar_t\""; break; case INT: - ret << "valueType-type=\"int\""; + ret += "valueType-type=\"int\""; break; case LONG: - ret << "valueType-type=\"long\""; + ret += "valueType-type=\"long\""; break; case LONGLONG: - ret << "valueType-type=\"long long\""; + ret += "valueType-type=\"long long\""; break; case UNKNOWN_INT: - ret << "valueType-type=\"unknown int\""; + ret += "valueType-type=\"unknown int\""; break; case FLOAT: - ret << "valueType-type=\"float\""; + ret += "valueType-type=\"float\""; break; case DOUBLE: - ret << "valueType-type=\"double\""; + ret += "valueType-type=\"double\""; break; case LONGDOUBLE: - ret << "valueType-type=\"long double\""; + ret += "valueType-type=\"long double\""; break; } @@ -7508,36 +7621,51 @@ std::string ValueType::dump() const case Sign::UNKNOWN_SIGN: break; case Sign::SIGNED: - ret << " valueType-sign=\"signed\""; + ret += " valueType-sign=\"signed\""; break; case Sign::UNSIGNED: - ret << " valueType-sign=\"unsigned\""; + ret += " valueType-sign=\"unsigned\""; break; } - if (bits > 0) - ret << " valueType-bits=\"" << bits << '\"'; + if (bits > 0) { + ret += " valueType-bits=\""; + ret += std::to_string(bits); + ret += '\"'; + } - if (pointer > 0) - ret << " valueType-pointer=\"" << pointer << '\"'; + if (pointer > 0) { + ret += " valueType-pointer=\""; + ret += std::to_string(pointer); + ret += '\"'; + } - if (constness > 0) - ret << " valueType-constness=\"" << constness << '\"'; + if (constness > 0) { + ret += " valueType-constness=\""; + ret += std::to_string(constness); + ret += '\"'; + } if (reference == Reference::None) - ret << " valueType-reference=\"None\""; + ret += " valueType-reference=\"None\""; else if (reference == Reference::LValue) - ret << " valueType-reference=\"LValue\""; + ret += " valueType-reference=\"LValue\""; else if (reference == Reference::RValue) - ret << " valueType-reference=\"RValue\""; + ret += " valueType-reference=\"RValue\""; - if (typeScope) - ret << " valueType-typeScope=\"" << typeScope << '\"'; + if (typeScope) { + ret += " valueType-typeScope=\""; + ret += ptr_to_string(typeScope); + ret += '\"'; + } - if (!originalTypeName.empty()) - ret << " valueType-originalTypeName=\"" << ErrorLogger::toxml(originalTypeName) << '\"'; + if (!originalTypeName.empty()) { + ret += " valueType-originalTypeName=\""; + ret += ErrorLogger::toxml(originalTypeName); + ret += '\"'; + } - return ret.str(); + return ret; } bool ValueType::isConst(nonneg int indirect) const diff --git a/lib/token.cpp b/lib/token.cpp index a13f98763..0a8524f16 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1695,119 +1695,171 @@ std::string Token::astStringZ3() const void Token::printValueFlow(bool xml, std::ostream &out) const { + std::string outs; + int line = 0; if (xml) - out << " " << std::endl; + outs += " \n"; else - out << "\n\n##Value flow" << std::endl; + outs += "\n\n##Value flow\n"; for (const Token *tok = this; tok; tok = tok->next()) { const auto* const values = tok->mImpl->mValues; if (!values) continue; if (values->empty()) // Values might be removed by removeContradictions continue; - if (xml) - out << " " << std::endl; - else if (line != tok->linenr()) - out << "Line " << tok->linenr() << std::endl; + if (xml) { + outs += " "; + outs += '\n'; + } + else if (line != tok->linenr()) { + outs += "Line "; + outs += std::to_string(tok->linenr()); + outs += '\n'; + } line = tok->linenr(); if (!xml) { ValueFlow::Value::ValueKind valueKind = values->front().valueKind; const bool same = std::all_of(values->begin(), values->end(), [&](const ValueFlow::Value& value) { return value.valueKind == valueKind; }); - out << " " << tok->str() << " "; + outs += " "; + outs += tok->str(); + outs += " "; if (same) { switch (valueKind) { case ValueFlow::Value::ValueKind::Impossible: case ValueFlow::Value::ValueKind::Known: - out << "always "; + outs += "always "; break; case ValueFlow::Value::ValueKind::Inconclusive: - out << "inconclusive "; + outs += "inconclusive "; break; case ValueFlow::Value::ValueKind::Possible: - out << "possible "; + outs += "possible "; break; } } if (values->size() > 1U) - out << '{'; + outs += '{'; } for (const ValueFlow::Value& value : *values) { if (xml) { - out << " valueType() && tok->valueType()->sign == ValueType::UNSIGNED) - out << "intvalue=\"" << (MathLib::biguint)value.intvalue << '\"'; - else - out << "intvalue=\"" << value.intvalue << '\"'; + if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED) { + outs += "intvalue=\""; + outs += std::to_string(static_cast(value.intvalue)); + outs += '\"'; + } + else { + outs += "intvalue=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; + } break; case ValueFlow::Value::ValueType::TOK: - out << "tokvalue=\"" << value.tokvalue << '\"'; + outs += "tokvalue=\""; + outs += ptr_to_string(value.tokvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::FLOAT: - out << "floatvalue=\"" << value.floatValue << '\"'; + outs += "floatvalue=\""; + outs += std::to_string(value.floatValue); // TODO: should this be MathLib::toString()? + outs += '\"'; break; case ValueFlow::Value::ValueType::MOVED: - out << "movedvalue=\"" << ValueFlow::Value::toString(value.moveKind) << '\"'; + outs += "movedvalue=\""; + outs += ValueFlow::Value::toString(value.moveKind); + outs += '\"'; break; case ValueFlow::Value::ValueType::UNINIT: - out << "uninit=\"1\""; + outs += "uninit=\"1\""; break; case ValueFlow::Value::ValueType::BUFFER_SIZE: - out << "buffer-size=\"" << value.intvalue << "\""; + outs += "buffer-size=\""; + outs += std::to_string(value.intvalue); + outs += "\""; break; case ValueFlow::Value::ValueType::CONTAINER_SIZE: - out << "container-size=\"" << value.intvalue << '\"'; + outs += "container-size=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::ITERATOR_START: - out << "iterator-start=\"" << value.intvalue << '\"'; + outs += "iterator-start=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::ITERATOR_END: - out << "iterator-end=\"" << value.intvalue << '\"'; + outs += "iterator-end=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::LIFETIME: - out << "lifetime=\"" << value.tokvalue << '\"'; - out << " lifetime-scope=\"" << ValueFlow::Value::toString(value.lifetimeScope) << "\""; - out << " lifetime-kind=\"" << ValueFlow::Value::toString(value.lifetimeKind) << "\""; + outs += "lifetime=\""; + outs += ptr_to_string(value.tokvalue); + outs += '\"'; + outs += " lifetime-scope=\""; + outs += ValueFlow::Value::toString(value.lifetimeScope); + outs += "\""; + outs += " lifetime-kind=\""; + outs += ValueFlow::Value::toString(value.lifetimeKind); + outs += "\""; break; case ValueFlow::Value::ValueType::SYMBOLIC: - out << "symbolic=\"" << value.tokvalue << '\"'; - out << " symbolic-delta=\"" << value.intvalue << '\"'; + outs += "symbolic=\""; + outs += ptr_to_string(value.tokvalue); + outs += '\"'; + outs += " symbolic-delta=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; } - out << " bound=\"" << ValueFlow::Value::toString(value.bound) << "\""; - if (value.condition) - out << " condition-line=\"" << value.condition->linenr() << '\"'; + outs += " bound=\""; + outs += ValueFlow::Value::toString(value.bound); + outs += "\""; + if (value.condition) { + outs += " condition-line=\""; + outs += std::to_string(value.condition->linenr()); + outs += '\"'; + } if (value.isKnown()) - out << " known=\"true\""; + outs += " known=\"true\""; else if (value.isPossible()) - out << " possible=\"true\""; + outs += " possible=\"true\""; else if (value.isImpossible()) - out << " impossible=\"true\""; + outs += " impossible=\"true\""; else if (value.isInconclusive()) - out << " inconclusive=\"true\""; - out << " path=\"" << value.path << "\""; - out << "/>" << std::endl; + outs += " inconclusive=\"true\""; + + outs += " path=\""; + outs += std::to_string(value.path); + outs += "\""; + + outs += "/>\n"; } else { if (&value != &values->front()) - out << ","; - out << value.toString(); + outs += ","; + outs += value.toString(); } } if (xml) - out << " " << std::endl; + outs += " \n"; else if (values->size() > 1U) - out << '}' << std::endl; + outs += "}\n"; else - out << std::endl; + outs += '\n'; } if (xml) - out << " " << std::endl; + outs += " \n"; + + out << outs; } const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Settings *settings) const diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 86a843c96..81fe6abba 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4014,8 +4014,8 @@ void Tokenizer::simplifyTemplates() /** Class used in Tokenizer::setVarIdPass1 */ class VariableMap { private: - std::map mVariableId; - std::map mVariableId_global; + std::unordered_map mVariableId; + std::unordered_map mVariableId_global; std::stack>> mScopeInfo; mutable nonneg int mVarId{}; public: @@ -4027,7 +4027,7 @@ public: return mVariableId.find(varname) != mVariableId.end(); } - const std::map& map(bool global) const { + const std::unordered_map& map(bool global) const { return global ? mVariableId_global : mVariableId; } nonneg int getVarId() const { @@ -4067,7 +4067,7 @@ void VariableMap::addVariable(const std::string& varname, bool globalNamespace) mVariableId_global[varname] = mVariableId[varname]; return; } - std::map::iterator it = mVariableId.find(varname); + std::unordered_map::iterator it = mVariableId.find(varname); if (it == mVariableId.end()) { mScopeInfo.top().emplace_back(varname, 0); mVariableId[varname] = ++mVarId; @@ -4330,7 +4330,7 @@ void Tokenizer::setVarIdClassDeclaration(Token* const startToken, --indentlevel; inEnum = false; } else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %name% [({]")) { - const std::map::const_iterator it = variableMap.map(false).find(tok->str()); + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); if (it != variableMap.map(false).end()) { tok->varId(it->second); } @@ -4348,7 +4348,7 @@ void Tokenizer::setVarIdClassDeclaration(Token* const startToken, } if (!inEnum) { - const std::map::const_iterator it = variableMap.map(false).find(tok->str()); + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); if (it != variableMap.map(false).end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); @@ -4692,7 +4692,7 @@ void Tokenizer::setVarIdPass1() while (tok != end) { if (tok->isName() && !(Token::simpleMatch(tok->next(), "<") && Token::Match(tok->tokAt(-1), ":: %name%"))) { - const std::map::const_iterator it = variableMap.map(false).find(tok->str()); + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); if (it != variableMap.map(false).end()) tok->varId(it->second); } @@ -4757,7 +4757,7 @@ void Tokenizer::setVarIdPass1() if ((!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) && !Token::simpleMatch(tok->next(), ": ;")) { - const std::map::const_iterator it = variableMap.map(globalNamespace).find(tok->str()); + const std::unordered_map::const_iterator it = variableMap.map(globalNamespace).find(tok->str()); if (it != variableMap.map(globalNamespace).end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); @@ -5797,134 +5797,223 @@ void Tokenizer::dump(std::ostream &out) const // The idea is not that this will be readable for humans. It's a // data dump that 3rd party tools could load and get useful info from. + std::string outs; + std::set containers; // tokens.. - out << " " << std::endl; + outs += " "; + outs += '\n'; for (const Token *tok = list.front(); tok; tok = tok->next()) { - out << " linenr() << "\" column=\"" << tok->column() << "\""; - out << " str=\"" << ErrorLogger::toxml(tok->str()) << '\"'; - out << " scope=\"" << tok->scope() << '\"'; + outs += " linenr()); + outs += "\" column=\""; + outs += std::to_string(tok->column()); + outs += "\""; + + outs += " str=\""; + outs += ErrorLogger::toxml(tok->str()); + outs += '\"'; + + outs += " scope=\""; + outs += ptr_to_string(tok->scope()); + outs += '\"'; if (tok->isName()) { - out << " type=\"name\""; + outs += " type=\"name\""; if (tok->isUnsigned()) - out << " isUnsigned=\"true\""; + outs += " isUnsigned=\"true\""; else if (tok->isSigned()) - out << " isSigned=\"true\""; + outs += " isSigned=\"true\""; } else if (tok->isNumber()) { - out << " type=\"number\""; + outs += " type=\"number\""; if (MathLib::isInt(tok->str())) - out << " isInt=\"true\""; + outs += " isInt=\"true\""; if (MathLib::isFloat(tok->str())) - out << " isFloat=\"true\""; - } else if (tok->tokType() == Token::eString) - out << " type=\"string\" strlen=\"" << Token::getStrLength(tok) << '\"'; + outs += " isFloat=\"true\""; + } else if (tok->tokType() == Token::eString) { + outs += " type=\"string\" strlen=\""; + outs += std::to_string(Token::getStrLength(tok)); + outs += '\"'; + } else if (tok->tokType() == Token::eChar) - out << " type=\"char\""; + outs += " type=\"char\""; else if (tok->isBoolean()) - out << " type=\"boolean\""; + outs += " type=\"boolean\""; else if (tok->isOp()) { - out << " type=\"op\""; + outs += " type=\"op\""; if (tok->isArithmeticalOp()) - out << " isArithmeticalOp=\"true\""; + outs += " isArithmeticalOp=\"true\""; else if (tok->isAssignmentOp()) - out << " isAssignmentOp=\"true\""; + outs += " isAssignmentOp=\"true\""; else if (tok->isComparisonOp()) - out << " isComparisonOp=\"true\""; + outs += " isComparisonOp=\"true\""; else if (tok->tokType() == Token::eLogicalOp) - out << " isLogicalOp=\"true\""; + outs += " isLogicalOp=\"true\""; } if (tok->isCast()) - out << " isCast=\"true\""; + outs += " isCast=\"true\""; if (tok->isExternC()) - out << " externLang=\"C\""; + outs += " externLang=\"C\""; if (tok->isExpandedMacro()) - out << " isExpandedMacro=\"true\""; + outs += " isExpandedMacro=\"true\""; if (tok->isTemplateArg()) - out << " isTemplateArg=\"true\""; + outs += " isTemplateArg=\"true\""; if (tok->isRemovedVoidParameter()) - out << " isRemovedVoidParameter=\"true\""; + outs += " isRemovedVoidParameter=\"true\""; if (tok->isSplittedVarDeclComma()) - out << " isSplittedVarDeclComma=\"true\""; + outs += " isSplittedVarDeclComma=\"true\""; if (tok->isSplittedVarDeclEq()) - out << " isSplittedVarDeclEq=\"true\""; + outs += " isSplittedVarDeclEq=\"true\""; if (tok->isImplicitInt()) - out << " isImplicitInt=\"true\""; + outs += " isImplicitInt=\"true\""; if (tok->isComplex()) - out << " isComplex=\"true\""; + outs += " isComplex=\"true\""; if (tok->isRestrict()) - out << " isRestrict=\"true\""; + outs += " isRestrict=\"true\""; if (tok->isAtomic()) - out << " isAtomic=\"true\""; + outs += " isAtomic=\"true\""; if (tok->isAttributeExport()) - out << " isAttributeExport=\"true\""; - if (tok->link()) - out << " link=\"" << tok->link() << '\"'; - if (tok->varId() > 0) - out << " varId=\"" << tok->varId() << '\"'; - if (tok->exprId() > 0) - out << " exprId=\"" << tok->exprId() << '\"'; - if (tok->variable()) - out << " variable=\"" << tok->variable() << '\"'; - if (tok->function()) - out << " function=\"" << tok->function() << '\"'; - if (!tok->values().empty()) - out << " values=\"" << &tok->values() << '\"'; - if (tok->type()) - out << " type-scope=\"" << tok->type()->classScope << '\"'; - if (tok->astParent()) - out << " astParent=\"" << tok->astParent() << '\"'; - if (tok->astOperand1()) - out << " astOperand1=\"" << tok->astOperand1() << '\"'; - if (tok->astOperand2()) - out << " astOperand2=\"" << tok->astOperand2() << '\"'; - if (!tok->originalName().empty()) - out << " originalName=\"" << tok->originalName() << '\"'; + outs += " isAttributeExport=\"true\""; + if (tok->link()) { + outs += " link=\""; + outs += ptr_to_string(tok->link()); + outs += '\"'; + } + if (tok->varId() > 0) { + outs += " varId=\""; + outs += std::to_string(tok->varId()); + outs += '\"'; + } + if (tok->exprId() > 0) { + outs += " exprId=\""; + outs += std::to_string(tok->exprId()); + outs += '\"'; + } + if (tok->variable()) { + outs += " variable=\""; + outs += ptr_to_string(tok->variable()); + outs += '\"'; + } + if (tok->function()) { + outs += " function=\""; + outs += ptr_to_string(tok->function()); + outs += '\"'; + } + if (!tok->values().empty()) { + outs += " values=\""; + outs += ptr_to_string(&tok->values()); + outs += '\"'; + } + if (tok->type()) { + outs += " type-scope=\""; + outs += ptr_to_string(tok->type()->classScope); + outs += '\"'; + } + if (tok->astParent()) { + outs += " astParent=\""; + outs += ptr_to_string(tok->astParent()); + outs += '\"'; + } + if (tok->astOperand1()) { + outs += " astOperand1=\""; + outs += ptr_to_string(tok->astOperand1()); + outs += '\"'; + } + if (tok->astOperand2()) { + outs += " astOperand2=\""; + outs += ptr_to_string(tok->astOperand2()); + outs += '\"'; + } + if (!tok->originalName().empty()) { + outs += " originalName=\""; + outs += tok->originalName(); + outs += '\"'; + } if (tok->valueType()) { const std::string vt = tok->valueType()->dump(); - if (!vt.empty()) - out << ' ' << vt; + if (!vt.empty()) { + outs += ' '; + outs += vt; + } containers.insert(tok->valueType()->container); } if (!tok->varId() && tok->scope()->isExecutable() && Token::Match(tok, "%name% (")) { if (mSettings->library.isnoreturn(tok)) - out << " noreturn=\"true\""; + outs += " noreturn=\"true\""; } - out << "/>" << std::endl; + outs += "/>"; + outs += '\n'; } - out << " " << std::endl; + outs += " "; + outs += '\n'; + + out << outs; + outs.clear(); mSymbolDatabase->printXml(out); containers.erase(nullptr); if (!containers.empty()) { - out << " \n"; + outs += " "; + outs += '\n'; for (const Library::Container* c: containers) { - out << " arrayLike_indexOp ? "true" : "false") << "\" " - << "std-string-like=\"" << (c->stdStringLike ? "true" : "false") << "\"/>\n"; + outs += " arrayLike_indexOp ? "true" : "false"); + outs += "\" "; + outs += "std-string-like=\""; + outs +=(c->stdStringLike ? "true" : "false"); + outs += "\"/>"; + outs += '\n'; } - out << " \n"; + outs += " "; + outs += '\n'; } if (list.front()) list.front()->printValueFlow(true, out); if (!mTypedefInfo.empty()) { - out << " " << std::endl; + outs += " "; + outs += '\n'; for (const TypedefInfo &typedefInfo: mTypedefInfo) { - out << " " << std::endl; + outs += " " << std::endl; + outs += " "; + outs += '\n'; } - out << mTemplateSimplifier->dump(); + outs += mTemplateSimplifier->dump(); + + out << outs; } void Tokenizer::simplifyHeadersAndUnusedTemplates() diff --git a/lib/utils.h b/lib/utils.h index e942c82f1..3f53f6fbc 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -269,4 +270,76 @@ std::size_t getArrayLength(const T (& /*unused*/)[size]) return size; } +/** this is meant as a replacement for when we cannot print a pointer via the stream insertion operator (operator<<) for performance reasons */ +// TODO: should give portable value / only used by Tokenizer::dump() and underlying functions - something deterministic would also help with comparing the output +static inline std::string ptr_to_string(const void* p) +{ +#if (defined(__APPLE__) && defined(__MACH__)) + if (p == nullptr) + return "0x0"; +#elif !defined(_WIN32) || defined(__MINGW32__) + if (p == nullptr) + return "0"; +#endif + + static constexpr int ptr_size = sizeof(void*); + +#if defined(_WIN32) && !defined(__MINGW32__) + // two characters of each byte / contains terminating \0 + static constexpr int buf_size = (ptr_size * 2) + 1; +#else + // two characters of each byte / contains 0x prefix and contains terminating \0 + static constexpr int buf_size = (ptr_size * 2) + 2 + 1; +#endif + char buf[buf_size]; + + // needs to be signed so we don't underflow in padding loop + int idx = sizeof(buf) - 1; + buf[idx--] = '\0'; // terminate string + + uintptr_t l = reinterpret_cast(p); + while (l != 0) + { + char c; + const uintptr_t temp = l % 16; // get the remainder + if (temp < 10) { + // 0-9 + c = '0' + temp; + } + else { +#if !defined(_WIN32) || defined(__MINGW32__) + // a-f + c = 'a' + (temp - 10); +#else + // A-F + c = 'A' + (temp - 10); +#endif + } + buf[idx--] = c; // store in reverse order + l = l / 16; + } + +#if defined(_WIN32) && !defined(__MINGW32__) + // pad address with 0 + while (idx >= 0) { + buf[idx--] = '0'; + } + + // 000000F0A61FF122 or 0230FB33 + return buf; +#else + // add 0x prefix + buf[idx--] = 'x'; + buf[idx--] = '0'; + + // 0x7ffc5aa334d8 + return &buf[idx+1]; +#endif +} + +static inline std::string bool_to_string(bool b) +{ + return b ? "true" : "false"; +} + #endif diff --git a/test/testutils.cpp b/test/testutils.cpp index 09f92d381..b4c143441 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -36,6 +38,7 @@ private: TEST_CASE(isStringLiteral); TEST_CASE(isCharLiteral); TEST_CASE(strToInt); + TEST_CASE(ptrToString); } void isValidGlobPattern() const { @@ -330,6 +333,58 @@ private: ASSERT_EQUALS("out of range (stoull)", err); } } + + void ptrToString() const + { + struct Dummy {}; + // stack address + { + const Dummy d; + const Dummy* const dp = &d; + std::ostringstream oss; + oss << dp; + ASSERT_EQUALS(oss.str(), ptr_to_string(dp)); + } + // highest address + { + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const void* const p = reinterpret_cast(std::numeric_limits::max()); + std::ostringstream oss; + oss << p; + ASSERT_EQUALS(oss.str(), ptr_to_string(p)); + } + // same in-between address + { + const Dummy d; + const Dummy* dp = &d; + dp = dp - ((unsigned long long)dp / 2); + std::ostringstream oss; + oss << dp; + ASSERT_EQUALS(oss.str(), ptr_to_string(dp)); + } + // lowest address + { + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const void* const p = reinterpret_cast(std::numeric_limits::min() + 1); + std::ostringstream oss; + oss << p; + ASSERT_EQUALS(oss.str(), ptr_to_string(p)); + } + // heap address + { + const auto dp = std::unique_ptr(new Dummy); + std::ostringstream oss; + oss << dp.get(); + ASSERT_EQUALS(oss.str(), ptr_to_string(dp.get())); + } + // NULL pointer + { + const Dummy* const dp = nullptr; + std::ostringstream oss; + oss << dp; + ASSERT_EQUALS(oss.str(), ptr_to_string(dp)); + } + } }; REGISTER_TEST(TestUtils)