/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2018 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
//---------------------------------------------------------------------------
#include "ctu.h"
#include "astutils.h"
#include "symboldatabase.h"
#include
//---------------------------------------------------------------------------
std::string CTU::getFunctionId(const Tokenizer *tokenizer, const Function *function)
{
return tokenizer->list.file(function->tokenDef) + ':' + MathLib::toString(function->tokenDef->linenr());
}
bool CTU::findPath(const CTU::FileInfo::FunctionCall &from,
const CTU::FileInfo::UnsafeUsage &to,
const std::map> &nestedCalls)
{
if (from.functionId == to.functionId && from.argnr == to.argnr)
return true;
const std::map>::const_iterator nc = nestedCalls.find(from.functionId);
if (nc == nestedCalls.end())
return false;
for (std::list::const_iterator it = nc->second.begin(); it != nc->second.end(); ++it) {
if (from.functionId == it->id && from.argnr == it->argnr && it->id2 == to.functionId && it->argnr2 == to.argnr)
return true;
}
return false;
}
CTU::FileInfo::Location::Location(const Tokenizer *tokenizer, const Token *tok)
{
fileName = tokenizer->list.file(tok);
linenr = tok->linenr();
}
std::string CTU::FileInfo::toString() const
{
std::ostringstream out;
// Function calls..
for (const CTU::FileInfo::FunctionCall &functionCall : functionCalls) {
out << " \n";
}
// Nested calls..
for (const CTU::FileInfo::NestedCall &nestedCall : nestedCalls) {
out << " \n";
}
return out.str();
}
std::string CTU::FileInfo::UnsafeUsage::toString() const
{
std::ostringstream out;
out << " \n";
return out.str();
}
CTU::FileInfo::NestedCall::NestedCall(const Tokenizer *tokenizer, const Scope *scope, unsigned int argnr_, const Token *tok)
:
id(getFunctionId(tokenizer, scope->function)),
functionName(scope->className),
argnr(argnr_),
argnr2(0),
location(CTU::FileInfo::Location(tokenizer, tok))
{
}
void CTU::FileInfo::loadFromXml(const tinyxml2::XMLElement *xmlElement)
{
for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) {
const char *id = e->Attribute("id");
if (!id)
continue;
const char *functionName = e->Attribute("functionName");
if (!functionName)
continue;
const char *argnr = e->Attribute("argnr");
if (!argnr || !MathLib::isInt(argnr))
continue;
const char *fileName = e->Attribute("fileName");
if (!fileName)
continue;
const char *linenr = e->Attribute("linenr");
if (!linenr || !MathLib::isInt(linenr))
continue;
if (std::strcmp(e->Name(), "function-call") == 0) {
FunctionCall functionCall;
functionCall.functionId = id;
functionCall.functionName = functionName;
functionCall.argnr = std::atoi(argnr);
const char *argExpr = e->Attribute("argExpr");
if (!argExpr)
continue;
functionCall.argumentExpression =
functionCall.valueType = (ValueFlow::Value::ValueType)std::atoi(e->Attribute("valueType"));
functionCall.argvalue = MathLib::toLongNumber(e->Attribute("argvalue"));
functionCall.location.fileName = fileName;
functionCall.location.linenr = std::atoi(linenr);
functionCalls.push_back(functionCall);
} else if (std::strcmp(e->Name(), "nested-call") == 0) {
NestedCall nestedCall;
nestedCall.functionName = functionName;
nestedCall.id = id;
const char *id2 = e->Attribute("id2");
if (!id2)
continue;
nestedCall.id2 = id2;
nestedCall.argnr = std::atoi(argnr);
const char *argnr2 = e->Attribute("argnr2");
if (!argnr2 || !MathLib::isInt(argnr2))
continue;
nestedCall.argnr2 = std::atoi(argnr2);
nestedCall.location.fileName = fileName;
nestedCall.location.linenr = std::atoi(linenr);
nestedCalls.push_back(nestedCall);
}
}
}
std::map> CTU::FileInfo::getNestedCallsMap() const
{
std::map> ret;
for (const CTU::FileInfo::NestedCall &nc : nestedCalls)
ret[nc.id].push_back(nc);
return ret;
}
std::list CTU::loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement)
{
std::list ret;
for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) {
if (std::strcmp(e->Name(), "unsafe-usage") != 0)
continue;
const char *id = e->Attribute("id");
if (!id)
continue;
const char *argnr = e->Attribute("argnr");
if (!argnr || !MathLib::isInt(argnr))
continue;
const char *argname = e->Attribute("argname");
if (!argname)
continue;
const char *fileName = e->Attribute("fileName");
if (!fileName)
continue;
const char *linenr = e->Attribute("linenr");
if (!linenr || !MathLib::isInt(linenr))
continue;
ret.push_back(FileInfo::UnsafeUsage(id, std::atoi(argnr), argname, FileInfo::Location(fileName, std::atoi(linenr))));
}
return ret;
}
static int isCallFunction(const Scope *scope, int argnr, const Token **tok)
{
const Variable * const argvar = scope->function->getArgumentVar(argnr);
if (!argvar->isPointer())
return -1;
for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) {
if (tok2->variable() != argvar)
continue;
if (!Token::Match(tok2->previous(), "[(,] %var% [,)]"))
break;
int argnr2 = 1;
const Token *prev = tok2;
while (prev && prev->str() != "(") {
if (Token::Match(prev,"]|)"))
prev = prev->link();
else if (prev->str() == ",")
++argnr2;
prev = prev->previous();
}
if (!prev || !Token::Match(prev->previous(), "%name% ("))
break;
if (!prev->astOperand1() || !prev->astOperand1()->function())
break;
*tok = prev->previous();
return argnr2;
}
return -1;
}
CTU::FileInfo *CTU::getFileInfo(const Tokenizer *tokenizer)
{
const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase();
FileInfo *fileInfo = new FileInfo;
// Parse all functions in TU
for (const Scope &scope : symbolDatabase->scopeList) {
if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function)
continue;
const Function *const function = scope.function;
// source function calls
for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) {
if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand2())
continue;
if (!tok->astOperand1()->function())
continue;
const std::vector args(getArguments(tok->previous()));
for (int argnr = 0; argnr < args.size(); ++argnr) {
const Token *argtok = args[argnr];
if (!argtok)
continue;
if (argtok->hasKnownIntValue()) {
struct FileInfo::FunctionCall functionCall;
functionCall.valueType = ValueFlow::Value::INT;
functionCall.functionId = getFunctionId(tokenizer, tok->astOperand1()->function());
functionCall.functionName = tok->astOperand1()->expressionString();
functionCall.location.fileName = tokenizer->list.file(tok);
functionCall.location.linenr = tok->linenr();
functionCall.argnr = argnr + 1;
functionCall.argumentExpression = argtok->expressionString();
functionCall.argvalue = argtok->values().front().intvalue;
fileInfo->functionCalls.push_back(functionCall);
continue;
}
// pointer to uninitialized data..
if (!argtok->isUnaryOp("&"))
continue;
argtok = argtok->astOperand1();
if (!argtok || !argtok->valueType() || argtok->valueType()->pointer != 0)
continue;
if (argtok->values().size() != 1U)
continue;
const ValueFlow::Value &v = argtok->values().front();
if (v.valueType == ValueFlow::Value::UNINIT && !v.isInconclusive()) {
struct FileInfo::FunctionCall functionCall;
functionCall.valueType = ValueFlow::Value::UNINIT;
functionCall.functionId = getFunctionId(tokenizer, tok->astOperand1()->function());
functionCall.functionName = tok->astOperand1()->expressionString();
functionCall.location.fileName = tokenizer->list.file(tok);
functionCall.location.linenr = tok->linenr();
functionCall.argnr = argnr + 1;
functionCall.argvalue = 0;
functionCall.argumentExpression = argtok->expressionString();
fileInfo->functionCalls.push_back(functionCall);
continue;
}
}
}
// Nested function calls
for (int argnr = 0; argnr < function->argCount(); ++argnr) {
const Token *tok;
int argnr2 = isCallFunction(&scope, argnr, &tok);
if (argnr2 > 0) {
FileInfo::NestedCall nestedCall(tokenizer, &scope, argnr+1, tok);
nestedCall.id = getFunctionId(tokenizer, function);
nestedCall.id2 = getFunctionId(tokenizer, tok->function());
nestedCall.argnr2 = argnr2;
fileInfo->nestedCalls.push_back(nestedCall);
}
}
}
return fileInfo;
}