Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 160 additions & 28 deletions externals/simplecpp/simplecpp.cpp

Large diffs are not rendered by default.

88 changes: 53 additions & 35 deletions externals/simplecpp/simplecpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@
# endif
#endif

#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 9
// Hack to workaround GCC bug.
// Details: https://trac.cppcheck.net/ticket/14850
// seen on g++ before 10.x
#define SIMPLECPP_NOEXCEPT
#else
#define SIMPLECPP_NOEXCEPT noexcept
#endif

namespace simplecpp {
/** C code standard */
enum cstd_t : std::int8_t { CUnknown=-1, C89, C99, C11, C17, C23, C2Y };
Expand Down Expand Up @@ -238,7 +247,10 @@ namespace simplecpp {
MISSING_HEADER,
INCLUDE_NESTED_TOO_DEEPLY,
SYNTAX_ERROR,
DIRECTIVE_AS_MACRO_PARAMETER,
PORTABILITY_BACKSLASH,
PORTABILITY_LINE_DIRECTIVE,
PORTABILITY_NO_EOF_NEWLINE,
UNHANDLED_CHAR_ERROR,
EXPLICIT_INCLUDE_NOT_FOUND,
FILE_NOT_FOUND,
Expand All @@ -251,52 +263,67 @@ namespace simplecpp {

using OutputList = std::list<Output>;

/**
* Command line preprocessor settings.
* On the command line these are configured by -D, -U, -I, --include, -std
*/
struct SIMPLECPP_LIB DUI {
DUI() = default;
std::list<std::string> defines;
std::set<std::string> undefined;
std::list<std::string> includePaths;
std::list<std::string> includes;
std::string std;
bool clearIncludeCache{};
bool removeComments{}; /** remove comment tokens from included files */
};

/** List of tokens. */
class SIMPLECPP_LIB TokenList {
public:
class Stream;

explicit TokenList(std::vector<std::string> &filenames);
/** generates a token list from the given std::istream parameter */
TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr);
TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr);
/** generates a token list from the given buffer */
template<size_t size>
TokenList(const char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data), size-1, filenames, filename, outputList, 0)
TokenList(const char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data), size-1, filenames, filename, dui, outputList, 0)
{}
/** generates a token list from the given buffer */
template<size_t size>
TokenList(const unsigned char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
: TokenList(data, size-1, filenames, filename, outputList, 0)
TokenList(const unsigned char (&data)[size], std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
: TokenList(data, size-1, filenames, filename, dui, outputList, 0)
{}
#if SIMPLECPP_TOKENLIST_ALLOW_PTR
/** generates a token list from the given buffer */
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
: TokenList(data, size, filenames, filename, outputList, 0)
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
: TokenList(data, size, filenames, filename, dui, outputList, 0)
{}
/** generates a token list from the given buffer */
TokenList(const char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data), size, filenames, filename, outputList, 0)
TokenList(const char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data), size, filenames, filename, dui, outputList, 0)
{}
#endif // SIMPLECPP_TOKENLIST_ALLOW_PTR
/** generates a token list from the given buffer */
TokenList(View data, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, outputList, 0)
TokenList(View data, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, dui, outputList, 0)
{}
#ifdef __cpp_lib_span
/** generates a token list from the given buffer */
TokenList(std::span<const char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, outputList, 0)
TokenList(std::span<const char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
: TokenList(reinterpret_cast<const unsigned char*>(data.data()), data.size(), filenames, filename, dui, outputList, 0)
{}

/** generates a token list from the given buffer */
TokenList(std::span<const unsigned char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr)
: TokenList(data.data(), data.size(), filenames, filename, outputList, 0)
TokenList(std::span<const unsigned char> data, std::vector<std::string> &filenames, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr)
: TokenList(data.data(), data.size(), filenames, filename, dui, outputList, 0)
{}
#endif // __cpp_lib_span

/** generates a token list from the given filename parameter */
TokenList(const std::string &filename, std::vector<std::string> &filenames, OutputList *outputList = nullptr);
TokenList(const std::string &filename, std::vector<std::string> &filenames, const DUI &dui = {}, OutputList *outputList = nullptr);
TokenList(const TokenList &other);
TokenList(TokenList &&other);
~TokenList();
Expand All @@ -312,7 +339,7 @@ namespace simplecpp {
void dump(bool linenrs = false) const;
std::string stringify(bool linenrs = false) const;

void readfile(Stream &stream, const std::string &filename=std::string(), OutputList *outputList = nullptr);
void readfile(Stream &stream, const std::string &filename=std::string(), const DUI &dui = {}, OutputList *outputList = nullptr);
/**
* @throws std::overflow_error thrown on overflow or division by zero
* @throws std::runtime_error thrown on invalid expressions
Expand Down Expand Up @@ -376,7 +403,7 @@ namespace simplecpp {
const std::string& file(const Location& loc) const;

private:
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename, OutputList *outputList, int /*unused*/);
TokenList(const unsigned char* data, std::size_t size, std::vector<std::string> &filenames, const std::string &filename, const DUI &dui, OutputList *outputList, int /*unused*/);

void combineOperators();

Expand Down Expand Up @@ -425,21 +452,6 @@ namespace simplecpp {
long long result; // condition result
};

/**
* Command line preprocessor settings.
* On the command line these are configured by -D, -U, -I, --include, -std
*/
struct SIMPLECPP_LIB DUI {
DUI() = default;
std::list<std::string> defines;
std::set<std::string> undefined;
std::list<std::string> includePaths;
std::list<std::string> includes;
std::string std;
bool clearIncludeCache{};
bool removeComments{}; /** remove comment tokens from included files */
};

struct SIMPLECPP_LIB FileData {
/** The canonical filename associated with this data */
std::string filename;
Expand All @@ -453,10 +465,10 @@ namespace simplecpp {
~FileDataCache();

FileDataCache(const FileDataCache &) = delete;
FileDataCache(FileDataCache &&) noexcept;
FileDataCache(FileDataCache &&) SIMPLECPP_NOEXCEPT;

FileDataCache &operator=(const FileDataCache &) = delete;
FileDataCache &operator=(FileDataCache &&) noexcept;
FileDataCache &operator=(FileDataCache &&) SIMPLECPP_NOEXCEPT;

/** Get the cached data for a file, or load and then return it if it isn't cached.
* returns the file data and true if the file was loaded, false if it was cached. */
Expand Down Expand Up @@ -578,9 +590,15 @@ namespace simplecpp {
/** Returns the C version a given standard */
SIMPLECPP_LIB cstd_t getCStd(const std::string &std);

/** Returns the name of a C standard */
SIMPLECPP_LIB const char *getCStdName(cstd_t std);

/** Returns the C++ version a given standard */
SIMPLECPP_LIB cppstd_t getCppStd(const std::string &std);

/** Returns the name of a C++ standard */
SIMPLECPP_LIB const char *getCppStdName(cppstd_t std);

/** Returns the __STDC_VERSION__ value for a given standard */
SIMPLECPP_LIB std::string getCStdString(const std::string &std);
SIMPLECPP_LIB std::string getCStdString(cstd_t std);
Expand Down
12 changes: 8 additions & 4 deletions lib/cppcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -894,16 +894,20 @@ std::size_t CppCheck::calculateHash(const Preprocessor& preprocessor, const std:

unsigned int CppCheck::checkBuffer(const FileWithDetails &file, const std::string &cfgname, const char* data, std::size_t size)
{
const auto f = [&file, data, size](std::vector<std::string>& files, simplecpp::OutputList* outputList) {
return simplecpp::TokenList{{data, size}, files, file.spath(), outputList};
const auto f = [&file, data, size, this](std::vector<std::string>& files, simplecpp::OutputList* outputList) {
simplecpp::DUI dui;
dui.std = mSettings.standards.getStdForLanguage(file.lang());
return simplecpp::TokenList{{data, size}, files, file.spath(), dui, outputList};

@ludviggunne ludviggunne Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be good with some preprocessor tests for this. I.e. skip the trailing newline and run as both C and C++. And preferably something that tests both checkBuffer and checkFile, not sure in which scenarios these are called.

};
return checkInternal(file, cfgname, f);
}

unsigned int CppCheck::checkFile(const FileWithDetails& file, const std::string &cfgname)
{
const auto f = [&file](std::vector<std::string>& files, simplecpp::OutputList* outputList) {
return simplecpp::TokenList{file.spath(), files, outputList};
const auto f = [&file, this](std::vector<std::string>& files, simplecpp::OutputList* outputList) {
simplecpp::DUI dui;
dui.std = mSettings.standards.getStdForLanguage(file.lang());
return simplecpp::TokenList{file.spath(), files, dui, outputList};
};
return checkInternal(file, cfgname, f);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/importproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ namespace {
}

static bool evalCondition(const std::string& condition, const ProjectConfiguration &p) {
std::string c = '(' + condition + ")";
std::string c = '(' + condition + ")\n";
replaceAll(c, "$(Configuration)", p.configuration);
replaceAll(c, "$(Platform)", p.platformStr);

Expand Down
2 changes: 1 addition & 1 deletion lib/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ static std::vector<std::string> getnames(const char *names)

static void gettokenlistfromvalid(const std::string& valid, TokenList& tokenList)
{
const std::string str(valid + ',');
const std::string str(valid + ",\n");
tokenList.createTokensFromBuffer(str.data(), str.size()); // TODO: check result?
for (Token *tok = tokenList.front(); tok; tok = tok->next()) {
if (Token::Match(tok,"- %num%")) {
Expand Down
6 changes: 6 additions & 0 deletions lib/preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,8 @@ const simplecpp::Output* Preprocessor::reportOutput(const simplecpp::OutputList
break;
case simplecpp::Output::WARNING:
case simplecpp::Output::PORTABILITY_BACKSLASH:
case simplecpp::Output::PORTABILITY_LINE_DIRECTIVE:
case simplecpp::Output::PORTABILITY_NO_EOF_NEWLINE:
break;
case simplecpp::Output::MISSING_HEADER: {
// not considered an "error"
Expand All @@ -939,6 +941,7 @@ const simplecpp::Output* Preprocessor::reportOutput(const simplecpp::OutputList
missingInclude(out.location, out.msg.substr(pos1+1, pos2-pos1-1), out.msg[pos1] == '\"' ? UserHeader : SystemHeader);
}
break;
case simplecpp::Output::DIRECTIVE_AS_MACRO_PARAMETER:
case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
case simplecpp::Output::SYNTAX_ERROR:
case simplecpp::Output::UNHANDLED_CHAR_ERROR:
Expand All @@ -963,6 +966,7 @@ static std::string simplecppErrToId(simplecpp::Output::Type type)
case simplecpp::Output::ERROR:
return "preprocessorErrorDirective";
case simplecpp::Output::SYNTAX_ERROR:
case simplecpp::Output::DIRECTIVE_AS_MACRO_PARAMETER:
return "syntaxError";
case simplecpp::Output::UNHANDLED_CHAR_ERROR:
return "unhandledChar";
Expand All @@ -979,6 +983,8 @@ static std::string simplecppErrToId(simplecpp::Output::Type type)
// no handled at all (warnings)
case simplecpp::Output::WARNING:
case simplecpp::Output::PORTABILITY_BACKSLASH:
case simplecpp::Output::PORTABILITY_LINE_DIRECTIVE:
case simplecpp::Output::PORTABILITY_NO_EOF_NEWLINE:
throw std::runtime_error("unexpected simplecpp::Output type " + std::to_string(type));
}

Expand Down
2 changes: 1 addition & 1 deletion lib/programmemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1937,7 +1937,7 @@ static std::shared_ptr<Token> createTokenFromExpression(const std::string& retur
{
std::shared_ptr<TokenList> tokenList = std::make_shared<TokenList>(settings, cpp ? Standards::Language::CPP : Standards::Language::C);
{
const std::string code = "return " + returnValue + ";";
const std::string code = "return " + returnValue + ";\n";
if (!tokenList->createTokensFromBuffer(code.data(), code.size()))
return nullptr;
}
Expand Down
12 changes: 12 additions & 0 deletions lib/standards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,15 @@ bool Standards::setStd(const std::string& str)
{
return setC(str) || setCPP(str);
}

std::string Standards::getStdForLanguage(Standards::Language language) const
{
switch (language) {
case C:
return getC();
case CPP:
return getCPP();
default:
return "";
}
}
1 change: 1 addition & 0 deletions lib/standards.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct CPPCHECKLIB Standards {
static std::string getCPP(cppstd_t std);
static cppstd_t getCPP(const std::string &std);
bool setStd(const std::string& str);
std::string getStdForLanguage(Language language) const;
};

/// @}
Expand Down
4 changes: 2 additions & 2 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8093,7 +8093,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
if (!typestr.empty()) {
ValueType valuetype;
TokenList tokenList(mSettings, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C);
const std::string str(typestr+";");
const std::string str(typestr+";\n");
tokenList.createTokensFromBuffer(str.data(), str.size()); // TODO: check result?
tokenList.simplifyStdType();
if (parsedecl(tokenList.front(), &valuetype, mDefaultSignedness, mSettings)) {
Expand Down Expand Up @@ -8186,7 +8186,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
continue;
}
TokenList tokenList(mSettings, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C);
const std::string str(typestr+";");
const std::string str(typestr+";\n");
if (tokenList.createTokensFromBuffer(str.data(), str.size())) {
ValueType vt;
tokenList.simplifyPlatformTypes();
Expand Down
4 changes: 3 additions & 1 deletion lib/tokenlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,9 @@ bool TokenList::createTokensFromBufferInternal(const char* data, size_t size, co
#endif

simplecpp::OutputList outputList;
simplecpp::TokenList tokens({data, size}, mFiles, file0, &outputList);
simplecpp::DUI dui;
dui.std = mSettings.standards.getStdForLanguage(mLang);
simplecpp::TokenList tokens({data, size}, mFiles, file0, dui, &outputList);

createTokens(std::move(tokens));

Expand Down
5 changes: 3 additions & 2 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1817,7 +1817,8 @@ static bool isNotEqual(std::pair<const Token*, const Token*> x, std::pair<const
static bool isNotEqual(std::pair<const Token*, const Token*> x, const std::string& y, bool cpp, const Settings& settings)
{
TokenList tokenList(settings, cpp ? Standards::Language::CPP : Standards::Language::C);
tokenList.createTokensFromBuffer(y.data(), y.size()); // TODO: check result?
const std::string str(y + "\n");
tokenList.createTokensFromBuffer(str.data(), str.size()); // TODO: check result?
return isNotEqual(x, std::make_pair(tokenList.front(), tokenList.back()));
}
static bool isNotEqual(std::pair<const Token*, const Token*> x, const ValueType* y, bool cpp, const Settings& settings)
Expand Down Expand Up @@ -7095,7 +7096,7 @@ static bool getMinMaxValues(const std::string& typestr,
MathLib::bigint& maxvalue)
{
TokenList typeTokens(settings, cpp ? Standards::Language::CPP : Standards::Language::C);
const std::string str(typestr + ";");
const std::string str(typestr + ";\n");
if (!typeTokens.createTokensFromBuffer(str.data(), str.size()))
return false;
typeTokens.simplifyPlatformTypes();
Expand Down
6 changes: 5 additions & 1 deletion test/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,12 @@ ScopedFile::~ScopedFile() {

void SimpleTokenizer2::preprocess(const char* code, std::size_t size, std::vector<std::string> &files, const std::string& file0, Tokenizer& tokenizer, ErrorLogger& errorlogger)
{
const Standards &standards = tokenizer.getSettings().standards;
const Standards::Language language = tokenizer.isCPP() ? Standards::CPP : Standards::C;
simplecpp::OutputList outputList;
simplecpp::TokenList tokens1({code, size}, files, file0, &outputList);
simplecpp::DUI dui;
dui.std = standards.getStdForLanguage(language);
simplecpp::TokenList tokens1({code, size}, files, file0, dui, &outputList);

Preprocessor preprocessor(tokens1, tokenizer.getSettings(), errorlogger, Path::identify(tokens1.getFiles()[0], false));
(void)preprocessor.loadFiles(files); // TODO: check result
Expand Down
Loading
Loading