Search code examples
ceclipse-cdt

Eclipse CDT syntax error for macro using _Generic keyword


I'm using Oxygen with CDT 9.3.0 built-in.

When I use a macro I defined that uses _Generic, all those macro uses are underlined with "syntax error", but the project compiles fine (which is setup to use my makefiles).

After reading a similar so question, and since _Generic begin from C11 possibly not supported by eclipse's code analysis, I tried defining a symbol for my macro definition to empty but it didn't work. (At project settings, C/C++ General->Paths and Symbols->Symbols Tab, GNU C, added symbol CONVERT(...) without a value and added a symbol CONVERT(X), and CONVERT() and CONVERT without a value).

For example my macro is:

#define FIRST_(_1, ...) _1
#define FIRST(...) FIRST_(__VA_ARGS__, _1, _2, _3)

#define CONVERT(...)                            \
                _Generic((FIRST(__VA_ARGS__)),  \
                    char*       : toText,   \
                    int         : toInt,    \
                    ) (__VA_ARGS__)

and usage point, that gives the syntax error:

void* test = CONVERT("testme");

Solution

  • As @ErikW pointed out, _Generic is a C11 feature that Eclipse CDT's parser does not support yet. This bug tracks adding support for it.

    (By the way, contributions to Eclipse CDT's C11 support are very welcome!)


    It is possible to work around this using macros.

    The problem with trying to define another version of the CONVERT(...) macro in "Paths and Symbols" is that the macros defined there are treated as if you wrote them at the very top of your file. A subsequent redefinition in your actual code overwrites the definition from "Paths and Symbols".

    I can think of two approaches to go about this:


    Approach 1

    CDT defines a special macro __CDT_PARSER__ which evaluates to true when it's parsing the code, but false when the code is actually compiled.

    You can take advantage of this to define a different version of CONVERT(...) for CDT's purposes:

    #ifdef __CDT_PARSER__
        #define CONVERT(...)
    #else
        #define CONVERT(...)                            \
                        _Generic((FIRST(__VA_ARGS__)),  \
                            char*       : toText,   \
                            int         : toInt,    \
                            ) (__VA_ARGS__)
    #endif
    

    This almost works, but not quite. We still get a syntax error, because this line:

    void* test = CONVERT("testme", 42);
    

    will now expand to:

    void* test = ;
    

    As you can see, we don't actually want an empty expansion for CONVERT(...). We want an expansion that will parse as a variable's initializer. 0 will work:

    #ifdef __CDT_PARSER__
        #define CONVERT(...) 0
    #else
        ...
    #endif
    

    Approach 2

    Instead of defining a different version of CONVERT(...), we could define _Generic(...) itself to be a macro for CDT's purposes.

    This time, we can do it in "Paths and Symbols", because there is no redefinition of _Generic(...) in the code that would mess it up.

    So let's define a symbol in "Paths and Symbols", with _Generic(...) as the name and an empty value.

    Now, this line:

    void* test = CONVERT("testme", 42);
    

    will expand to:

    void* test = _Generic((FIRST("testme", 42)),  \
                        char*       : toText,   \
                        int         : toInt,    \
                        ) ("testme", 42)
    

    which will in turn expand to:

    void* test = ("testme", 42);
    

    which parses (("testme", 42) parses as a parenthesized comma-expression and is thus a valid initializer).

    This approach has the advantage that you don't need to modify your actual code, and that it handles all uses of the _Generic macro rather than just the one in CONVERT.

    On the other hand, it's possible that for some other uses of the _Generic macro, this particular expansion won't parse. If that's the case, you might be able to come up with a different expansion that will parse for all uses, or else you can go with Approach 1.