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.
This commit is contained in:
Oliver Stöneberg 2023-08-31 11:54:46 +02:00 committed by GitHub
parent 98401a3046
commit 0fadf9ed25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 681 additions and 284 deletions

View File

@ -315,8 +315,8 @@ static void createDumpFile(const Settings& settings,
break;
}
fdump << "<?xml version=\"1.0\"?>" << std::endl;
fdump << "<dumps" << language << ">" << std::endl;
fdump << "<?xml version=\"1.0\"?>\n";
fdump << "<dumps" << language << ">\n";
fdump << " <platform"
<< " name=\"" << settings.platform.toString() << '\"'
<< " char_bit=\"" << settings.platform.char_bit << '\"'
@ -325,7 +325,7 @@ static void createDumpFile(const Settings& settings,
<< " long_bit=\"" << settings.platform.long_bit << '\"'
<< " long_long_bit=\"" << settings.platform.long_long_bit << '\"'
<< " pointer_bit=\"" << (settings.platform.sizeof_pointer * settings.platform.char_bit) << '\"'
<< "/>\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 << "<dump cfg=\"\">" << std::endl;
fdump << "<dump cfg=\"\">\n";
for (const ErrorMessage& errmsg: compilerWarnings)
fdump << " <clang-warning file=\"" << toxml(errmsg.callStack.front().getfile()) << "\" line=\"" << errmsg.callStack.front().line << "\" column=\"" << errmsg.callStack.front().column << "\" message=\"" << toxml(errmsg.shortMessage()) << "\"/>\n";
fdump << " <standards>" << std::endl;
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>" << std::endl;
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
fdump << " </standards>" << std::endl;
fdump << " <standards>\n";
fdump << " <c version=\"" << mSettings.standards.getC() << "\"/>\n";
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>\n";
fdump << " </standards>\n";
tokenizer.dump(fdump);
fdump << "</dump>" << std::endl;
fdump << "</dumps>" << std::endl;
fdump << "</dump>\n";
fdump << "</dumps>\n";
fdump.close();
}

View File

@ -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 << "&lt;";
xml += "&lt;";
break;
case '>':
xml << "&gt;";
xml += "&gt;";
break;
case '&':
xml << "&amp;";
xml += "&amp;";
break;
case '\"':
xml << "&quot;";
xml += "&quot;";
break;
case '\'':
xml << "&apos;";
xml += "&apos;";
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<std::string> &files)

View File

@ -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<const Variable *> variables;
// Scopes..
out << " <scopes>" << std::endl;
outs += " <scopes>\n";
for (std::list<Scope>::const_iterator scope = scopeList.cbegin(); scope != scopeList.cend(); ++scope) {
out << " <scope";
out << " id=\"" << &*scope << "\"";
out << " type=\"" << scope->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 += " <scope";
outs += " id=\"";
outs += ptr_to_string(&*scope);
outs += "\"";
outs += " type=\"";
outs += scopeTypeToString(scope->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 << " <functionList>" << std::endl;
outs += " <functionList>\n";
for (std::list<Function>::const_iterator function = scope->functionList.cbegin(); function != scope->functionList.cend(); ++function) {
out << " <function id=\"" << &*function
<< "\" token=\"" << function->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 += " <function id=\"";
outs += ptr_to_string(&*function);
outs += "\" token=\"";
outs += ptr_to_string(function->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 << " <arg nr=\"" << argnr+1 << "\" variable=\"" << arg << "\"/>" << std::endl;
outs += " <arg nr=\"";
outs += std::to_string(argnr+1);
outs += "\" variable=\"";
outs += ptr_to_string(arg);
outs += "\"/>\n";
variables.insert(arg);
}
out << " </function>" << std::endl;
outs += " </function>\n";
}
}
out << " </functionList>" << std::endl;
outs += " </functionList>\n";
}
if (!scope->varlist.empty()) {
out << " <varlist>" << std::endl;
for (std::list<Variable>::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var)
out << " <var id=\"" << &*var << "\"/>" << std::endl;
out << " </varlist>" << std::endl;
outs += " <varlist>\n";
for (std::list<Variable>::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) {
outs += " <var id=\"";
outs += ptr_to_string(&*var);
outs += "\"/>\n";
}
outs += " </varlist>\n";
}
out << " </scope>" << std::endl;
outs += " </scope>\n";
}
}
out << " </scopes>" << std::endl;
outs += " </scopes>\n";
if (!typeList.empty()) {
out << " <types>\n";
outs += " <types>\n";
for (const Type& type:typeList) {
out << " <type id=\"" << &type << "\" classScope=\"" << type.classScope << "\"";
outs += " <type id=\"";
outs += ptr_to_string(&type);
outs += "\" classScope=\"";
outs += ptr_to_string(type.classScope);
outs += "\"";
if (type.derivedFrom.empty()) {
out << "/>\n";
outs += "/>\n";
continue;
}
out << ">\n";
outs += ">\n";
for (const Type::BaseInfo& baseInfo: type.derivedFrom) {
out << " <derivedFrom"
<< " access=\"" << accessControlToString(baseInfo.access) << "\""
<< " type=\"" << baseInfo.type << "\""
<< " isVirtual=\"" << (baseInfo.isVirtual ? "true" : "false") << "\""
<< " nameTok=\"" << baseInfo.nameTok << "\""
<< "/>\n";
outs += " <derivedFrom";
outs += " access=\"";
outs += accessControlToString(baseInfo.access);
outs += "\"";
outs += " type=\"";
outs += ptr_to_string(baseInfo.type);
outs += "\"";
outs += " isVirtual=\"";
outs += bool_to_string(baseInfo.isVirtual);
outs += "\"";
outs += " nameTok=\"";
outs += ptr_to_string(baseInfo.nameTok);
outs += "\"";
outs += "/>\n";
}
out << " </type>\n";
outs += " </type>\n";
}
out << " </types>\n";
outs += " </types>\n";
}
// Variables..
for (const Variable *var : mVariableList)
variables.insert(var);
out << " <variables>" << std::endl;
outs += " <variables>\n";
for (const Variable *var : variables) {
if (!var)
continue;
out << " <var id=\"" << var << '\"';
out << " nameToken=\"" << var->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 += " <var id=\"";
outs += ptr_to_string(var);
outs += '\"';
outs += " nameToken=\"";
outs += ptr_to_string(var->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 << " </variables>" << std::endl;
out << std::resetiosflags(std::ios::boolalpha);
outs += " </variables>\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

View File

@ -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 << " <valueflow>" << std::endl;
outs += " <valueflow>\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 << " <values id=\"" << values << "\">" << std::endl;
else if (line != tok->linenr())
out << "Line " << tok->linenr() << std::endl;
if (xml) {
outs += " <values id=\"";
outs += ptr_to_string(values);
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 << " <value ";
outs += " <value ";
switch (value.valueType) {
case ValueFlow::Value::ValueType::INT:
if (tok->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<MathLib::biguint>(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 << " </values>" << std::endl;
outs += " </values>\n";
else if (values->size() > 1U)
out << '}' << std::endl;
outs += "}\n";
else
out << std::endl;
outs += '\n';
}
if (xml)
out << " </valueflow>" << std::endl;
outs += " </valueflow>\n";
out << outs;
}
const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Settings *settings) const

View File

@ -4014,8 +4014,8 @@ void Tokenizer::simplifyTemplates()
/** Class used in Tokenizer::setVarIdPass1 */
class VariableMap {
private:
std::map<std::string, nonneg int> mVariableId;
std::map<std::string, nonneg int> mVariableId_global;
std::unordered_map<std::string, nonneg int> mVariableId;
std::unordered_map<std::string, nonneg int> mVariableId_global;
std::stack<std::vector<std::pair<std::string, nonneg int>>> mScopeInfo;
mutable nonneg int mVarId{};
public:
@ -4027,7 +4027,7 @@ public:
return mVariableId.find(varname) != mVariableId.end();
}
const std::map<std::string, nonneg int>& map(bool global) const {
const std::unordered_map<std::string, nonneg int>& 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<std::string, nonneg int>::iterator it = mVariableId.find(varname);
std::unordered_map<std::string, nonneg int>::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<std::string, nonneg int>::const_iterator it = variableMap.map(false).find(tok->str());
const std::unordered_map<std::string, nonneg int>::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<std::string, nonneg int>::const_iterator it = variableMap.map(false).find(tok->str());
const std::unordered_map<std::string, nonneg int>::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<std::string, nonneg int>::const_iterator it = variableMap.map(false).find(tok->str());
const std::unordered_map<std::string, nonneg int>::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<std::string, nonneg int>::const_iterator it = variableMap.map(globalNamespace).find(tok->str());
const std::unordered_map<std::string, nonneg int>::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<const Library::Container*> containers;
// tokens..
out << " <tokenlist>" << std::endl;
outs += " <tokenlist>";
outs += '\n';
for (const Token *tok = list.front(); tok; tok = tok->next()) {
out << " <token id=\"" << tok << "\" file=\"" << ErrorLogger::toxml(list.file(tok)) << "\" linenr=\"" << tok->linenr() << "\" column=\"" << tok->column() << "\"";
out << " str=\"" << ErrorLogger::toxml(tok->str()) << '\"';
out << " scope=\"" << tok->scope() << '\"';
outs += " <token id=\"";
outs += ptr_to_string(tok);
outs += "\" file=\"";
outs += ErrorLogger::toxml(list.file(tok));
outs += "\" linenr=\"";
outs += std::to_string(tok->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 << " </tokenlist>" << std::endl;
outs += " </tokenlist>";
outs += '\n';
out << outs;
outs.clear();
mSymbolDatabase->printXml(out);
containers.erase(nullptr);
if (!containers.empty()) {
out << " <containers>\n";
outs += " <containers>";
outs += '\n';
for (const Library::Container* c: containers) {
out << " <container id=\"" << c << "\" array-like-index-op=\""
<< (c->arrayLike_indexOp ? "true" : "false") << "\" "
<< "std-string-like=\"" << (c->stdStringLike ? "true" : "false") << "\"/>\n";
outs += " <container id=\"";
outs += ptr_to_string(c);
outs += "\" array-like-index-op=\"";
outs += (c->arrayLike_indexOp ? "true" : "false");
outs += "\" ";
outs += "std-string-like=\"";
outs +=(c->stdStringLike ? "true" : "false");
outs += "\"/>";
outs += '\n';
}
out << " </containers>\n";
outs += " </containers>";
outs += '\n';
}
if (list.front())
list.front()->printValueFlow(true, out);
if (!mTypedefInfo.empty()) {
out << " <typedef-info>" << std::endl;
outs += " <typedef-info>";
outs += '\n';
for (const TypedefInfo &typedefInfo: mTypedefInfo) {
out << " <info"
<< " name=\"" << typedefInfo.name << "\""
<< " file=\"" << ErrorLogger::toxml(typedefInfo.filename) << "\""
<< " line=\"" << typedefInfo.lineNumber << "\""
<< " column=\"" << typedefInfo.column << "\""
<< " used=\"" << (typedefInfo.used?1:0) << "\""
<< "/>" << std::endl;
outs += " <info";
outs += " name=\"";
outs += typedefInfo.name;
outs += "\"";
outs += " file=\"";
outs += ErrorLogger::toxml(typedefInfo.filename);
outs += "\"";
outs += " line=\"";
outs += std::to_string(typedefInfo.lineNumber);
outs += "\"";
outs += " column=\"";
outs += std::to_string(typedefInfo.column);
outs += "\"";
outs += " used=\"";
outs += std::to_string(typedefInfo.used?1:0);
outs += "\"";
outs += "/>";
outs += '\n';
}
out << " </typedef-info>" << std::endl;
outs += " </typedef-info>";
outs += '\n';
}
out << mTemplateSimplifier->dump();
outs += mTemplateSimplifier->dump();
out << outs;
}
void Tokenizer::simplifyHeadersAndUnusedTemplates()

View File

@ -26,6 +26,7 @@
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <limits>
#include <stdexcept>
@ -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<uintptr_t>(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

View File

@ -22,6 +22,8 @@
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
@ -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<void*>(std::numeric_limits<uintptr_t>::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<void*>(std::numeric_limits<uintptr_t>::min() + 1);
std::ostringstream oss;
oss << p;
ASSERT_EQUALS(oss.str(), ptr_to_string(p));
}
// heap address
{
const auto dp = std::unique_ptr<Dummy>(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)