diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 9a4f43c9..a2f7b6dc 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -30,7 +30,7 @@ jobs: - name: Prepare CMake run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DDISABLE_CPP03_SYNTAX_CHECK=ON + cmake -S . -B cmake.output -G "Unix Makefiles" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON env: CXX: clang-20 diff --git a/CMakeLists.txt b/CMakeLists.txt index 672e63bb..6ab0166e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required (VERSION 3.10) project (simplecpp LANGUAGES CXX) -option(DISABLE_CPP03_SYNTAX_CHECK "Disable the C++03 syntax check." OFF) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) include(CheckCXXCompilerFlag) @@ -70,25 +71,7 @@ endif() add_library(simplecpp_obj OBJECT simplecpp.cpp) add_executable(simplecpp $ main.cpp) -set_property(TARGET simplecpp PROPERTY CXX_STANDARD 11) - -if (NOT DISABLE_CPP03_SYNTAX_CHECK) - # it is not possible to set a standard older than C++14 with Visual Studio - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # we need to create a dummy library as -fsyntax-only will not produce any output files causing the build to fail - add_library(simplecpp-03-syntax OBJECT simplecpp.cpp) - target_compile_options(simplecpp-03-syntax PRIVATE -std=c++03) - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") - target_compile_options(simplecpp-03-syntax PRIVATE -Wno-long-long) - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options(simplecpp-03-syntax PRIVATE -Wno-c++11-long-long -Wno-c++11-compat) - endif() - add_dependencies(simplecpp simplecpp-03-syntax) - endif() -endif() - add_executable(testrunner $ test.cpp) -set_property(TARGET testrunner PROPERTY CXX_STANDARD 11) enable_testing() add_test(NAME testrunner COMMAND testrunner) diff --git a/Makefile b/Makefile index db1ca257..73977517 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ all: testrunner simplecpp -CXXFLAGS = -Wall -Wextra -pedantic -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wredundant-decls -Wundef -Wno-multichar -Wold-style-cast -std=c++0x -g +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 LDFLAGS = -g %.o: %.cpp simplecpp.h @@ -11,8 +11,6 @@ testrunner: test.o simplecpp.o $(CXX) $(LDFLAGS) simplecpp.o test.o -o testrunner test: testrunner simplecpp - # The -std=c++03 makes sure that simplecpp.cpp is C++03 conformant. We don't require a C++11 compiler - g++ -std=c++03 -fsyntax-only simplecpp.cpp ./testrunner python3 run-tests.py diff --git a/simplecpp.cpp b/simplecpp.cpp index 599ffdfe..bccb9b1b 100755 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -31,12 +31,10 @@ #include #include #include -#if __cplusplus >= 201103L #ifdef SIMPLECPP_WINDOWS #include #endif #include -#endif #include #include @@ -51,18 +49,6 @@ #undef ERROR #endif -#if __cplusplus >= 201103L -#define OVERRIDE override -#define EXPLICIT explicit -#else -#define OVERRIDE -#define EXPLICIT -#endif - -#if (__cplusplus < 201103L) && !defined(__APPLE__) -#define nullptr NULL -#endif - static bool isHex(const std::string &s) { return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0); @@ -368,22 +354,22 @@ class simplecpp::TokenList::Stream { class StdIStream : public simplecpp::TokenList::Stream { public: // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members - EXPLICIT StdIStream(std::istream &istr) + explicit StdIStream(std::istream &istr) : istr(istr) { assert(istr.good()); init(); } - virtual int get() OVERRIDE { + virtual int get() override { return istr.get(); } - virtual int peek() OVERRIDE { + virtual int peek() override { return istr.peek(); } - virtual void unget() OVERRIDE { + virtual void unget() override { istr.unget(); } - virtual bool good() OVERRIDE { + virtual bool good() override { return istr.good(); } @@ -402,20 +388,20 @@ class StdCharBufStream : public simplecpp::TokenList::Stream { init(); } - virtual int get() OVERRIDE { + virtual int get() override { if (pos >= size) return lastStatus = EOF; return str[pos++]; } - virtual int peek() OVERRIDE { + virtual int peek() override { if (pos >= size) return lastStatus = EOF; return str[pos]; } - virtual void unget() OVERRIDE { + virtual void unget() override { --pos; } - virtual bool good() OVERRIDE { + virtual bool good() override { return lastStatus != EOF; } @@ -429,7 +415,7 @@ class StdCharBufStream : public simplecpp::TokenList::Stream { class FileStream : public simplecpp::TokenList::Stream { public: // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members - EXPLICIT FileStream(const std::string &filename, std::vector &files) + explicit FileStream(const std::string &filename, std::vector &files) : file(fopen(filename.c_str(), "rb")) , lastCh(0) , lastStatus(0) { @@ -440,25 +426,25 @@ class FileStream : public simplecpp::TokenList::Stream { init(); } - ~FileStream() OVERRIDE { + ~FileStream() override { fclose(file); file = nullptr; } - virtual int get() OVERRIDE { + virtual int get() override { lastStatus = lastCh = fgetc(file); return lastCh; } - virtual int peek() OVERRIDE{ + virtual int peek() override{ // keep lastCh intact const int ch = fgetc(file); unget_internal(ch); return ch; } - virtual void unget() OVERRIDE { + virtual void unget() override { unget_internal(lastCh); } - virtual bool good() OVERRIDE { + virtual bool good() override { return lastStatus != EOF; } @@ -519,12 +505,10 @@ simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(nullptr), b *this = other; } -#if __cplusplus >= 201103L simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(nullptr), backToken(nullptr), files(other.files) { *this = std::move(other); } -#endif simplecpp::TokenList::~TokenList() { @@ -543,7 +527,6 @@ simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other) return *this; } -#if __cplusplus >= 201103L simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other) { if (this != &other) { @@ -557,7 +540,6 @@ simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other) } return *this; } -#endif void simplecpp::TokenList::clear() { @@ -1477,15 +1459,11 @@ unsigned int simplecpp::TokenList::fileIndex(const std::string &filename) namespace simplecpp { class Macro; -#if __cplusplus >= 201103L using MacroMap = std::unordered_map; -#else - typedef std::map MacroMap; -#endif class Macro { public: - explicit Macro(std::vector &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), valueDefinedInCode_(false) {} + 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) {} Macro(const Token *tok, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(true) { if (sameline(tok->previousSkipComments(), tok)) @@ -1515,6 +1493,11 @@ namespace simplecpp { *this = other; } + ~Macro() { + delete optExpandValue; + delete optNoExpandValue; + } + Macro &operator=(const Macro &other) { if (this != &other) { files = other.files; @@ -1707,6 +1690,9 @@ namespace simplecpp { bool parseDefine(const Token *nametoken) { nameTokDef = nametoken; variadic = false; + variadicOpt = false; + optExpandValue = nullptr; + optNoExpandValue = nullptr; if (!nameTokDef) { valueToken = endToken = nullptr; args.clear(); @@ -1744,8 +1730,49 @@ namespace simplecpp { if (!sameline(valueToken, nameTokDef)) valueToken = nullptr; endToken = valueToken; - while (sameline(endToken, nameTokDef)) + while (sameline(endToken, nameTokDef)) { + if (variadic && endToken->str() == "__VA_OPT__") + variadicOpt = true; endToken = endToken->next; + } + + if (variadicOpt) { + TokenList expandValue(files); + TokenList noExpandValue(files); + for (const Token *tok = valueToken; tok && tok != endToken;) { + if (tok->str() == "__VA_OPT__") { + if (!sameline(tok, tok->next) || tok->next->op != '(') + throw Error(tok->location, "In definition of '" + nameTokDef->str() + "': Missing opening parenthesis for __VA_OPT__"); + tok = tok->next->next; + int par = 1; + while (tok && tok != endToken) { + if (tok->op == '(') + par++; + else if (tok->op == ')') + par--; + else if (tok->str() == "__VA_OPT__") + throw Error(tok->location, "In definition of '" + nameTokDef->str() + "': __VA_OPT__ cannot be nested"); + if (par == 0) { + tok = tok->next; + break; + } + expandValue.push_back(new Token(*tok)); + tok = tok->next; + } + if (par != 0) { + const Token *const lastTok = expandValue.back() ? expandValue.back() : valueToken->next; + throw Error(lastTok->location, "In definition of '" + nameTokDef->str() + "': Missing closing parenthesis for __VA_OPT__"); + } + } else { + expandValue.push_back(new Token(*tok)); + noExpandValue.push_back(new Token(*tok)); + tok = tok->next; + } + } + optExpandValue = new TokenList(std::move(expandValue)); + optNoExpandValue = new TokenList(std::move(noExpandValue)); + } + return true; } @@ -1900,8 +1927,22 @@ namespace simplecpp { Token * const output_end_1 = output->back(); + const Token *valueToken2; + const Token *endToken2; + + if (variadicOpt) { + if (parametertokens2.size() > args.size() && parametertokens2[args.size() - 1]->next->op != ')') + valueToken2 = optExpandValue->cfront(); + else + valueToken2 = optNoExpandValue->cfront(); + endToken2 = nullptr; + } else { + valueToken2 = valueToken; + endToken2 = endToken; + } + // expand - for (const Token *tok = valueToken; tok != endToken;) { + for (const Token *tok = valueToken2; tok != endToken2;) { if (tok->op != '#') { // A##B => AB if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') { @@ -1950,7 +1991,7 @@ namespace simplecpp { } tok = tok->next; - if (tok == endToken) { + if (tok == endToken2) { output->push_back(new Token(*tok->previous)); break; } @@ -2020,24 +2061,6 @@ namespace simplecpp { // Macro parameter.. { TokenList temp(files); - if (tok->str() == "__VA_OPT__") { - if (sameline(tok, tok->next) && tok->next->str() == "(") { - tok = tok->next; - int paren = 1; - while (sameline(tok, tok->next)) { - if (tok->next->str() == "(") - ++paren; - else if (tok->next->str() == ")") - --paren; - if (paren == 0) - return tok->next->next; - tok = tok->next; - if (parametertokens.size() > args.size() && parametertokens.front()->next->str() != ")") - tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens)->previous; - } - } - throw Error(tok->location, "Missing parenthesis for __VA_OPT__(content)"); - } if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) { if (tok->str() == "__VA_ARGS__" && temp.empty() && output->cback() && output->cback()->str() == "," && tok->nextSkipComments() && tok->nextSkipComments()->str() == ")") @@ -2338,6 +2361,13 @@ namespace simplecpp { /** is macro variadic? */ bool variadic; + /** does the macro expansion have __VA_OPT__? */ + bool variadicOpt; + + /** Expansion value for varadic macros with __VA_OPT__ expanded and discarded respectively */ + const TokenList *optExpandValue; + const TokenList *optNoExpandValue; + /** was the value of this macro actually defined in the code? */ bool valueDefinedInCode_; }; @@ -2380,47 +2410,9 @@ namespace simplecpp { #ifdef SIMPLECPP_WINDOWS -#if __cplusplus >= 201103L using MyMutex = std::mutex; template using MyLock = std::lock_guard; -#else -class MyMutex { -public: - MyMutex() { - InitializeCriticalSection(&m_criticalSection); - } - - ~MyMutex() { - DeleteCriticalSection(&m_criticalSection); - } - - CRITICAL_SECTION* lock() { - return &m_criticalSection; - } -private: - CRITICAL_SECTION m_criticalSection; -}; - -template -class MyLock { -public: - explicit MyLock(T& m) - : m_mutex(m) { - EnterCriticalSection(m_mutex.lock()); - } - - ~MyLock() { - LeaveCriticalSection(m_mutex.lock()); - } - -private: - MyLock& operator=(const MyLock&); - MyLock(const MyLock&); - - T& m_mutex; -}; -#endif class RealFileNameMap { public: @@ -2681,13 +2673,16 @@ static void simplifySizeof(simplecpp::TokenList &expr, const std::map= "201703L"); } +static bool isGnu(const simplecpp::DUI &dui) +{ + return dui.std.rfind("gnu", 0) != std::string::npos; +} static std::string currentDirectoryOSCalc() { const std::size_t size = 4096; @@ -2760,7 +2755,7 @@ static std::string extractRelativePathFromAbsolute(const std::string& absoluteSi static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader); static void simplifyHasInclude(simplecpp::TokenList &expr, const simplecpp::DUI &dui) { - if (!isCpp17OrLater(dui)) + if (!isCpp17OrLater(dui) && !isGnu(dui)) return; for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { @@ -3483,13 +3478,16 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL // use a dummy vector for the macros because as this is not part of the file and would add an empty entry - e.g. /usr/include/poll.h std::vector dummy; - const bool hasInclude = isCpp17OrLater(dui); + 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) { const std::string ¯ostr = *it; const std::string::size_type eq = macrostr.find('='); const std::string::size_type par = macrostr.find('('); const std::string macroname = macrostr.substr(0, std::min(eq,par)); + if (macroname == "__STRICT_ANSI__") + strictAnsiDefined = true; if (dui.undefined.find(macroname) != dui.undefined.end()) continue; const std::string lhs(macrostr.substr(0,eq)); @@ -3498,6 +3496,10 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL macros.insert(std::pair(macro.name(), macro)); } + const bool strictAnsiUndefined = dui.undefined.find("__STRICT_ANSI__") != dui.undefined.cend(); + if (!isGnu(dui) && !strictAnsiDefined && !strictAnsiUndefined) + macros.insert(std::pair("__STRICT_ANSI__", Macro("__STRICT_ANSI__", "1", dummy))); + macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", dummy))); macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", dummy))); macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", dummy))); @@ -3621,6 +3623,16 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL } output.clear(); return; + } catch (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); + } + output.clear(); + return; } } else if (ifstates.top() == True && rawtok->str() == INCLUDE) { TokenList inc1(files); @@ -4032,7 +4044,3 @@ std::string simplecpp::getCppStdString(const std::string &std) { return getCppStdString(getCppStd(std)); } - -#if (__cplusplus < 201103L) && !defined(__APPLE__) -#undef nullptr -#endif diff --git a/simplecpp.h b/simplecpp.h index f5c69593..9fd95808 100755 --- a/simplecpp.h +++ b/simplecpp.h @@ -27,10 +27,6 @@ # define SIMPLECPP_LIB #endif -#if (__cplusplus < 201103L) && !defined(__APPLE__) -#define nullptr NULL -#endif - #if defined(_MSC_VER) # pragma warning(push) // suppress warnings about "conversion from 'type1' to 'type2', possible loss of data" @@ -214,14 +210,10 @@ namespace simplecpp { /** generates a token list from the given filename parameter */ TokenList(const std::string &filename, std::vector &filenames, OutputList *outputList = nullptr); TokenList(const TokenList &other); -#if __cplusplus >= 201103L TokenList(TokenList &&other); -#endif ~TokenList(); TokenList &operator=(const TokenList &other); -#if __cplusplus >= 201103L TokenList &operator=(TokenList &&other); -#endif void clear(); bool empty() const { @@ -395,8 +387,4 @@ namespace simplecpp { # pragma warning(pop) #endif -#if (__cplusplus < 201103L) && !defined(__APPLE__) -#undef nullptr -#endif - #endif diff --git a/test.cpp b/test.cpp index cec253b8..6070c9e0 100644 --- a/test.cpp +++ b/test.cpp @@ -923,7 +923,7 @@ static void define_va_opt_3() simplecpp::OutputList outputList; ASSERT_EQUALS("", preprocess(code1, &outputList)); - ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n", + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n", toString(outputList)); outputList.clear(); @@ -934,7 +934,7 @@ static void define_va_opt_3() "err()"; ASSERT_EQUALS("", preprocess(code2, &outputList)); - ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n", + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n", toString(outputList)); } @@ -946,7 +946,7 @@ static void define_va_opt_4() simplecpp::OutputList outputList; ASSERT_EQUALS("", preprocess(code1, &outputList)); - ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n", + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n", toString(outputList)); outputList.clear(); @@ -956,7 +956,7 @@ static void define_va_opt_4() "err()"; ASSERT_EQUALS("", preprocess(code2, &outputList)); - ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n", + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n", toString(outputList)); } @@ -968,7 +968,46 @@ static void define_va_opt_5() simplecpp::OutputList outputList; ASSERT_EQUALS("", preprocess(code, &outputList)); - ASSERT_EQUALS("file0,1,syntax_error,failed to expand 'err', Missing parenthesis for __VA_OPT__(content)\n", + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n", + toString(outputList)); +} + +static void define_va_opt_6() +{ + // nested __VA_OPT__ + const char code[] = "#define err(...) __VA_OPT__(__VA_OPT__(something))\n" + "err()"; + + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code, &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': __VA_OPT__ cannot be nested\n", + toString(outputList)); +} + +static void define_va_opt_7() +{ + // eof in __VA_OPT__ + const char code1[] = "#define err(...) __VA_OPT__"; + + simplecpp::OutputList outputList; + ASSERT_EQUALS("", preprocess(code1, &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing opening parenthesis for __VA_OPT__\n", + toString(outputList)); + + outputList.clear(); + + const char code2[] = "#define err(...) __VA_OPT__("; + + ASSERT_EQUALS("", preprocess(code2, &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n", + toString(outputList)); + + outputList.clear(); + + const char code3[] = "#define err(...) __VA_OPT__(x"; + + ASSERT_EQUALS("", preprocess(code3, &outputList)); + ASSERT_EQUALS("file0,1,syntax_error,Failed to parse #define, In definition of 'err': Missing closing parenthesis for __VA_OPT__\n", toString(outputList)); } @@ -1563,6 +1602,63 @@ static void has_include_5() ASSERT_EQUALS("", preprocess(code)); } +static void has_include_6() +{ + const char code[] = "#if defined( __has_include)\n" + " #if !__has_include()\n" + " A\n" + " #else\n" + " B\n" + " #endif\n" + "#endif"; + simplecpp::DUI dui; + dui.std = "gnu99"; + ASSERT_EQUALS("\n\nA", preprocess(code, dui)); + ASSERT_EQUALS("", preprocess(code)); +} + +static void strict_ansi_1() +{ + const char code[] = "#if __STRICT_ANSI__\n" + " A\n" + "#endif"; + simplecpp::DUI dui; + dui.std = "gnu99"; + ASSERT_EQUALS("", preprocess(code, dui)); +} + +static void strict_ansi_2() +{ + const char code[] = "#if __STRICT_ANSI__\n" + " A\n" + "#endif"; + simplecpp::DUI dui; + dui.std = "c99"; + ASSERT_EQUALS("\nA", preprocess(code, dui)); +} + +static void strict_ansi_3() +{ + const char code[] = "#if __STRICT_ANSI__\n" + " A\n" + "#endif"; + simplecpp::DUI dui; + dui.std = "c99"; + dui.undefined.insert("__STRICT_ANSI__"); + ASSERT_EQUALS("", preprocess(code, dui)); +} + +static void strict_ansi_4() +{ + const char code[] = "#if __STRICT_ANSI__\n" + " A\n" + "#endif"; + simplecpp::DUI dui; + dui.std = "gnu99"; + dui.defines.push_back("__STRICT_ANSI__"); + ASSERT_EQUALS("\nA", preprocess(code, dui)); +} + static void ifdef1() { const char code[] = "#ifdef A\n" @@ -3063,6 +3159,8 @@ int main(int argc, char **argv) TEST_CASE(define_va_opt_3); TEST_CASE(define_va_opt_4); TEST_CASE(define_va_opt_5); + TEST_CASE(define_va_opt_6); + TEST_CASE(define_va_opt_7); TEST_CASE(pragma_backslash); // multiline pragma directive @@ -3123,6 +3221,12 @@ int main(int argc, char **argv) TEST_CASE(has_include_3); TEST_CASE(has_include_4); TEST_CASE(has_include_5); + TEST_CASE(has_include_6); + + TEST_CASE(strict_ansi_1); + TEST_CASE(strict_ansi_2); + TEST_CASE(strict_ansi_3); + TEST_CASE(strict_ansi_4); TEST_CASE(ifdef1); TEST_CASE(ifdef2); diff --git a/testsuite/clang-preprocessor-tests/macro_fn_va_opt.c b/testsuite/clang-preprocessor-tests/macro_fn_va_opt.c new file mode 100644 index 00000000..ccb09e95 --- /dev/null +++ b/testsuite/clang-preprocessor-tests/macro_fn_va_opt.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -E %s | grep '^ printf( "%%s" , "Hello" );$' + +#define P( x, ...) printf( x __VA_OPT__(,) __VA_ARGS__ ) +#define PF( x, ...) P( x __VA_OPT__(,) __VA_ARGS__ ) + +int main() +{ + PF( "%s", "Hello" ); + PF( "Hello", ); + PF( "Hello" ); + PF( , ); + PF( ); +}