Skip to content

Commit

Permalink
make sure the language for a TokenList/Token is always determined
Browse files Browse the repository at this point in the history
  • Loading branch information
firewave committed Feb 21, 2024
1 parent fa40b5e commit a6f4038
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 62 deletions.
7 changes: 6 additions & 1 deletion lib/clangimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,12 @@ namespace clangimport {
void dumpAst(int num = 0, int indent = 0) const;
void createTokens1(TokenList &tokenList) {
//dumpAst();
if (!tokenList.back())
if (!tokenList.back()) {
setLocations(tokenList, 0, 1, 1);
// FIXME: treat as C++ if no filename (i.e. no lang) is specified for now
if (tokenList.getSourceFilePath().empty())
tokenList.setLang(Standards::Language::CPP);
}
else
setLocations(tokenList, tokenList.back()->fileIndex(), tokenList.back()->linenr(), 1);
createTokens(tokenList);
Expand Down Expand Up @@ -626,6 +630,7 @@ void clangimport::AstNode::setValueType(Token *tok)
continue;

TokenList decl(nullptr);
decl.setLang(tok->isCpp() ? Standards::Language::CPP : Standards::Language::C);
addTypeTokens(decl, type, tok->scope());
if (!decl.front())
break;
Expand Down
10 changes: 7 additions & 3 deletions lib/cppcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -633,10 +633,14 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string

try {
if (mSettings.library.markupFile(filename)) {
if (mSettings.isUnusedFunctionCheckEnabled() && mSettings.buildDir.empty()) {
if (mSettings.isUnusedFunctionCheckEnabled() &&
mSettings.buildDir.empty()) {
// this is not a real source file - we just want to tokenize it. treat it as C anyways as the language needs to be determined.
Tokenizer tokenizer(mSettings, this);
if (fileStream)
tokenizer.list.setLang(Standards::Language::C);
if (fileStream) {
tokenizer.list.createTokens(*fileStream, filename);
}
else {
std::ifstream in(filename);
tokenizer.list.createTokens(in, filename);
Expand Down Expand Up @@ -804,7 +808,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
}
Tokenizer tokenizer2(mSettings, this);
std::istringstream istr2(code);
tokenizer2.list.createTokens(istr2);
tokenizer2.list.createTokens(istr2, Path::identify(*files.begin()));
executeRules("define", tokenizer2);
}
#endif
Expand Down
8 changes: 4 additions & 4 deletions lib/importproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,11 @@ namespace {
replaceAll(c, "$(Configuration)", p.configuration);
replaceAll(c, "$(Platform)", p.platformStr);

// TODO : Better evaluation
Settings s;
std::istringstream istr(c);
// TODO: evaluate without using the Tokenizer
const Settings s;
Tokenizer tokenizer(s);
tokenizer.tokenize(istr,"vcxproj");
std::istringstream istr(c);
tokenizer.tokenize(istr,"vcxproj.c");
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
if (tok->str() == "(" && tok->astOperand1() && tok->astOperand2()) {
if (tok->astOperand1()->expressionString() == "Configuration.Contains")
Expand Down
11 changes: 6 additions & 5 deletions lib/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ static std::vector<std::string> getnames(const char *names)
return ret;
}

static void gettokenlistfromvalid(const std::string& valid, TokenList& tokenList)
static void gettokenlistfromvalid(const std::string& valid, bool cpp, TokenList& tokenList)
{
std::istringstream istr(valid + ',');
tokenList.createTokens(istr);
tokenList.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C);
for (Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok,"- %num%")) {
tok->str("-" + tok->strAt(1));
Expand Down Expand Up @@ -929,7 +929,7 @@ bool Library::isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint
if (ac->valid.find('.') != std::string::npos)
return isFloatArgValid(ftok, argnr, argvalue);
TokenList tokenList(nullptr);
gettokenlistfromvalid(ac->valid, tokenList);
gettokenlistfromvalid(ac->valid, ftok->isCpp(), tokenList);
for (const Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (tok->isNumber() && argvalue == MathLib::toBigNumber(tok->str()))
return true;
Expand All @@ -949,7 +949,7 @@ bool Library::isFloatArgValid(const Token *ftok, int argnr, double argvalue) con
if (!ac || ac->valid.empty())
return true;
TokenList tokenList(nullptr);
gettokenlistfromvalid(ac->valid, tokenList);
gettokenlistfromvalid(ac->valid, ftok->isCpp(), tokenList);
for (const Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toDoubleNumber(tok->str()) && argvalue <= MathLib::toDoubleNumber(tok->strAt(2)))
return true;
Expand Down Expand Up @@ -1738,13 +1738,14 @@ bool Library::hasAnyTypeCheck(const std::string& typeName) const

std::shared_ptr<Token> createTokenFromExpression(const std::string& returnValue,
const Settings* settings,
bool cpp,
std::unordered_map<nonneg int, const Token*>* lookupVarId)
{
std::shared_ptr<TokenList> tokenList = std::make_shared<TokenList>(settings);
{
const std::string code = "return " + returnValue + ";";
std::istringstream istr(code);
if (!tokenList->createTokens(istr))
if (!tokenList->createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C))
return nullptr;
}

Expand Down
1 change: 1 addition & 0 deletions lib/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok);

std::shared_ptr<Token> createTokenFromExpression(const std::string& returnValue,
const Settings* settings,
bool cpp,
std::unordered_map<nonneg int, const Token*>* lookupVarId = nullptr);

/// @}
Expand Down
7 changes: 4 additions & 3 deletions lib/programmemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ namespace {
arg_map[argn] = v;
argn++;
}
return evaluateLibraryFunction(arg_map, returnValue, settings);
return evaluateLibraryFunction(arg_map, returnValue, settings, ftok->isCpp());
}
}
}
Expand Down Expand Up @@ -1752,15 +1752,16 @@ std::vector<ValueFlow::Value> execute(const Scope* scope, ProgramMemory& pm, con

ValueFlow::Value evaluateLibraryFunction(const std::unordered_map<nonneg int, ValueFlow::Value>& args,
const std::string& returnValue,
const Settings* settings)
const Settings* settings,
bool cpp)
{
thread_local static std::unordered_map<std::string,
std::function<ValueFlow::Value(const std::unordered_map<nonneg int, ValueFlow::Value>& arg)>>
functions = {};
if (functions.count(returnValue) == 0) {

std::unordered_map<nonneg int, const Token*> lookupVarId;
std::shared_ptr<Token> expr = createTokenFromExpression(returnValue, settings, &lookupVarId);
std::shared_ptr<Token> expr = createTokenFromExpression(returnValue, settings, cpp, &lookupVarId);

functions[returnValue] =
[lookupVarId, expr, settings](const std::unordered_map<nonneg int, ValueFlow::Value>& xargs) {
Expand Down
3 changes: 2 additions & 1 deletion lib/programmemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ ProgramMemory getProgramMemory(const Token* tok, const Token* expr, const ValueF

ValueFlow::Value evaluateLibraryFunction(const std::unordered_map<nonneg int, ValueFlow::Value>& args,
const std::string& returnValue,
const Settings* settings);
const Settings* settings,
bool cpp);

#endif

Expand Down
4 changes: 2 additions & 2 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7588,7 +7588,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
ValueType valuetype;
TokenList tokenList(&mSettings);
std::istringstream istr(typestr+";");
tokenList.createTokens(istr);
tokenList.createTokens(istr, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C);
tokenList.simplifyStdType();
if (parsedecl(tokenList.front(), &valuetype, mDefaultSignedness, mSettings, mIsCpp)) {
valuetype.originalTypeName = typestr;
Expand Down Expand Up @@ -7678,7 +7678,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
}
TokenList tokenList(&mSettings);
std::istringstream istr(typestr+";");
if (tokenList.createTokens(istr)) {
if (tokenList.createTokens(istr, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C)) {
ValueType vt;
tokenList.simplifyPlatformTypes();
tokenList.simplifyStdType();
Expand Down
1 change: 0 additions & 1 deletion lib/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -1479,7 +1479,6 @@ class CPPCHECKLIB Token {
mImpl->mDebug = td;
}

/** defaults to C++ if it cannot be determined */
bool isCpp() const;
};

Expand Down
72 changes: 71 additions & 1 deletion lib/tokenlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

//---------------------------------------------------------------------------

#include "tokenlist.h"

#include "astutils.h"
Expand All @@ -40,6 +41,15 @@

#include <simplecpp.h>

//#define N_ASSERT_LANG

#ifndef N_ASSERT_LANG
#include <cassert>
#define ASSERT_LANG(x) assert(x)
#else
#define ASSERT_LANG(x)
#endif

// How many compileExpression recursions are allowed?
// For practical code this could be endless. But in some special torture test
// there needs to be a limit.
Expand Down Expand Up @@ -83,9 +93,16 @@ void TokenList::deallocateTokens()

void TokenList::determineCppC()
{
// only try to determine it if it wasn't enforced
// only try to determine if it wasn't enforced
if (mLang == Standards::Language::None) {
mLang = Path::identify(getSourceFilePath());
// TODO: cannot enable assert as this might occur for unknown extensions
//ASSERT_LANG(mLang != Standards::Language::None);
if (mLang == Standards::Language::None) {
// TODO: should default to C instead like we do for headers
// default to C++
mLang = Standards::Language::CPP;
}
}
}

Expand Down Expand Up @@ -317,8 +334,31 @@ void TokenList::insertTokens(Token *dest, const Token *src, nonneg int n)

bool TokenList::createTokens(std::istream &code, const std::string& file0)
{
ASSERT_LANG(!file0.empty());

appendFileIfNew(file0);

return createTokensInternal(code, file0);
}

//---------------------------------------------------------------------------

bool TokenList::createTokens(std::istream &code, Standards::Language lang)
{
ASSERT_LANG(lang != Standards::Language::None);
if (mLang == Standards::Language::None) {
mLang = lang;
} else {
ASSERT_LANG(lang == mLang);
}

return createTokensInternal(code, "");
}

//---------------------------------------------------------------------------

bool TokenList::createTokensInternal(std::istream &code, const std::string& file0)
{
simplecpp::OutputList outputList;
simplecpp::TokenList tokens(code, mFiles, file0, &outputList);

Expand Down Expand Up @@ -2096,3 +2136,33 @@ bool TokenList::isKeyword(const std::string &str) const
static const auto& latest_c_keywords = Keywords::getAll(Standards::cstd_t::CLatest);
return latest_c_keywords.find(str) != latest_c_keywords.end();
}

bool TokenList::isC() const
{
ASSERT_LANG(mLang != Standards::Language::None);

// TODO: remove the fallback
if (mLang == Standards::Language::None)
return false; // treat as C++ by default

return mLang == Standards::Language::C;
}

bool TokenList::isCPP() const
{
ASSERT_LANG(mLang != Standards::Language::None);

// TODO: remove the fallback
if (mLang == Standards::Language::None)
return true; // treat as C++ by default

return mLang == Standards::Language::CPP;
}

void TokenList::setLang(Standards::Language lang)
{
ASSERT_LANG(lang != Standards::Language::None);
ASSERT_LANG(mLang == Standards::Language::None);

mLang = lang;
}
19 changes: 10 additions & 9 deletions lib/tokenlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,13 @@ class CPPCHECKLIB TokenList {
/** @return the source file path. e.g. "file.cpp" */
const std::string& getSourceFilePath() const;

/** Is the code C. Used for bailouts */
bool isC() const {
return mLang == Standards::Language::C;
}
/** @return true if the code is C */
bool isC() const;

/** Is the code CPP. Used for bailouts */
bool isCPP() const {
return mLang == Standards::Language::CPP;
}
/** @return true if the code is C++ */
bool isCPP() const;

void setLang(Standards::Language lang);

/**
* Delete all tokens in given token list
Expand Down Expand Up @@ -105,7 +103,8 @@ class CPPCHECKLIB TokenList {
* @param code input stream for code
* @param file0 source file name
*/
bool createTokens(std::istream &code, const std::string& file0 = emptyString);
bool createTokens(std::istream &code, const std::string& file0);
bool createTokens(std::istream &code, Standards::Language lang);

void createTokens(simplecpp::TokenList&& tokenList);

Expand Down Expand Up @@ -203,6 +202,8 @@ class CPPCHECKLIB TokenList {
private:
void determineCppC();

bool createTokensInternal(std::istream &code, const std::string& file0);

/** Token list */
TokensFrontBack mTokensFrontBack;

Expand Down
Loading

0 comments on commit a6f4038

Please sign in to comment.