Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
186 changes: 159 additions & 27 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
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
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: 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
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
2 changes: 1 addition & 1 deletion test/testcppcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ class TestCppcheck : public TestFixture {
";\n";

simplecpp::OutputList outputList;
const simplecpp::TokenList tokens2(code, files, "", &outputList);
const simplecpp::TokenList tokens2(code, files, "", {}, &outputList);
const std::string expected2 = " <rawtokens>\n"
" <file index=\"0\" name=\"test.c\"/>\n"
" <file index=\"1\" name=\"\"/>\n"
Expand Down
9 changes: 5 additions & 4 deletions test/testpreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class TestPreprocessor : public TestFixture {
std::string expandMacros_(const char* file, int line, const char (&code)[size], ErrorLogger &errorLogger) const {
simplecpp::OutputList outputList;
std::vector<std::string> files;
simplecpp::TokenList tokens1 = simplecpp::TokenList(code, files, "file.cpp", &outputList);
simplecpp::TokenList tokens1 = simplecpp::TokenList(code, files, "file.cpp", {}, &outputList);
Preprocessor p(tokens1, settingsDefault, errorLogger, Path::identify(tokens1.getFiles()[0], false));
ASSERT_LOC(p.loadFiles(files), file, line);
simplecpp::TokenList tokens2 = p.preprocess("", files, outputList);
Expand All @@ -75,7 +75,7 @@ class TestPreprocessor : public TestFixture {
throw std::runtime_error("token list not empty");

simplecpp::OutputList outputList;
const simplecpp::TokenList tokens1(code, files, file0, &outputList);
const simplecpp::TokenList tokens1(code, files, file0, dui, &outputList);

// Preprocess..
simplecpp::TokenList tokens2(files);
Expand Down Expand Up @@ -124,7 +124,7 @@ class TestPreprocessor : public TestFixture {
simplecpp::OutputList outputList;
std::vector<std::string> files;

simplecpp::TokenList tokens({code, size}, files, Path::simplifyPath(filename), &outputList);
simplecpp::TokenList tokens({code, size}, files, Path::simplifyPath(filename), {}, &outputList);

// TODO: we should be using the actual Preprocessor implementation
Preprocessor preprocessor(tokens, settings, errorlogger, Path::identify(tokens.getFiles()[0], false));
Expand Down Expand Up @@ -399,7 +399,8 @@ class TestPreprocessor : public TestFixture {
ASSERT(settings.library.load("", library, false).errorcode == Library::ErrorCode::OK);
std::vector<std::string> files;
simplecpp::OutputList outputList;
simplecpp::TokenList tokens(code,files,"test.c",&outputList);
simplecpp::DUI dui;
simplecpp::TokenList tokens(code,files,"test.c",dui,&outputList);

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.

Let's just use {} for empty DUIs.

Preprocessor preprocessor(tokens, settings, *this, Standards::Language::C);
std::set<std::string> configs = { "" };
std::set<std::string> configDefines = { "__cplusplus" };
Expand Down
2 changes: 1 addition & 1 deletion test/testtokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ class TestTokenizer : public TestFixture {
void directiveDump(const char (&code)[size], const char filename[], const Settings& settings, std::ostream& ostr) {
simplecpp::OutputList outputList;
std::vector<std::string> files;
simplecpp::TokenList tokens1(code, files, filename, &outputList);
simplecpp::TokenList tokens1(code, files, filename, {}, &outputList);
Preprocessor preprocessor(tokens1, settings, *this, Path::identify(tokens1.getFiles()[0], false));
std::list<Directive> directives;
preprocessor.setLoadCallback([&](const simplecpp::FileData &data) {
Expand Down
2 changes: 1 addition & 1 deletion test/testtokenlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class TestTokenList : public TestFixture {
// TokenList::determineCppC() because there are no tokens
const char code[] = "#include <sys/poll.h>";
std::vector<std::string> files;
simplecpp::TokenList tokens1(code, files, "poll.h", nullptr);
simplecpp::TokenList tokens1(code, files, "poll.h", {}, nullptr);
Preprocessor preprocessor(tokens1, settingsDefault, *this, Path::identify(tokens1.getFiles()[0], false));
simplecpp::OutputList outputList_pp;
simplecpp::TokenList tokensP = preprocessor.preprocess("", files, outputList_pp);
Expand Down