From 31164c2eb28cc117aad8dbc6e24afd0433884d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 7 Oct 2025 19:24:22 +0200 Subject: [PATCH 01/15] selfcheck.sh: added options `VALGRIND_TOOL` and `SIMPLECPP_PATH` / CI-unixish.yml: use `selfcheck.sh` to run valgrind (#560) --- .github/workflows/CI-unixish.yml | 14 +++++++++----- selfcheck.sh | 26 ++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 9778f68a..b4089ee1 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -100,9 +100,10 @@ jobs: if: matrix.os == 'ubuntu-24.04' run: | make clean - make -j$(nproc) + make -j$(nproc) CXXOPTS="-O1" valgrind --leak-check=full --num-callers=50 --show-reachable=yes --track-origins=yes --gen-suppressions=all --error-exitcode=42 ./testrunner - valgrind --leak-check=full --num-callers=50 --show-reachable=yes --track-origins=yes --gen-suppressions=all --error-exitcode=42 ./simplecpp simplecpp.cpp -e + # TODO: run Python tests with valgrind + VALGRIND_TOOL=memcheck ./selfcheck.sh - name: Run with libstdc++ debug mode if: matrix.os == 'ubuntu-24.04' && matrix.compiler == 'g++' @@ -146,10 +147,13 @@ jobs: tar xvf 1.5.1.tar.gz make clean make -j$(nproc) CXXOPTS="-O2 -g3" - valgrind --tool=callgrind ./simplecpp -e simplecpp-1.5.1/simplecpp.cpp 2>callgrind.log || (cat callgrind.log && false) + VALGRIND_TOOL=callgrind SIMPLECPP_PATH=simplecpp-1.5.1 ./selfcheck.sh >callgrind.log || (cat callgrind.log && false) cat callgrind.log - callgrind_annotate --auto=no > callgrind.annotated.log - head -50 callgrind.annotated.log + for f in callgrind.out.*; + do + callgrind_annotate --auto=no $f > $f.annotated.log + head -50 $f.annotated.log + done - uses: actions/upload-artifact@v4 if: matrix.os == 'ubuntu-24.04' diff --git a/selfcheck.sh b/selfcheck.sh index cc77981d..e708023a 100755 --- a/selfcheck.sh +++ b/selfcheck.sh @@ -1,7 +1,28 @@ #!/bin/bash -output=$(./simplecpp simplecpp.cpp -e -f 2>&1) +if [ -z "$SIMPLECPP_PATH" ]; then + SIMPLECPP_PATH=. +fi + +if [ -n "$VALGRIND_TOOL" ]; then + if [ "$VALGRIND_TOOL" = "memcheck" ]; then + VALGRIND_OPTS="--error-limit=yes --leak-check=full --num-callers=50 --show-reachable=yes --track-origins=yes --gen-suppressions=all --error-exitcode=42" + elif [ "$VALGRIND_TOOL" = "callgrind" ]; then + VALGRIND_OPTS="--tool=callgrind" + else + echo "unsupported valgrind tool '$VALGRIND_TOOL'" + exit 1 + fi + VALGRIND_CMD="valgrind --tool=$VALGRIND_TOOL --log-fd=9 $VALGRIND_OPTS" + VALGRIND_REDIRECT="valgrind_$VALGRIND_TOOL.log" +else + VALGRIND_CMD= + VALGRIND_REDIRECT="/dev/null" +fi + +output=$($VALGRIND_CMD ./simplecpp "$SIMPLECPP_PATH/simplecpp.cpp" -e -f 2>&1 9> "$VALGRIND_REDIRECT") ec=$? +cat "$VALGRIND_REDIRECT" errors=$(echo "$output" | grep -v 'Header not found: <') if [ $ec -ne 0 ]; then # only fail if we got errors which do not refer to missing system includes @@ -104,8 +125,9 @@ else fi # run with -std=gnuc++* so __has_include(...) is available -./simplecpp simplecpp.cpp -e -f -std=gnu++11 $defs $inc +$VALGRIND_CMD ./simplecpp "$SIMPLECPP_PATH/simplecpp.cpp" -e -f -std=gnu++11 $defs $inc 9> "$VALGRIND_REDIRECT" ec=$? +cat "$VALGRIND_REDIRECT" if [ $ec -ne 0 ]; then exit $ec fi From ccde80d6982d39f1f4ae6968828750f175b8912d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Wed, 8 Oct 2025 20:28:46 +0200 Subject: [PATCH 02/15] enabled and fixed `modernize-use-equals-default` clang-tidy warnings (#561) --- .clang-tidy | 1 - simplecpp.cpp | 2 +- simplecpp.h | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 725db88e..c93066ab 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -31,7 +31,6 @@ Checks: > -modernize-pass-by-value, -modernize-return-braced-init-list, -modernize-use-auto, - -modernize-use-equals-default, -modernize-use-equals-delete, -modernize-use-default-member-init, -modernize-use-nodiscard, diff --git a/simplecpp.cpp b/simplecpp.cpp index 9f7de67d..a8e197b6 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -245,7 +245,7 @@ void simplecpp::Token::printOut() const // cppcheck-suppress noConstructor - we call init() in the inherited to initialize the private members class simplecpp::TokenList::Stream { public: - virtual ~Stream() {} + virtual ~Stream() = default; virtual int get() = 0; virtual int peek() = 0; diff --git a/simplecpp.h b/simplecpp.h index da859cba..a23e4cb4 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -78,7 +78,7 @@ namespace simplecpp { public: explicit Location(const std::vector &f) : files(f), fileIndex(0), line(1U), col(0U) {} - Location(const Location &loc) : files(loc.files), fileIndex(loc.fileIndex), line(loc.line), col(loc.col) {} + Location(const Location &loc) = default; Location &operator=(const Location &other) { if (this != &other) { From a74ed72db655ea71be0177dd9944fb6d3fc02e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Thu, 9 Oct 2025 18:04:13 +0200 Subject: [PATCH 03/15] enabled and fixed `modernize-use-default-member-init` clang-tidy warnings (#564) --- .clang-tidy | 1 - simplecpp.cpp | 16 +++++++--------- simplecpp.h | 24 ++++++++++++------------ 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index c93066ab..b7c39f2b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -32,7 +32,6 @@ Checks: > -modernize-return-braced-init-list, -modernize-use-auto, -modernize-use-equals-delete, - -modernize-use-default-member-init, -modernize-use-nodiscard, -modernize-use-trailing-return-type, -readability-avoid-nested-conditional-operator, diff --git a/simplecpp.cpp b/simplecpp.cpp index a8e197b6..d5b51080 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -386,8 +386,7 @@ class StdCharBufStream : public simplecpp::TokenList::Stream { StdCharBufStream(const unsigned char* str, std::size_t size) : str(str) , size(size) - , pos(0) - , lastStatus(0) { + { init(); } @@ -411,8 +410,8 @@ class StdCharBufStream : public simplecpp::TokenList::Stream { private: const unsigned char *str; const std::size_t size; - std::size_t pos; - int lastStatus; + std::size_t pos{}; + int lastStatus{}; }; class FileStream : public simplecpp::TokenList::Stream { @@ -420,8 +419,7 @@ class FileStream : public simplecpp::TokenList::Stream { // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members explicit FileStream(const std::string &filename, std::vector &files) : file(fopen(filename.c_str(), "rb")) - , lastCh(0) - , lastStatus(0) { + { if (!file) { files.push_back(filename); throw simplecpp::Output(files, simplecpp::Output::FILE_NOT_FOUND, "File is missing: " + filename); @@ -465,8 +463,8 @@ class FileStream : public simplecpp::TokenList::Stream { FileStream &operator=(const FileStream&); FILE *file; - int lastCh; - int lastStatus; + int lastCh{}; + int lastStatus{}; }; simplecpp::TokenList::TokenList(std::vector &filenames) : frontToken(nullptr), backToken(nullptr), files(filenames) {} @@ -1487,7 +1485,7 @@ namespace simplecpp { class Macro { public: - explicit Macro(std::vector &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), variadicOpt(false), optExpandValue(nullptr), optNoExpandValue(nullptr), valueDefinedInCode_(false) {} + explicit Macro(std::vector &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), variadicOpt(false), valueDefinedInCode_(false) {} Macro(const Token *tok, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(true) { if (sameline(tok->previousSkipComments(), tok)) diff --git a/simplecpp.h b/simplecpp.h index a23e4cb4..cbea068b 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -76,7 +76,7 @@ namespace simplecpp { */ class SIMPLECPP_LIB Location { public: - explicit Location(const std::vector &f) : files(f), fileIndex(0), line(1U), col(0U) {} + explicit Location(const std::vector &f) : files(f) {} Location(const Location &loc) = default; @@ -109,9 +109,9 @@ namespace simplecpp { } const std::vector &files; - unsigned int fileIndex; - unsigned int line; - unsigned int col; + unsigned int fileIndex{}; + unsigned int line{1}; + unsigned int col{}; private: static const std::string emptyFileName; }; @@ -123,12 +123,12 @@ namespace simplecpp { class SIMPLECPP_LIB Token { public: Token(const TokenString &s, const Location &loc, bool wsahead = false) : - whitespaceahead(wsahead), location(loc), previous(nullptr), next(nullptr), nextcond(nullptr), string(s) { + whitespaceahead(wsahead), location(loc), string(s) { flags(); } Token(const Token &tok) : - macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name), number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location), previous(nullptr), next(nullptr), nextcond(nullptr), string(tok.string), mExpandedFrom(tok.mExpandedFrom) {} + macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name), number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location), string(tok.string), mExpandedFrom(tok.mExpandedFrom) {} const TokenString& str() const { return string; @@ -153,9 +153,9 @@ namespace simplecpp { bool number; bool whitespaceahead; Location location; - Token *previous; - Token *next; - mutable const Token *nextcond; + Token *previous{}; + Token *next{}; + mutable const Token *nextcond{}; const Token *previousSkipComments() const { const Token *tok = this->previous; @@ -393,14 +393,14 @@ namespace simplecpp { * On the command line these are configured by -D, -U, -I, --include, -std */ struct SIMPLECPP_LIB DUI { - DUI() : clearIncludeCache(false), removeComments(false) {} + DUI() = default; std::list defines; std::set undefined; std::list includePaths; std::list includes; std::string std; - bool clearIncludeCache; - bool removeComments; /** remove comment tokens from included files */ + bool clearIncludeCache{}; + bool removeComments{}; /** remove comment tokens from included files */ }; struct SIMPLECPP_LIB FileData { From cb9a9a21cc1f9c1c14a5065fe98fcdf42d7b7319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sun, 12 Oct 2025 14:21:28 +0200 Subject: [PATCH 04/15] enabled and fixed `modernize-use-equals-delete` clang-tidy warnings (#565) --- .clang-tidy | 1 - simplecpp.cpp | 6 +++--- simplecpp.h | 5 ++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index b7c39f2b..182f1a55 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -31,7 +31,6 @@ Checks: > -modernize-pass-by-value, -modernize-return-braced-init-list, -modernize-use-auto, - -modernize-use-equals-delete, -modernize-use-nodiscard, -modernize-use-trailing-return-type, -readability-avoid-nested-conditional-operator, diff --git a/simplecpp.cpp b/simplecpp.cpp index d5b51080..7774fd29 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -427,6 +427,9 @@ class FileStream : public simplecpp::TokenList::Stream { init(); } + FileStream(const FileStream&) = delete; + FileStream &operator=(const FileStream&) = delete; + ~FileStream() override { fclose(file); file = nullptr; @@ -459,9 +462,6 @@ class FileStream : public simplecpp::TokenList::Stream { ungetc(ch, file); } - FileStream(const FileStream&); - FileStream &operator=(const FileStream&); - FILE *file; int lastCh{}; int lastStatus{}; diff --git a/simplecpp.h b/simplecpp.h index cbea068b..7e556657 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -130,6 +130,8 @@ namespace simplecpp { Token(const Token &tok) : macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name), number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location), string(tok.string), mExpandedFrom(tok.mExpandedFrom) {} + Token &operator=(const Token &tok) = delete; + const TokenString& str() const { return string; } @@ -195,9 +197,6 @@ namespace simplecpp { TokenString string; std::set mExpandedFrom; - - // Not implemented - prevent assignment - Token &operator=(const Token &tok); }; /** Output from preprocessor */ From 318a37b553b4ccafeb6c84db1086c4f8a0756ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sat, 18 Oct 2025 16:18:49 +0200 Subject: [PATCH 05/15] optimized handling of some preprocessor directives (#502) --- simplecpp.cpp | 108 ++++++++++++++++++++++++-------------------------- simplecpp.h | 3 +- 2 files changed, 53 insertions(+), 58 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 7774fd29..8f1879ef 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -697,33 +697,55 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, if (oldLastToken != cback()) { oldLastToken = cback(); - if (!isLastLinePreprocessor()) + const Token * const llTok = isLastLinePreprocessor(); + if (!llTok) continue; - const std::string lastline(lastLine()); - if (lastline == "# file %str%") { - const Token *strtok = cback(); - while (strtok->comment) - strtok = strtok->previous; - loc.push(location); - location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); - location.line = 1U; - } else if (lastline == "# line %num%") { - const Token *numtok = cback(); - while (numtok->comment) - numtok = numtok->previous; - lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), &location); - } else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") { - const Token *strtok = cback(); - while (strtok->comment) - strtok = strtok->previous; - const Token *numtok = strtok->previous; - while (numtok->comment) - numtok = numtok->previous; - lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")), - std::atol(numtok->str().c_str()), &location); + const Token * const llNextToken = llTok->next; + if (!llTok->next) + continue; + if (llNextToken->next) { + // #file "file.c" + if (llNextToken->str() == "file" && + llNextToken->next->str()[0] == '\"') + { + const Token *strtok = cback(); + while (strtok->comment) + strtok = strtok->previous; + loc.push(location); + location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); + location.line = 1U; + } + // #3 "file.c" + // #line 3 "file.c" + else if ((llNextToken->number && + llNextToken->next->str()[0] == '\"') || + (llNextToken->str() == "line" && + llNextToken->next->number && + llNextToken->next->next && + llNextToken->next->next->str()[0] == '\"')) + { + const Token *strtok = cback(); + while (strtok->comment) + strtok = strtok->previous; + const Token *numtok = strtok->previous; + while (numtok->comment) + numtok = numtok->previous; + lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")), + std::atol(numtok->str().c_str()), &location); + } + // #line 3 + else if (llNextToken->str() == "line" && + llNextToken->next->number) + { + const Token *numtok = cback(); + while (numtok->comment) + numtok = numtok->previous; + lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), &location); + } } // #endfile - else if (lastline == "# endfile" && !loc.empty()) { + else if (llNextToken->str() == "endfile" && !loc.empty()) + { location = loc.top(); loc.pop(); } @@ -740,8 +762,8 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, TokenString currentToken; if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#') { - const Token* const llTok = lastLineTok(); - if (llTok && llTok->op == '#' && llTok->next && (llTok->next->str() == "error" || llTok->next->str() == "warning")) { + const Token* const ppTok = cback()->previous; + if (ppTok->next && (ppTok->next->str() == "error" || ppTok->next->str() == "warning")) { char prev = ' '; while (stream.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) { currentToken += ch; @@ -1418,34 +1440,6 @@ std::string simplecpp::TokenList::readUntil(Stream &stream, const Location &loca return ret; } -std::string simplecpp::TokenList::lastLine(int maxsize) const -{ - std::string ret; - int count = 0; - for (const Token *tok = cback(); ; tok = tok->previous) { - if (!sameline(tok, cback())) { - break; - } - if (tok->comment) - continue; - if (++count > maxsize) - return ""; - if (!ret.empty()) - ret += ' '; - // add tokens in reverse for performance reasons - if (tok->str()[0] == '\"') - ret += "%rts%"; // %str% - else if (tok->number) - ret += "%mun%"; // %num% - else { - ret += tok->str(); - std::reverse(ret.end() - tok->str().length(), ret.end()); - } - } - std::reverse(ret.begin(), ret.end()); - return ret; -} - const simplecpp::Token* simplecpp::TokenList::lastLineTok(int maxsize) const { const Token* prevTok = nullptr; @@ -1462,10 +1456,12 @@ const simplecpp::Token* simplecpp::TokenList::lastLineTok(int maxsize) const return prevTok; } -bool simplecpp::TokenList::isLastLinePreprocessor(int maxsize) const +const simplecpp::Token* simplecpp::TokenList::isLastLinePreprocessor(int maxsize) const { const Token * const prevTok = lastLineTok(maxsize); - return prevTok && prevTok->op == '#'; + if (prevTok && prevTok->op == '#') + return prevTok; + return nullptr; } unsigned int simplecpp::TokenList::fileIndex(const std::string &filename) diff --git a/simplecpp.h b/simplecpp.h index 7e556657..e164fa90 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -359,9 +359,8 @@ namespace simplecpp { std::string readUntil(Stream &stream, const Location &location, char start, char end, OutputList *outputList); void lineDirective(unsigned int fileIndex, unsigned int line, Location *location); - std::string lastLine(int maxsize=1000) const; const Token* lastLineTok(int maxsize=1000) const; - bool isLastLinePreprocessor(int maxsize=1000) const; + const Token* isLastLinePreprocessor(int maxsize=1000) const; unsigned int fileIndex(const std::string &filename); From a766d223ca5a9e38cbc4844ef94813535663ca15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sat, 18 Oct 2025 16:19:04 +0200 Subject: [PATCH 06/15] enabled and cleaned up some compiler warning flags (#551) --- CMakeLists.txt | 36 ++++++++++++++++++++---------------- Makefile | 3 ++- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index efdc0d96..f13fb3fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,49 +19,50 @@ function(add_compile_options_safe FLAG) endif() endfunction() -if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-pedantic) + add_compile_options(-Wall) add_compile_options(-Wextra) add_compile_options(-Wcast-qual) # Cast for removing type qualifiers add_compile_options(-Wfloat-equal) # Floating values used in equality comparisons add_compile_options(-Wmissing-declarations) # If a global function is defined without a previous declaration add_compile_options(-Wmissing-format-attribute) # - add_compile_options(-Wno-long-long) add_compile_options(-Wpacked) # add_compile_options(-Wredundant-decls) # if anything is declared more than once in the same scope add_compile_options(-Wundef) - add_compile_options(-Wno-missing-braces) - add_compile_options(-Wno-sign-compare) - add_compile_options(-Wno-multichar) add_compile_options(-Woverloaded-virtual) # when a function declaration hides virtual functions from a base class add_compile_options(-Wsuggest-attribute=noreturn) add_compile_options_safe(-Wuseless-cast) -elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + + # we are not interested in these + set_source_files_properties(test.cpp PROPERTIES COMPILE_FLAGS -Wno-multichar) +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") add_compile_definitions(_CRT_SECURE_NO_WARNINGS) - # TODO: bump warning level - #add_compile_options(/W4) # Warning Level - # TODO: enable warning + + add_compile_options(/W4) # Warning Level + + add_compile_options(/wd4127) # warning C4127: conditional expression is constant + add_compile_options(/wd4244) # warning C4244: 'x': conversion from 'int' to 'char', possible loss of data add_compile_options(/wd4267) # warning C4267: '...': conversion from 'size_t' to 'unsigned int', possible loss of data + add_compile_options(/wd4706) # warning C4706: assignment within conditional expression elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Weverything) + # no need for c++98 compatibility add_compile_options(-Wno-c++98-compat-pedantic) - # these are not really fixable + + # these are not really fixable until newer standards add_compile_options(-Wno-exit-time-destructors) add_compile_options(-Wno-global-constructors) add_compile_options(-Wno-weak-vtables) add_compile_options_safe(-Wno-unsafe-buffer-usage) add_compile_options_safe(-Wno-nrvo) - # we are not interested in these - add_compile_options(-Wno-multichar) - add_compile_options(-Wno-four-char-constants) - # ignore C++11-specific warning - add_compile_options(-Wno-suggest-override) - add_compile_options(-Wno-suggest-destructor-override) + # contradicts -Wcovered-switch-default add_compile_options(-Wno-switch-default) + # TODO: fix these? add_compile_options(-Wno-padded) add_compile_options(-Wno-sign-conversion) @@ -69,6 +70,9 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wno-shorten-64-to-32) add_compile_options(-Wno-shadow-field-in-constructor) + # we are not interested in these + set_source_files_properties(test.cpp PROPERTIES COMPILE_FLAGS "-Wno-multichar -Wno-four-char-constants") + if (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 14 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 14) # TODO: verify this regression still exists in clang-15 if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") diff --git a/Makefile b/Makefile index b6bc26da..b7b54597 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ all: testrunner simplecpp CPPFLAGS ?= -CXXFLAGS = -Wall -Wextra -pedantic -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wredundant-decls -Wundef -Wno-multichar -Wold-style-cast -std=c++11 -g $(CXXOPTS) +CXXFLAGS = -Wall -Wextra -pedantic -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wpacked -Wredundant-decls -Wundef -Woverloaded-virtual -std=c++11 -g $(CXXOPTS) LDFLAGS = -g $(LDOPTS) # Define test source dir macro for compilation (preprocessor flags) @@ -9,6 +9,7 @@ TEST_CPPFLAGS = -DSIMPLECPP_TEST_SOURCE_DIR=\"$(CURDIR)\" # Only test.o gets the define test.o: CPPFLAGS += $(TEST_CPPFLAGS) +test.o: CXXFLAGS += -Wno-multichar %.o: %.cpp simplecpp.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< From 8e85885e62ff43ed51338c1c1e8a4dbb690b96a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Mon, 20 Oct 2025 17:21:01 +0200 Subject: [PATCH 07/15] refs #424 - removed filelist parameter from `simplecpp::Output` constructors / cleanups (#569) --- simplecpp.cpp | 220 +++++++++++++++++++++++++++----------------------- simplecpp.h | 3 +- 2 files changed, 122 insertions(+), 101 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 8f1879ef..d56a6ce8 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -422,7 +422,7 @@ class FileStream : public simplecpp::TokenList::Stream { { if (!file) { files.push_back(filename); - throw simplecpp::Output(files, simplecpp::Output::FILE_NOT_FOUND, "File is missing: " + filename); + throw simplecpp::Output(simplecpp::Output::FILE_NOT_FOUND, simplecpp::Location(files), "File is missing: " + filename); } init(); } @@ -615,14 +615,15 @@ static std::string escapeString(const std::string &str) return ostr.str(); } -static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector &files, const simplecpp::Location &location) +static void portabilityBackslash(simplecpp::OutputList *outputList, const simplecpp::Location &location) { if (!outputList) return; - simplecpp::Output err(files); - err.type = simplecpp::Output::PORTABILITY_BACKSLASH; - err.location = location; - err.msg = "Combination 'backslash space newline' is not portable."; + simplecpp::Output err = { + simplecpp::Output::PORTABILITY_BACKSLASH, + location, + "Combination 'backslash space newline' is not portable." + }; outputList->push_back(std::move(err)); } @@ -670,13 +671,12 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, if (ch >= 0x80) { if (outputList) { - simplecpp::Output err(files); - err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR; - err.location = location; - std::ostringstream s; - s << static_cast(ch); - err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported."; - outputList->push_back(err); + simplecpp::Output err = { + simplecpp::Output::UNHANDLED_CHAR_ERROR, + location, + "The code contains unhandled character(s) (character code=" + std::to_string(static_cast(ch)) + "). Neither unicode nor extended ascii is supported." + }; + outputList->push_back(std::move(err)); } clear(); return; @@ -685,7 +685,7 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, if (ch == '\n') { if (cback() && cback()->op == '\\') { if (location.col > cback()->location.col + 1U) - portabilityBackslash(outputList, files, cback()->location); + portabilityBackslash(outputList, cback()->location); ++multiline; deleteToken(back()); } else { @@ -812,7 +812,7 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, const TokenString check_portability = currentToken + tmp; const std::string::size_type pos = check_portability.find_last_not_of(" \t"); if (pos < check_portability.size() - 1U && check_portability[pos] == '\\') - portabilityBackslash(outputList, files, location); + portabilityBackslash(outputList, location); ++multiline; tmp_ch = stream.readChar(); currentToken += '\n'; @@ -872,11 +872,12 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, } if (!stream.good() || ch == '\n') { if (outputList) { - Output err(files); - err.type = Output::SYNTAX_ERROR; - err.location = location; - err.msg = "Invalid newline in raw string delimiter."; - outputList->push_back(err); + Output err = { + Output::SYNTAX_ERROR, + location, + "Invalid newline in raw string delimiter." + }; + outputList->push_back(std::move(err)); } return; } @@ -885,10 +886,11 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, currentToken += stream.readChar(); if (!endsWith(currentToken, endOfRawString)) { if (outputList) { - Output err(files); - err.type = Output::SYNTAX_ERROR; - err.location = location; - err.msg = "Raw string missing terminating delimiter."; + Output err = { + Output::SYNTAX_ERROR, + location, + "Raw string missing terminating delimiter." + }; outputList->push_back(std::move(err)); } return; @@ -1428,10 +1430,11 @@ std::string simplecpp::TokenList::readUntil(Stream &stream, const Location &loca if (!stream.good() || ch != end) { clear(); if (outputList) { - Output err(files); - err.type = Output::SYNTAX_ERROR; - err.location = location; - err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported."; + Output err = { + Output::SYNTAX_ERROR, + location, + std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported." + }; outputList->push_back(std::move(err)); } return ""; @@ -3160,10 +3163,11 @@ simplecpp::FileDataCache simplecpp::load(const simplecpp::TokenList &rawtokens, if (filedata == nullptr) { if (outputList) { - simplecpp::Output err(filenames); - err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; - err.location = Location(filenames); - err.msg = "Can not open include file '" + filename + "' that is explicitly included."; + simplecpp::Output err = { + simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND, + Location(filenames), + "Can not open include file '" + filename + "' that is explicitly included." + }; outputList->push_back(std::move(err)); } continue; @@ -3231,12 +3235,13 @@ static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token simplecpp::TokenList value(files); try { *tok1 = it->second.expand(value, tok, macros, files); - } catch (simplecpp::Macro::Error &err) { + } catch (const simplecpp::Macro::Error &err) { if (outputList) { - simplecpp::Output out(files); - out.type = simplecpp::Output::SYNTAX_ERROR; - out.location = err.location; - out.msg = "failed to expand \'" + tok->str() + "\', " + err.what; + simplecpp::Output out = { + simplecpp::Output::SYNTAX_ERROR, + err.location, + "failed to expand \'" + tok->str() + "\', " + err.what + }; outputList->push_back(std::move(out)); } return false; @@ -3348,10 +3353,12 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL const cppstd_t cpp_std = simplecpp::getCppStd(dui.std); if (cpp_std == CPPUnknown) { if (outputList) { - simplecpp::Output err(files); - err.type = Output::DUI_ERROR; - err.msg = "unknown standard specified: '" + dui.std + "'"; - outputList->push_back(err); + simplecpp::Output err = { + Output::DUI_ERROR, + Location(files), + "unknown standard specified: '" + dui.std + "'" + }; + outputList->push_back(std::move(err)); } output.clear(); return; @@ -3403,11 +3410,12 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) { if (outputList) { - simplecpp::Output err(files); - err.type = Output::SYNTAX_ERROR; - err.location = rawtok->location; - err.msg = "#" + rawtok->str() + " without #if"; - outputList->push_back(err); + simplecpp::Output err = { + Output::SYNTAX_ERROR, + rawtok->location, + "#" + rawtok->str() + " without #if" + }; + outputList->push_back(std::move(err)); } output.clear(); return; @@ -3415,15 +3423,19 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL if (ifstates.top() == True && (rawtok->str() == ERROR || rawtok->str() == WARNING)) { if (outputList) { - simplecpp::Output err(rawtok->location.files); - err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING; - err.location = rawtok->location; + std::string msg; for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) { - if (!err.msg.empty() && isNameChar(tok->str()[0])) - err.msg += ' '; - err.msg += tok->str(); + if (!msg.empty() && isNameChar(tok->str()[0])) + msg += ' '; + msg += tok->str(); } - err.msg = '#' + rawtok->str() + ' ' + err.msg; + msg = '#' + rawtok->str() + ' ' + msg; + simplecpp::Output err = { + rawtok->str() == ERROR ? Output::ERROR : Output::WARNING, + rawtok->location, + std::move(msg) + }; + outputList->push_back(std::move(err)); } if (rawtok->str() == ERROR) { @@ -3446,21 +3458,23 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL } } catch (const std::runtime_error &) { if (outputList) { - simplecpp::Output err(files); - err.type = Output::SYNTAX_ERROR; - err.location = rawtok->location; - err.msg = "Failed to parse #define"; - outputList->push_back(err); + simplecpp::Output err = { + Output::SYNTAX_ERROR, + rawtok->location, + "Failed to parse #define" + }; + outputList->push_back(std::move(err)); } output.clear(); return; - } catch (simplecpp::Macro::Error &err) { + } catch (const simplecpp::Macro::Error &err) { if (outputList) { - simplecpp::Output out(files); - out.type = simplecpp::Output::SYNTAX_ERROR; - out.location = err.location; - out.msg = "Failed to parse #define, " + err.what; - outputList->push_back(out); + simplecpp::Output out = { + simplecpp::Output::SYNTAX_ERROR, + err.location, + "Failed to parse #define, " + err.what + }; + outputList->push_back(std::move(out)); } output.clear(); return; @@ -3496,11 +3510,12 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL if (inc2.empty() || inc2.cfront()->str().size() <= 2U) { if (outputList) { - simplecpp::Output err(files); - err.type = Output::SYNTAX_ERROR; - err.location = rawtok->location; - err.msg = "No header in #include"; - outputList->push_back(err); + simplecpp::Output err = { + Output::SYNTAX_ERROR, + rawtok->location, + "No header in #include" + }; + outputList->push_back(std::move(err)); } output.clear(); return; @@ -3513,19 +3528,21 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL const FileData *const filedata = cache.get(rawtok->location.file(), header, dui, systemheader, files, outputList).first; if (filedata == nullptr) { if (outputList) { - simplecpp::Output out(files); - out.type = Output::MISSING_HEADER; - out.location = rawtok->location; - out.msg = "Header not found: " + inctok->str(); - outputList->push_back(out); + simplecpp::Output out = { + simplecpp::Output::MISSING_HEADER, + rawtok->location, + "Header not found: " + inctok->str() + }; + outputList->push_back(std::move(out)); } } else if (includetokenstack.size() >= 400) { if (outputList) { - simplecpp::Output out(files); - out.type = Output::INCLUDE_NESTED_TOO_DEEPLY; - out.location = rawtok->location; - out.msg = "#include nested too deeply"; - outputList->push_back(out); + simplecpp::Output out = { + simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY, + rawtok->location, + "#include nested too deeply" + }; + outputList->push_back(std::move(out)); } } else if (pragmaOnce.find(filedata->filename) == pragmaOnce.end()) { includetokenstack.push(gotoNextLine(rawtok)); @@ -3535,11 +3552,12 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL } else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) { if (!sameline(rawtok,rawtok->next)) { if (outputList) { - simplecpp::Output out(files); - out.type = Output::SYNTAX_ERROR; - out.location = rawtok->location; - out.msg = "Syntax error in #" + rawtok->str(); - outputList->push_back(out); + simplecpp::Output out = { + simplecpp::Output::SYNTAX_ERROR, + rawtok->location, + "Syntax error in #" + rawtok->str() + }; + outputList->push_back(std::move(out)); } output.clear(); return; @@ -3580,10 +3598,11 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL tok = tok ? tok->next : nullptr; if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) { if (outputList) { - Output out(rawtok->location.files); - out.type = Output::SYNTAX_ERROR; - out.location = rawtok->location; - out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + Output out = { + Output::SYNTAX_ERROR, + rawtok->location, + "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition" + }; outputList->push_back(std::move(out)); } output.clear(); @@ -3622,11 +3641,12 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL tok = tok ? tok->next : nullptr; if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')') || (!closingAngularBracket)) { if (outputList) { - Output out(rawtok->location.files); - out.type = Output::SYNTAX_ERROR; - out.location = rawtok->location; - out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; - outputList->push_back(out); + Output out = { + Output::SYNTAX_ERROR, + rawtok->location, + "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition" + }; + outputList->push_back(std::move(out)); } output.clear(); return; @@ -3659,13 +3679,15 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL } } catch (const std::exception &e) { if (outputList) { - Output out(rawtok->location.files); - out.type = Output::SYNTAX_ERROR; - out.location = rawtok->location; - out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + std::string msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; if (e.what() && *e.what()) - out.msg += std::string(", ") + e.what(); - outputList->push_back(out); + msg += std::string(", ") + e.what(); + Output out = { + Output::SYNTAX_ERROR, + rawtok->location, + std::move(msg) + }; + outputList->push_back(std::move(out)); } output.clear(); return; diff --git a/simplecpp.h b/simplecpp.h index e164fa90..b1b25fad 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -201,7 +201,6 @@ namespace simplecpp { /** Output from preprocessor */ struct SIMPLECPP_LIB Output { - explicit Output(const std::vector &files) : type(ERROR), location(files) {} enum Type : std::uint8_t { ERROR, /* #error */ WARNING, /* #warning */ @@ -214,7 +213,7 @@ namespace simplecpp { FILE_NOT_FOUND, DUI_ERROR } type; - explicit Output(const std::vector& files, Type type, const std::string& msg) : type(type), location(files), msg(msg) {} + Output(Type type, const Location& loc, std::string msg) : type(type), location(loc), msg(std::move(msg)) {} Location location; std::string msg; }; From bd154957a525c71ad29362f22aecc67d96682b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 28 Oct 2025 12:08:34 +0100 Subject: [PATCH 08/15] enabled and fixed `readability-simplify-boolean-expr` clang-tidy warnings (#568) --- .clang-tidy | 1 - simplecpp.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 182f1a55..8dc64681 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -42,7 +42,6 @@ Checks: > -readability-isolate-declaration, -readability-magic-numbers, -readability-redundant-inline-specifier, - -readability-simplify-boolean-expr, -readability-use-concise-preprocessor-directives, -readability-uppercase-literal-suffix, -performance-avoid-endl, diff --git a/simplecpp.cpp b/simplecpp.cpp index d56a6ce8..1e18d2ef 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -882,7 +882,7 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, return; } const std::string endOfRawString(')' + delim + currentToken); - while (stream.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1)) + while (stream.good() && (!endsWith(currentToken, endOfRawString) || currentToken.size() <= 1)) currentToken += stream.readChar(); if (!endsWith(currentToken, endOfRawString)) { if (outputList) { @@ -2052,7 +2052,7 @@ namespace simplecpp { } const Token *recursiveExpandToken(TokenList &output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { - if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) { + if (!temp.cback() || !temp.cback()->name || !tok->next || tok->next->op != '(') { output.takeTokens(temp); return tok->next; } From e78af461426b3cae40b1cc79a4a5e540b057b613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 28 Oct 2025 20:36:01 +0100 Subject: [PATCH 09/15] cleaned up includes based on `include-what-you-use` (#576) --- simplecpp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/simplecpp.h b/simplecpp.h index b1b25fad..ee52ad43 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #if __cplusplus >= 202002L # include @@ -41,7 +42,7 @@ #endif #ifndef _WIN32 -# include +# include #endif #if defined(_MSC_VER) @@ -69,7 +70,6 @@ namespace simplecpp { using TokenString = std::string; class Macro; - class FileDataCache; /** * Location in source code From 4aa40060b86e3103825a00b95a4c383e34a148a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 28 Oct 2025 20:36:15 +0100 Subject: [PATCH 10/15] enabled and fixed `modernize-use-auto` clang-tidy warnings (#574) --- .clang-tidy | 1 - simplecpp.cpp | 24 ++++++++++++------------ simplecpp.h | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 8dc64681..b63cb3d9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -30,7 +30,6 @@ Checks: > -modernize-loop-convert, -modernize-pass-by-value, -modernize-return-braced-init-list, - -modernize-use-auto, -modernize-use-nodiscard, -modernize-use-trailing-return-type, -readability-avoid-nested-conditional-operator, diff --git a/simplecpp.cpp b/simplecpp.cpp index 1e18d2ef..e6186be2 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -253,12 +253,12 @@ class simplecpp::TokenList::Stream { virtual bool good() = 0; unsigned char readChar() { - unsigned char ch = static_cast(get()); + auto ch = static_cast(get()); // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the // character is non-ASCII character then replace it with 0xff if (isUtf16) { - const unsigned char ch2 = static_cast(get()); + const auto ch2 = static_cast(get()); const int ch16 = makeUtf16Char(ch, ch2); ch = static_cast(((ch16 >= 0x80) ? 0xff : ch16)); } @@ -281,13 +281,13 @@ class simplecpp::TokenList::Stream { } unsigned char peekChar() { - unsigned char ch = static_cast(peek()); + auto ch = static_cast(peek()); // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the // character is non-ASCII character then replace it with 0xff if (isUtf16) { (void)get(); - const unsigned char ch2 = static_cast(peek()); + const auto ch2 = static_cast(peek()); unget(); const int ch16 = makeUtf16Char(ch, ch2); ch = static_cast(((ch16 >= 0x80) ? 0xff : ch16)); @@ -1705,7 +1705,7 @@ namespace simplecpp { private: /** Create new token where Token::macro is set for replaced tokens */ Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced, const Token *expandedFromToken=nullptr) const { - Token *tok = new Token(str,loc); + auto *tok = new Token(str,loc); if (replaced) tok->macro = nameTokDef->str(); if (expandedFromToken) @@ -2365,11 +2365,11 @@ namespace simplecpp { static bool isReplaced(const std::set &expandedmacros) { // return true if size > 1 - std::set::const_iterator it = expandedmacros.begin(); - if (it == expandedmacros.end()) + auto it = expandedmacros.cbegin(); + if (it == expandedmacros.cend()) return false; ++it; - return (it != expandedmacros.end()); + return (it != expandedmacros.cend()); } /** name token in definition */ @@ -3060,7 +3060,7 @@ std::pair simplecpp::FileDataCache::tryload(FileDat return {id_it->second, false}; } - FileData *const data = new FileData {path, TokenList(path, filenames, outputList)}; + auto *const data = new FileData {path, TokenList(path, filenames, outputList)}; if (dui.removeComments) data->tokens.removeComments(); @@ -3154,7 +3154,7 @@ simplecpp::FileDataCache simplecpp::load(const simplecpp::TokenList &rawtokens, std::list filelist; // -include files - for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { + for (auto it = dui.includes.cbegin(); it != dui.includes.cend(); ++it) { const std::string &filename = *it; const auto loadResult = cache.get("", filename, dui, false, filenames, outputList); @@ -3316,7 +3316,7 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL const bool hasInclude = isCpp17OrLater(dui) || isGnu(dui); MacroMap macros; bool strictAnsiDefined = false; - for (std::list::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { + for (auto it = dui.defines.cbegin(); it != dui.defines.cend(); ++it) { const std::string ¯ostr = *it; const std::string::size_type eq = macrostr.find('='); const std::string::size_type par = macrostr.find('('); @@ -3382,7 +3382,7 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL std::set pragmaOnce; includetokenstack.push(rawtokens.cfront()); - for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { + for (auto it = dui.includes.cbegin(); it != dui.includes.cend(); ++it) { const FileData *const filedata = cache.get("", *it, dui, false, files, outputList).first; if (filedata != nullptr && filedata->tokens.cfront() != nullptr) includetokenstack.push(filedata->tokens.cfront()); diff --git a/simplecpp.h b/simplecpp.h index ee52ad43..22f3c19f 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -423,7 +423,7 @@ namespace simplecpp { void insert(FileData data) { // NOLINTNEXTLINE(misc-const-correctness) - FP - FileData *const newdata = new FileData(std::move(data)); + auto *const newdata = new FileData(std::move(data)); mData.emplace_back(newdata); mNameMap.emplace(newdata->filename, newdata); From a8fcf9bb5eb26dfcd1a830f0ac0502d46cd42604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 28 Oct 2025 20:36:27 +0100 Subject: [PATCH 11/15] moved static array into `simplifyName()` and simplified it (#575) --- simplecpp.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index e6186be2..4e892e03 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2666,12 +2666,11 @@ static void simplifyHasInclude(simplecpp::TokenList &expr, const simplecpp::DUI } } -static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"}; -static const std::set altop(&altopData[0], &altopData[8]); static void simplifyName(simplecpp::TokenList &expr) { for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->name) { + static const std::set altop = {"and","or","bitand","bitor","compl","not","not_eq","xor"}; if (altop.find(tok->str()) != altop.end()) { bool alt; if (tok->str() == "not" || tok->str() == "compl") { From 6ddb8c6a37a304069527053972269eb7081bee32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 28 Oct 2025 20:37:05 +0100 Subject: [PATCH 12/15] improved testing of `#line` handling (#505) --- simplecpp.cpp | 1 + test.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/simplecpp.cpp b/simplecpp.cpp index 4e892e03..7d306fd7 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -715,6 +715,7 @@ void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); location.line = 1U; } + // TODO: add support for "# 3" // #3 "file.c" // #line 3 "file.c" else if ((llNextToken->number && diff --git a/test.cpp b/test.cpp index 26e3b95b..5f09c17f 100644 --- a/test.cpp +++ b/test.cpp @@ -2061,6 +2061,77 @@ static void location5() "int x ;", preprocess(code)); } +static void location6() +{ + const char code[] = + "#line 3\n" + "__LINE__ __FILE__\n"; + ASSERT_EQUALS("\n" + "\n" + "3 \"\"", + preprocess(code)); +} + +static void location7() +{ + const char code[] = + "#line 3 \"file.c\"\n" + "__LINE__ __FILE__\n"; + ASSERT_EQUALS("\n" + "#line 3 \"file.c\"\n" + "3 \"file.c\"", + preprocess(code)); +} + +static void location8() +{ + const char code[] = + "# 3\n" + "__LINE__ __FILE__\n"; + ASSERT_EQUALS("\n" + "2 \"\"", // TODO: should say 3 + preprocess(code)); +} + +static void location9() +{ + const char code[] = + "# 3 \"file.c\"\n" + "__LINE__ __FILE__\n"; + ASSERT_EQUALS("\n" + "#line 3 \"file.c\"\n" + "3 \"file.c\"", + preprocess(code)); +} + +static void location10() +{ + const char code[] = + "#line 3\n" + "__LINE__ __FILE__\n"; + ASSERT_EQUALS("\n" + "\n" // TODO: should this have the #line marker? + "3 \"\"", + preprocess(code)); +} + +static void location11() +{ + const char code[] = + "#line 3 \"file.c\"\n" + "__LINE__ __FILE__\n" + "#line 33 \"file2.c\"\n" + "__LINE__ __FILE__\n"; + ASSERT_EQUALS("\n" + "#line 3 \"file.c\"\n" + "3 \"file.c\"\n" + "#line 33 \"file2.c\"\n" + "33 \"file2.c\"", + preprocess(code)); +} + +// TODO: test #file/#endfile + static void missingHeader1() { const char code[] = "#include \"notexist.h\"\n"; @@ -3489,6 +3560,12 @@ int main(int argc, char **argv) TEST_CASE(location3); TEST_CASE(location4); TEST_CASE(location5); + TEST_CASE(location6); + TEST_CASE(location7); + TEST_CASE(location8); + TEST_CASE(location9); + TEST_CASE(location10); + TEST_CASE(location11); TEST_CASE(missingHeader1); TEST_CASE(missingHeader2); From d381ec26441476e5b6618b1cdc0bb118395547a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sat, 1 Nov 2025 14:23:29 +0100 Subject: [PATCH 13/15] CI-unixish.yml: added profile-guided optimization (PGO) to callgrind step (#580) --- .github/workflows/CI-unixish.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index b4089ee1..d6800a82 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -43,11 +43,12 @@ jobs: sudo apt-get update sudo apt-get install valgrind + # llvm contains llvm-profdata - name: Install missing software on ubuntu (clang++) if: contains(matrix.os, 'ubuntu') && matrix.compiler == 'clang++' run: | sudo apt-get update - sudo apt-get install libc++-dev + sudo apt-get install libc++-dev llvm # coreutils contains "nproc" - name: Install missing software on macos @@ -145,15 +146,34 @@ jobs: run: | wget https://github.com/danmar/simplecpp/archive/refs/tags/1.5.1.tar.gz tar xvf 1.5.1.tar.gz + rm -f 1.5.1.tar.gz + make clean - make -j$(nproc) CXXOPTS="-O2 -g3" + make -j$(nproc) CXXOPTS="-O2 -g3" simplecpp VALGRIND_TOOL=callgrind SIMPLECPP_PATH=simplecpp-1.5.1 ./selfcheck.sh >callgrind.log || (cat callgrind.log && false) cat callgrind.log + + # PGO - start + make clean + make -j$(nproc) CXXOPTS="-O2 -g3 -fprofile-generate" LDOPTS="-fprofile-generate" simplecpp + SIMPLECPP_PATH=simplecpp-1.5.1 ./selfcheck.sh >/dev/null + + if compgen -G "default_*.profraw" > /dev/null; then + llvm-profdata merge -output=default.profdata default_*.profraw + fi + + make clean + make -j$(nproc) CXXOPTS="-O2 -g3 -fprofile-use" LDOPTS="-fprofile-use" simplecpp + VALGRIND_TOOL=callgrind SIMPLECPP_PATH=simplecpp-1.5.1 ./selfcheck.sh >callgrind_pgo.log || (cat callgrind_pgo.log && false) + cat callgrind_pgo.log + # PGO - end + for f in callgrind.out.*; do callgrind_annotate --auto=no $f > $f.annotated.log head -50 $f.annotated.log done + rm -rf simplecpp-1.5.1 - uses: actions/upload-artifact@v4 if: matrix.os == 'ubuntu-24.04' From 7a81c1ab935a28b38e21d0338654ee256577d3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 5 Nov 2025 11:49:40 +0100 Subject: [PATCH 14/15] Fix #581 (Failure when header is included twice with different macros defined) (#582) --- integration_test.py | 35 +++++++++++++++++++++++++++++++++++ simplecpp.cpp | 11 +++++------ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/integration_test.py b/integration_test.py index e5497db3..068da776 100644 --- a/integration_test.py +++ b/integration_test.py @@ -446,3 +446,38 @@ def test_incpath_dir(record_property, tmpdir): assert '' == stderr assert "error: could not open include 'inc'\n" == stdout + + +def test_include_header_twice(tmpdir): + """ Issue #581 - Failure when header is included twice with different + macros defined""" + + header_file = tmpdir / 'test.h' + with open(header_file, 'wt') as f: + f.write(f""" + #if defined AAA + #elif defined BBB + # undef BBB + #endif + + #ifdef BBB + # error BBB is defined + #endif + """) + + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(f""" + # define Y + # include "test.h" + + # define BBB + # include "test.h" + """) + + args = [test_file] + + _, stdout, stderr = simplecpp(args, cwd=tmpdir) + + assert stderr == '' + diff --git a/simplecpp.cpp b/simplecpp.cpp index 7d306fd7..ef891eb1 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -3701,12 +3701,11 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL else ifstates.push(conditionIsTrue ? True : ElseIsTrue); iftokens.push(rawtok); - } else if (ifstates.top() == True) { - ifstates.top() = AlwaysFalse; - iftokens.top()->nextcond = rawtok; - iftokens.top() = rawtok; - } else if (ifstates.top() == ElseIsTrue && conditionIsTrue) { - ifstates.top() = True; + } else { + if (ifstates.top() == True) + ifstates.top() = AlwaysFalse; + else if (ifstates.top() == ElseIsTrue && conditionIsTrue) + ifstates.top() = True; iftokens.top()->nextcond = rawtok; iftokens.top() = rawtok; } From 5cd15b3eaf7389347b018131e8f6e2bf45590b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Fri, 7 Nov 2025 15:06:52 +0100 Subject: [PATCH 15/15] test.cpp: do not unconditionally remove comments (#506) --- test.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/test.cpp b/test.cpp index 5f09c17f..69e08781 100644 --- a/test.cpp +++ b/test.cpp @@ -107,7 +107,8 @@ static std::string preprocess(const char code[], const simplecpp::DUI &dui, simp std::vector files; simplecpp::FileDataCache cache; simplecpp::TokenList tokens = makeTokenList(code,files); - tokens.removeComments(); + if (dui.removeComments) + tokens.removeComments(); simplecpp::TokenList tokens2(files); simplecpp::preprocess(tokens2, tokens, files, cache, dui, outputList); simplecpp::cleanup(cache); @@ -437,33 +438,36 @@ static void comment() static void comment_multiline() { + simplecpp::DUI dui; + dui.removeComments = true; + const char code[] = "#define ABC {// \\\n" "}\n" "void f() ABC\n"; - ASSERT_EQUALS("\n\nvoid f ( ) {", preprocess(code)); + ASSERT_EQUALS("\n\nvoid f ( ) {", preprocess(code, dui)); const char code1[] = "#define ABC {// \\\r\n" "}\n" "void f() ABC\n"; - ASSERT_EQUALS("\n\nvoid f ( ) {", preprocess(code1)); + ASSERT_EQUALS("\n\nvoid f ( ) {", preprocess(code1, dui)); const char code2[] = "#define A 1// \\\r" "\r" "2\r" "A\r"; - ASSERT_EQUALS("\n\n2\n1", preprocess(code2)); + ASSERT_EQUALS("\n\n2\n1", preprocess(code2, dui)); const char code3[] = "void f() {// \\ \n}\n"; - ASSERT_EQUALS("void f ( ) {", preprocess(code3)); + ASSERT_EQUALS("void f ( ) {", preprocess(code3, dui)); const char code4[] = "void f() {// \\\\\\\t\t\n}\n"; - ASSERT_EQUALS("void f ( ) {", preprocess(code4)); + ASSERT_EQUALS("void f ( ) {", preprocess(code4, dui)); const char code5[] = "void f() {// \\\\\\a\n}\n"; - ASSERT_EQUALS("void f ( ) {\n}", preprocess(code5)); + ASSERT_EQUALS("void f ( ) {\n}", preprocess(code5, dui)); const char code6[] = "void f() {// \\\n\n\n}\n"; - ASSERT_EQUALS("void f ( ) {\n\n\n}", preprocess(code6)); + ASSERT_EQUALS("void f ( ) {\n\n\n}", preprocess(code6, dui)); // #471 ensure there is newline in comment so that line-splicing can be detected by tools ASSERT_EQUALS("// abc\ndef", readfile("// abc\\\ndef")); @@ -570,9 +574,12 @@ static void define6() static void define7() { + simplecpp::DUI dui; + dui.removeComments = true; + const char code[] = "#define A(X) X+1\n" "A(1 /*23*/)"; - ASSERT_EQUALS("\n1 + 1", preprocess(code)); + ASSERT_EQUALS("\n1 + 1", preprocess(code, dui)); } static void define8() // 6.10.3.10 @@ -1591,6 +1598,7 @@ static void has_include_2() " #endif\n" "#endif"; simplecpp::DUI dui; + dui.removeComments = true; // TODO: remove this dui.includePaths.push_back(testSourceDir); ASSERT_EQUALS("\n\nA", preprocess(code, dui)); // we default to latest standard internally dui.std = "c++14"; @@ -2167,7 +2175,9 @@ static void missingHeader4() { const char code[] = "#/**/include <>\n"; simplecpp::OutputList outputList; - ASSERT_EQUALS("", preprocess(code, &outputList)); + simplecpp::DUI dui; + dui.removeComments = true; // TODO: remove this + ASSERT_EQUALS("", preprocess(code, dui, &outputList)); ASSERT_EQUALS("file0,1,syntax_error,No header in #include\n", toString(outputList)); }