From 8a30993ececc91d9540832dd70e943727dd0050e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Tue, 10 Mar 2026 10:36:31 +0100 Subject: [PATCH 01/15] reverted to Clang 21 for now (#630) --- .clang-tidy | 2 -- .github/workflows/clang-tidy.yml | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index e0b384bf..ba15bb0f 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -22,8 +22,6 @@ Checks: > -bugprone-easily-swappable-parameters, -bugprone-narrowing-conversions, -bugprone-switch-missing-default-case, - -bugprone-throwing-static-initialization, - -bugprone-unchecked-string-to-number-conversion, -concurrency-mt-unsafe, -misc-no-recursion, -misc-non-private-member-variables-in-classes, diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index b2b32b7d..9109be9e 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -33,19 +33,19 @@ jobs: run: | wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh - sudo ./llvm.sh 22 - sudo apt-get install clang-tidy-22 + sudo ./llvm.sh 21 + sudo apt-get install clang-tidy-21 - name: Verify clang-tidy configuration run: | - clang-tidy-22 --verify-config + clang-tidy-21 --verify-config - name: Prepare CMake run: | cmake -S . -B cmake.output -Werror=dev --warn-uninitialized -DCMAKE_COMPILE_WARNING_AS_ERROR=On -DCMAKE_EXPORT_COMPILE_COMMANDS=ON env: - CXX: clang-22 + CXX: clang-21 - name: Clang-Tidy run: | - run-clang-tidy-22 -q -j $(nproc) -enable-check-profile -p=cmake.output + run-clang-tidy-21 -q -j $(nproc) -p=cmake.output From efb55b81fd6addfd23f608cce33f0eef02960752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sun, 15 Mar 2026 18:45:54 +0100 Subject: [PATCH 02/15] clang-tidy.yml: updated to Clang 22 - again (#633) --- .clang-tidy | 2 ++ .github/workflows/clang-tidy.yml | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index ba15bb0f..e0b384bf 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -22,6 +22,8 @@ Checks: > -bugprone-easily-swappable-parameters, -bugprone-narrowing-conversions, -bugprone-switch-missing-default-case, + -bugprone-throwing-static-initialization, + -bugprone-unchecked-string-to-number-conversion, -concurrency-mt-unsafe, -misc-no-recursion, -misc-non-private-member-variables-in-classes, diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 9109be9e..b2b32b7d 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -33,19 +33,19 @@ jobs: run: | wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh - sudo ./llvm.sh 21 - sudo apt-get install clang-tidy-21 + sudo ./llvm.sh 22 + sudo apt-get install clang-tidy-22 - name: Verify clang-tidy configuration run: | - clang-tidy-21 --verify-config + clang-tidy-22 --verify-config - name: Prepare CMake run: | cmake -S . -B cmake.output -Werror=dev --warn-uninitialized -DCMAKE_COMPILE_WARNING_AS_ERROR=On -DCMAKE_EXPORT_COMPILE_COMMANDS=ON env: - CXX: clang-21 + CXX: clang-22 - name: Clang-Tidy run: | - run-clang-tidy-21 -q -j $(nproc) -p=cmake.output + run-clang-tidy-22 -q -j $(nproc) -enable-check-profile -p=cmake.output From ae1f0fbdfc626548398e80676928d5bfd01e0688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sun, 15 Mar 2026 20:06:17 +0100 Subject: [PATCH 03/15] added basic test coverage for `IfCond` and `MacroUsage` (#629) --- test.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/test.cpp b/test.cpp index fa94a266..1dd59587 100644 --- a/test.cpp +++ b/test.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -103,7 +104,7 @@ static std::string readfile(const char code[], std::size_t size, simplecpp::Outp return makeTokenList(code,size,files,std::string(),outputList).stringify(); } -static std::string preprocess(const char code[], const simplecpp::DUI &dui, simplecpp::OutputList *outputList, const std::string &file = std::string()) +static std::string preprocess(const char code[], const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage = nullptr, std::list *ifCond = nullptr, const std::string &file = std::string()) { std::vector files; simplecpp::FileDataCache cache; @@ -111,7 +112,7 @@ static std::string preprocess(const char code[], const simplecpp::DUI &dui, simp if (dui.removeComments) tokens.removeComments(); simplecpp::TokenList tokens2(files); - simplecpp::preprocess(tokens2, tokens, files, cache, dui, outputList); + simplecpp::preprocess(tokens2, tokens, files, cache, dui, outputList, macroUsage, ifCond); simplecpp::cleanup(cache); return tokens2.stringify(); } @@ -123,7 +124,7 @@ static std::string preprocess(const char code[]) static std::string preprocess(const char code[], const std::string &file) { - return preprocess(code, simplecpp::DUI(), nullptr, file); + return preprocess(code, simplecpp::DUI(), nullptr, nullptr, nullptr, file); } static std::string preprocess(const char code[], const simplecpp::DUI &dui) @@ -136,6 +137,16 @@ static std::string preprocess(const char code[], simplecpp::OutputList *outputLi return preprocess(code, simplecpp::DUI(), outputList); } +static std::string preprocess(const char code[], std::list *ifCond) +{ + return preprocess(code, simplecpp::DUI(), nullptr, nullptr, ifCond); +} + +static std::string preprocess(const char code[], std::list *macroUsage) +{ + return preprocess(code, simplecpp::DUI(), nullptr, macroUsage); +} + static std::string toString(const simplecpp::OutputList &outputList) { std::ostringstream ostr; @@ -3461,6 +3472,70 @@ static void bad_macro_syntax() // #616 ASSERT_EQUALS("bad macro syntax. macroname=\" value=1", outputList.cbegin()->msg); } +static void ifCond() +{ + { + const char code[] = "int i;"; + std::list ifCond; + ASSERT_EQUALS("int i ;", preprocess(code, &ifCond)); + ASSERT_EQUALS(0, ifCond.size()); + } + { + const char code[] = "#if 0\n" + "# elif __GNUC__ == 1\n" + "# elif defined(__APPLE__)\n" + "#endif\n"; + std::list ifCond; + ASSERT_EQUALS("", preprocess(code, &ifCond)); + ASSERT_EQUALS(3, ifCond.size()); + auto it = ifCond.cbegin(); + ASSERT_EQUALS(0, it->location.fileIndex); + ASSERT_EQUALS(1, it->location.line); + ASSERT_EQUALS(2, it->location.col); + ASSERT_EQUALS("0", it->E); + ASSERT_EQUALS(0, it->result); + ++it; + ASSERT_EQUALS(0, it->location.fileIndex); + ASSERT_EQUALS(2, it->location.line); + ASSERT_EQUALS(3, it->location.col); + ASSERT_EQUALS("__GNUC__ == 1", it->E); + ASSERT_EQUALS(0, it->result); + ++it; + ASSERT_EQUALS(0, it->location.fileIndex); + ASSERT_EQUALS(3, it->location.line); + ASSERT_EQUALS(4, it->location.col); + ASSERT_EQUALS("0", it->E); + ASSERT_EQUALS(0, it->result); + } +} + +static void macroUsage() +{ + { + const char code[] = "int i;"; + std::list macroUsage; + ASSERT_EQUALS("int i ;", preprocess(code, ¯oUsage)); + ASSERT_EQUALS(0, macroUsage.size()); + } + { + const char code[] = "#define DEF_1\n" + "#ifdef DEF_1\n" + "#endif\n"; + std::list macroUsage; + ASSERT_EQUALS("", preprocess(code, ¯oUsage)); + ASSERT_EQUALS(1, macroUsage.size()); + auto it = macroUsage.cbegin(); + ASSERT_EQUALS("DEF_1", it->macroName); + ASSERT_EQUALS(0, it->macroLocation.fileIndex); + ASSERT_EQUALS(1, it->macroLocation.line); + ASSERT_EQUALS(9, it->macroLocation.col); + ASSERT_EQUALS(true, it->macroValueKnown); + ASSERT_EQUALS(0, it->useLocation.fileIndex); + ASSERT_EQUALS(2, it->useLocation.line); + ASSERT_EQUALS(8, it->useLocation.col); + } +} + static void isAbsolutePath() { #ifdef _WIN32 ASSERT_EQUALS(true, simplecpp::isAbsolutePath("C:\\foo\\bar")); @@ -3790,6 +3865,9 @@ int main(int argc, char **argv) TEST_CASE(bad_macro_syntax); + TEST_CASE(ifCond); + TEST_CASE(macroUsage); + TEST_CASE(fuzz_crash); TEST_CASE(leak); From f437d61a26ccf3a7163456d14a507db6b1de5e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Fri, 27 Mar 2026 18:16:06 +0100 Subject: [PATCH 04/15] fixed #632 - avoid unhandled `simplecpp::Macro::Error` in `simplecpp::preprocess` with `-D` (#631) --- simplecpp.cpp | 22 ++++++++++++++++++---- test.cpp | 14 ++++++++++++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index 7afc17ab..b59f773c 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -1739,6 +1739,9 @@ namespace simplecpp { return tok; } + /** + * @throws Error thrown in case of __VA_OPT__ issues + */ bool parseDefine(const Token *nametoken) { nameTokDef = nametoken; variadic = false; @@ -3379,6 +3382,17 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL } output.clear(); return; + } catch (const simplecpp::Macro::Error& e) { + if (outputList) { + simplecpp::Output err{ + Output::DUI_ERROR, + {}, + e.what + }; + outputList->emplace_back(std::move(err)); + } + output.clear(); + return; } } @@ -3507,14 +3521,14 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL else it->second = macro; } - } catch (const std::runtime_error &) { + } catch (const std::runtime_error &err) { if (outputList) { - simplecpp::Output err{ + simplecpp::Output out{ Output::SYNTAX_ERROR, rawtok->location, - "Failed to parse #define" + std::string("Failed to parse #define, ") + err.what() }; - outputList->emplace_back(std::move(err)); + outputList->emplace_back(std::move(out)); } output.clear(); return; diff --git a/test.cpp b/test.cpp index 1dd59587..b0bf0b4f 100644 --- a/test.cpp +++ b/test.cpp @@ -698,7 +698,7 @@ static void define_invalid_1() const char code[] = "#define A(\nB\n"; simplecpp::OutputList outputList; ASSERT_EQUALS("", preprocess(code, &outputList)); - ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define\n", toString(outputList)); + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, bad macro syntax\n", toString(outputList)); } static void define_invalid_2() @@ -706,7 +706,7 @@ static void define_invalid_2() const char code[] = "#define\nhas#"; simplecpp::OutputList outputList; ASSERT_EQUALS("", preprocess(code, &outputList)); - ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define\n", toString(outputList)); + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, bad macro syntax\n", toString(outputList)); } static void define_define_1() @@ -1105,6 +1105,15 @@ static void define_va_opt_8() ASSERT_EQUALS("", toString(outputList)); } +static void define_va_opt_9() +{ + simplecpp::DUI dui; + dui.defines.emplace_back("f(...)=__VA_OPT__"); + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess("", dui, &outputList)); + ASSERT_EQUALS("file0,0,dui_error,In definition of 'f': Missing opening parenthesis for __VA_OPT__\n", toString(outputList)); +} + static void define_ifdef() { const char code[] = "#define A(X) X\n" @@ -3674,6 +3683,7 @@ int main(int argc, char **argv) TEST_CASE(define_va_opt_6); TEST_CASE(define_va_opt_7); TEST_CASE(define_va_opt_8); + TEST_CASE(define_va_opt_9); // #632 TEST_CASE(pragma_backslash); // multiline pragma directive From 87c13d6709762fa722beea544002a4e7a44761b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Thu, 9 Apr 2026 15:33:19 +0200 Subject: [PATCH 05/15] clang-tidy.yml: run clang-tidy with C++23 (#635) --- .github/workflows/clang-tidy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index b2b32b7d..333672d7 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -42,7 +42,7 @@ jobs: - name: Prepare CMake run: | - cmake -S . -B cmake.output -Werror=dev --warn-uninitialized -DCMAKE_COMPILE_WARNING_AS_ERROR=On -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + cmake -S . -B cmake.output -Werror=dev --warn-uninitialized -DCMAKE_CXX_STANDARD=23 -DCMAKE_COMPILE_WARNING_AS_ERROR=On -DCMAKE_EXPORT_COMPILE_COMMANDS=ON env: CXX: clang-22 From 6521caaab5024148138c4e0954d5fc966dd11cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Thu, 9 Apr 2026 16:22:13 +0200 Subject: [PATCH 06/15] CI-mingw.yml: exclude `clang++` on `MINGW32` as it is no longer available (#644) --- .github/workflows/CI-mingw.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/CI-mingw.yml b/.github/workflows/CI-mingw.yml index a3bac5cf..390de83d 100644 --- a/.github/workflows/CI-mingw.yml +++ b/.github/workflows/CI-mingw.yml @@ -33,6 +33,9 @@ jobs: exclude: - msystem: CLANG64 compiler: g++ + # the mingw-w64-i686-clang package is no longer available + - msystem: MINGW32 + compiler: clang++ fail-fast: false runs-on: windows-2025 From 7360858b281db99cd5a7e6da6a0558ca6aa8d9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Thu, 9 Apr 2026 17:45:28 +0200 Subject: [PATCH 07/15] CI-windows.yml: updated Python to 3.14 (#642) --- .github/workflows/CI-windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI-windows.yml b/.github/workflows/CI-windows.yml index 521bc24c..cfced0d2 100644 --- a/.github/workflows/CI-windows.yml +++ b/.github/workflows/CI-windows.yml @@ -32,10 +32,10 @@ jobs: - name: Setup msbuild.exe uses: microsoft/setup-msbuild@v2 - - name: Set up Python 3.13 + - name: Set up Python uses: actions/setup-python@v6 with: - python-version: '3.13' + python-version: '3.14' check-latest: true - name: Install missing Python packages From 1d0c78039150157f91972b854f464347a4b9df71 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:25:08 +0200 Subject: [PATCH 08/15] Refs #638 Fix stylistic issues uncovered by cppcheck (#643) --- simplecpp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/simplecpp.h b/simplecpp.h index 54f6a90c..49aa600b 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -83,7 +83,7 @@ namespace simplecpp { , mSize(strlen(data)) {} - // only provide when std::span is not available so using untyped initilization won't use View + // only provide when std::span is not available so using untyped initialization won't use View #if !defined(__cpp_lib_span) View(const char* data, std::size_t size) : mData(data) @@ -376,7 +376,7 @@ namespace simplecpp { const std::string& file(const Location& loc) const; private: - TokenList(const unsigned char* data, std::size_t size, std::vector &filenames, const std::string &filename, OutputList *outputList, int unused); + TokenList(const unsigned char* data, std::size_t size, std::vector &filenames, const std::string &filename, OutputList *outputList, int /*unused*/); void combineOperators(); From 7e7a525ab1afb4fd7c16f357950ed1d1e213ff19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Fri, 10 Apr 2026 14:07:33 +0200 Subject: [PATCH 09/15] CI-unixish.yml: added `ubuntu-22.04-arm`, `ubuntu-24.04-arm` and `macos-26-intel` (#640) --- .github/workflows/CI-unixish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 6fd8a429..1a4ce649 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: - os: [ubuntu-22.04, ubuntu-24.04, macos-14, macos-15, macos-15-intel, macos-26] + os: [ubuntu-22.04, ubuntu-22.04-arm, ubuntu-24.04, ubuntu-24.04-arm, macos-14, macos-15, macos-15-intel, macos-26, macos-26-intel] compiler: [clang++] include: - os: ubuntu-22.04 From 4044f8fcdf175814668c17ea4a768a751ba1313c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sun, 12 Apr 2026 09:04:34 +0200 Subject: [PATCH 10/15] main.cpp: fixed handling of options without value (#645) --- main.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/main.cpp b/main.cpp index d67f94c2..5c48f830 100644 --- a/main.cpp +++ b/main.cpp @@ -86,7 +86,7 @@ int main(int argc, char **argv) break; } dui.includes.emplace_back(std::move(value)); - } else if (std::strncmp(arg, "-is",3)==0) { + } else if (std::strcmp(arg, "-is")==0) { found = true; use_istream = true; } @@ -104,20 +104,28 @@ int main(int argc, char **argv) } break; case 'q': - found = true; - quiet = true; + if (std::strcmp(arg, "-q")==0) { + found = true; + quiet = true; + } break; case 'e': - found = true; - error_only = true; + if (std::strcmp(arg, "-e")==0) { + found = true; + error_only = true; + } break; case 'f': - found = true; - fail_on_error = true; + if (std::strcmp(arg, "-f")==0) { + found = true; + fail_on_error = true; + } break; case 'l': - linenrs = true; - found = true; + if (std::strcmp(arg, "-l")==0) { + linenrs = true; + found = true; + } break; } if (!found) { From ac5cbe3408ad76c2c845c66ccbc2680e93a5c80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 13 Apr 2026 10:49:46 +0200 Subject: [PATCH 11/15] Fix #590 (Incorrect expansion of functional macros) (#646) --- simplecpp.cpp | 2 ++ simplecpp.h | 3 +++ test.cpp | 11 +++++++++++ 3 files changed, 16 insertions(+) diff --git a/simplecpp.cpp b/simplecpp.cpp index b59f773c..a0e8caa8 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -2204,6 +2204,8 @@ namespace simplecpp { } output.push_back(newMacroToken(tok->str(), loc, true, tok)); + if (it != macros.end()) + output.back()->markExpandedFrom(&it->second); return tok->next; } diff --git a/simplecpp.h b/simplecpp.h index 49aa600b..b744e62d 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -213,6 +213,9 @@ namespace simplecpp { bool isExpandedFrom(const Macro* m) const { return mExpandedFrom.find(m) != mExpandedFrom.end(); } + void markExpandedFrom(const Macro* m) { + mExpandedFrom.insert(m); + } void printAll() const; void printOut() const; diff --git a/test.cpp b/test.cpp index b0bf0b4f..bc8cb4d5 100644 --- a/test.cpp +++ b/test.cpp @@ -946,6 +946,16 @@ static void define_define_23() // #403 crash (infinite recursion) ASSERT_EQUALS("\n\n\n\nYdieZ ( void ) ;", preprocess(code)); } +static void define_define_24() // #590 +{ + const char code[] = "#define B A\n" + "#define A x(B)\n" + "#define C(s) s\n" + "#define D(s) C(s)\n" + "D(A)\n"; + ASSERT_EQUALS("\n\n\n\nx ( A )", preprocess(code)); +} + static void define_va_args_1() { const char code[] = "#define A(fmt...) dostuff(fmt)\n" @@ -3671,6 +3681,7 @@ int main(int argc, char **argv) TEST_CASE(define_define_21); TEST_CASE(define_define_22); // #400 TEST_CASE(define_define_23); // #403 - crash, infinite recursion + TEST_CASE(define_define_24); // #590 TEST_CASE(define_va_args_1); TEST_CASE(define_va_args_2); TEST_CASE(define_va_args_3); From ea9d3cbb19207e21e2ce4708b4c7076e11a8fd85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 16 Apr 2026 13:51:08 +0200 Subject: [PATCH 12/15] Refs #638: Avoid shadowing arguments (#639) --- simplecpp.cpp | 6 +++--- simplecpp.h | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/simplecpp.cpp b/simplecpp.cpp index a0e8caa8..a7ced05a 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -635,10 +635,10 @@ static bool isStringLiteralPrefix(const std::string &str) str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R"; } -void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location &location) +void simplecpp::TokenList::lineDirective(unsigned int fileIndex_, unsigned int line, Location &location) { - if (fileIndex != location.fileIndex || line >= location.line) { - location.fileIndex = fileIndex; + if (fileIndex_ != location.fileIndex || line >= location.line) { + location.fileIndex = fileIndex_; location.line = line; return; } diff --git a/simplecpp.h b/simplecpp.h index b744e62d..f29166ff 100644 --- a/simplecpp.h +++ b/simplecpp.h @@ -174,9 +174,9 @@ namespace simplecpp { bool isOneOf(const char ops[]) const; bool startsWithOneOf(const char c[]) const; bool endsWithOneOf(const char c[]) const; - static bool isNumberLike(const std::string& str) { - return std::isdigit(static_cast(str[0])) || - (str.size() > 1U && (str[0] == '-' || str[0] == '+') && std::isdigit(static_cast(str[1]))); + static bool isNumberLike(const std::string& s) { + return std::isdigit(static_cast(s[0])) || + (s.size() > 1U && (s[0] == '-' || s[0] == '+') && std::isdigit(static_cast(s[1]))); } TokenString macro; @@ -399,7 +399,7 @@ namespace simplecpp { void constFoldQuestionOp(Token *&tok1); std::string readUntil(Stream &stream, const Location &location, char start, char end, OutputList *outputList); - void lineDirective(unsigned int fileIndex, unsigned int line, Location &location); + void lineDirective(unsigned int fileIndex_, unsigned int line, Location &location); const Token* lastLineTok(int maxsize=1000) const; const Token* isLastLinePreprocessor(int maxsize=1000) const; From 316d4ee8e489ceaf1b9926df24d37b784d3655a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Fri, 17 Apr 2026 00:14:30 +0200 Subject: [PATCH 13/15] test.cpp: also run tests with char buffer (#261) --- test.cpp | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/test.cpp b/test.cpp index bc8cb4d5..b654e159 100644 --- a/test.cpp +++ b/test.cpp @@ -6,6 +6,7 @@ #include "simplecpp.h" #include +#include #include #include #include @@ -25,6 +26,15 @@ #define STRINGIZE(x) STRINGIZE_(x) static const std::string testSourceDir = SIMPLECPP_TEST_SOURCE_DIR; + +namespace { + enum class Input : std::uint8_t { + Stringstream, + CharBuffer + }; +} + +static Input USE_INPUT = Input::Stringstream; static int numberOfFailedAssertions = 0; #define ASSERT_EQUALS(expected, actual) (assertEquals((expected), (actual), __LINE__)) @@ -41,11 +51,21 @@ static std::string pprint(const std::string &in) return ret; } +static const char* inputString(Input input) { + switch (input) { + case Input::Stringstream: + return "Stringstream"; + case Input::CharBuffer: + return "CharBuffer"; + } + return ""; // unreachable - needed for GCC and Visual Studio +} + static int assertEquals(const std::string &expected, const std::string &actual, int line) { if (expected != actual) { numberOfFailedAssertions++; - std::cerr << "------ assertion failed ---------" << std::endl; + std::cerr << "------ assertion failed (" << inputString(USE_INPUT) << ")---------" << std::endl; std::cerr << "line test.cpp:" << line << std::endl; std::cerr << "expected:" << pprint(expected) << std::endl; std::cerr << "actual:" << pprint(actual) << std::endl; @@ -83,8 +103,16 @@ static void testcase(const std::string &name, void (*f)(), int argc, char * cons static simplecpp::TokenList makeTokenList(const char code[], std::size_t size, std::vector &filenames, const std::string &filename=std::string(), simplecpp::OutputList *outputList=nullptr) { - std::istringstream istr(std::string(code, size)); - return {istr,filenames,filename,outputList}; + switch (USE_INPUT) { + case Input::Stringstream: { + std::istringstream istr(std::string(code, size)); + return {istr,filenames,filename,outputList}; + } + case Input::CharBuffer: + return {{code, size}, filenames, filename, outputList}; + } + + return simplecpp::TokenList{filenames}; // unreachable - needed for GCC and Visual Studio } static simplecpp::TokenList makeTokenList(const char code[], std::vector &filenames, const std::string &filename=std::string(), simplecpp::OutputList *outputList=nullptr) @@ -3619,8 +3647,10 @@ static void leak() } } -int main(int argc, char **argv) +static void runTests(int argc, char **argv, Input input) { + USE_INPUT = input; + TEST_CASE(backslash); TEST_CASE(builtin); @@ -3892,6 +3922,11 @@ int main(int argc, char **argv) TEST_CASE(fuzz_crash); TEST_CASE(leak); +} +int main(int argc, char **argv) +{ + runTests(argc, argv, Input::Stringstream); + runTests(argc, argv, Input::CharBuffer); return numberOfFailedAssertions > 0 ? EXIT_FAILURE : EXIT_SUCCESS; } From 470196af7c753d3aee222c1534c63e5da869335e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Fri, 17 Apr 2026 23:27:32 +0200 Subject: [PATCH 14/15] CI-unixish.yml: added some missing `g++` cases to matrix (#649) --- .github/workflows/CI-unixish.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 1a4ce649..cdda088b 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -15,8 +15,12 @@ jobs: include: - os: ubuntu-22.04 compiler: g++ + - os: ubuntu-22.04-arm + compiler: g++ - os: ubuntu-24.04 compiler: g++ + - os: ubuntu-24.04-arm + compiler: g++ fail-fast: false runs-on: ${{ matrix.os }} From 24136a07c2ee0ef8a383a18483dbde20c4a8cf16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sun, 26 Apr 2026 03:00:53 +0200 Subject: [PATCH 15/15] added tests for various fixed issues (#648) --- test.cpp | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/test.cpp b/test.cpp index b654e159..cd6a4db5 100644 --- a/test.cpp +++ b/test.cpp @@ -719,6 +719,181 @@ static void define13() "}", preprocess(code)); } +static void define14() // #296 +{ + const char code[] = "#define bar(x) x % 2\n" + "#define foo(x) printf(#x \"\\n\")\n" + "\n" + " foo(bar(3));\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "printf ( \"bar(3)\" \"\\n\" ) ;", preprocess(code)); +} + +static void define15() // #231 +{ + const char code[] = "#define CAT(a, b) CAT2(a, b)\n" + "#define CAT2(a, b) a ## b\n" + "#define FOO x\n" + "#define BAR() CAT(F, OO)\n" + "#define BAZ CAT(B, AR)()\n" + "BAZ\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "\n" + "\n" + "x", preprocess(code)); +} + +static void define16() // #201 +{ + const char code[] = "#define ALL_COLORS(warm_colors) \\\n" + " X(Blue) \\\n" + " X(Green) \\\n" + " X(Purple) \\\n" + " warm_colors\n" + "\n" + "#define WARM_COLORS \\\n" + " X(Red) \\\n" + " X(Yellow) \\\n" + " X(Orange)\n" + "\n" + "#define COLOR_SET ALL_COLORS(WARM_COLORS)\n" + "\n" + "#define X(color) #color,\n" + "\n" + "COLOR_SET\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\"Blue\" , \"Green\" , \"Purple\" , \"Red\" , \"Yellow\" , \"Orange\" ,", preprocess(code)); +} + +static void define17() // #185 +{ + const char code[] = "#define at(x, y) x##y\n" + "#define b(...) \\\n" + "aa(__VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , \\\n" + ", , , , , , , , 2)\n" + "#define aa(c, d, a, b, e, f, g, h, ab, ac, i, ad, j, k, l, m, n, o, p, ae, q, \\\n" + "r, s, t, u, v, w, x, y, z, af, ag, ah, ai, aj, ak, al, am, an, ao, \\\n" + "ap) \\\n" + "ap\n" + "#define aq(...) ar(b(__VA_ARGS__), __VA_ARGS__) static_assert(true, \" \")\n" + "#define ar(ap, ...) at(I_, ap)(__VA_ARGS__)\n" + "#define I_2(as, a)\n" + "aq(a, array);\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "static_assert ( true , \" \" ) ;", preprocess(code)); +} + +static void define18() // #130 +{ + const char code[] = "#define MAC2STR(x) x[0],x[1],x[2],x[3],x[4],x[5]\n" + "#define FT_DEBUG(fmt, args...) if(pGlobalCtx && pGlobalCtx->debug_level>=2) printf(\"FT-dbg: \"fmt, ##args)\n" + "\n" + "FT_DEBUG(\" %02x:%02x:%02x:%02x:%02x:%02x\\n\", MAC2STR(pCtx->wlan_intf_addr[i]));\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "if ( pGlobalCtx && pGlobalCtx -> debug_level >= 2 ) printf ( \"FT-dbg: \" \" %02x:%02x:%02x:%02x:%02x:%02x\\n\" , pCtx -> wlan_intf_addr [ i ] [ 0 ] , pCtx -> wlan_intf_addr [ i ] [ 1 ] , pCtx -> wlan_intf_addr [ i ] [ 2 ] , pCtx -> wlan_intf_addr [ i ] [ 3 ] , pCtx -> wlan_intf_addr [ i ] [ 4 ] , pCtx -> wlan_intf_addr [ i ] [ 5 ] ) ;", preprocess(code)); +} + +static void define19() // #124 +{ + const char code[] = "#define CONCAT(tok) tok##suffix\n" + "\n" + "CONCAT(Test);\n" + "CONCAT(const Test);\n"; + ASSERT_EQUALS("\n" + "\n" + "Testsuffix ;\n" + "const Testsuffix ;", preprocess(code)); +} + +static void define20() // #113 +{ + const char code[] = "#define TARGS4 T1,T2,T3,T4\n" + "#define FOOIMPL(T__CLASS, TARGS) void foo(const T__CLASS& x) { }\n" + "#define FOOIMPL_4(T__CLASS) FOOIMPL(T__CLASS, TARGS4)\n" + "FOOIMPL_4(y)\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "void foo ( const y < T1 , T2 , T3 , T4 > & x ) { }", preprocess(code)); +} + +static void define21() // #66 +{ + const char code[] = "#define GETMYID(a) ((a))+1\n" + "#define FIGHT_FOO(c, ...) foo(c, ##__VA_ARGS__)\n" + "FIGHT_FOO(1, GETMYID(a));\n"; + ASSERT_EQUALS("\n" + "\n" + "foo ( 1 , ( ( a ) ) + 1 ) ;", preprocess(code)); +} + +static void define22() // #40 +{ + const char code[] = "#define COUNTER_NAME(NAME, ...) NAME##Count\n" + "#define COMMA ,\n" + "\n" + "#define DECLARE_COUNTERS(LIST) unsigned long LIST(COUNTER_NAME, COMMA);\n" + "\n" + "#define ACTUAL_LIST(FUNCTION, SEPARATOR) \\\n" + "FUNCTION(event1, int, foo) SEPARATOR \\\n" + "FUNCTION(event2, char, bar)\n" + "\n" + "DECLARE_COUNTERS(ACTUAL_LIST)\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "unsigned long event1Count , event2Count ;", preprocess(code)); +} + +static void define23() // #40 +{ + const char code[] = "#define COMMA ,\n" + "#define MULTI(SEPARATOR) A SEPARATOR B\n" + "\n" + "#define VARS MULTI(COMMA)\n" + "unsigned VARS;\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "\n" + "unsigned A , B ;", preprocess(code)); +} static void define_invalid_1() @@ -1181,6 +1356,15 @@ static void pragma_backslash() ASSERT_EQUALS("", preprocess(code, &outputList)); } +static void pragma_backslash_2() // #217 +{ + const char code[] = "#pragma comment(linker, \"foo \\\n" + "bar\")\n"; + + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); +} + static void dollar() { ASSERT_EQUALS("$ab", readfile("$ab")); @@ -3685,6 +3869,16 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(define11); TEST_CASE(define12); TEST_CASE(define13); + TEST_CASE(define14); // #296 + TEST_CASE(define15); // #231 + TEST_CASE(define16); // #201 + TEST_CASE(define17); // #185 + TEST_CASE(define18); // #130 + TEST_CASE(define19); // #124 + TEST_CASE(define20); // #113 + TEST_CASE(define21); // #66 + TEST_CASE(define22); // #40 + TEST_CASE(define23); // #40 TEST_CASE(define_invalid_1); TEST_CASE(define_invalid_2); TEST_CASE(define_define_1); @@ -3727,6 +3921,7 @@ static void runTests(int argc, char **argv, Input input) TEST_CASE(define_va_opt_9); // #632 TEST_CASE(pragma_backslash); // multiline pragma directive + TEST_CASE(pragma_backslash_2); // #217 // UB: #ifdef as macro parameter TEST_CASE(define_ifdef);