Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

failed to expand BOOST_MPL_AUX_NA_SPEC, Wrong number of parameters for macro BOOST_PP_TUPLE_ELEM #159

Open
gdsotirov opened this issue Mar 14, 2019 · 23 comments

Comments

@gdsotirov
Copy link

I wasn't sure whether to report this here or in cppcheck project, so please excuse me if I chose the wrong place.

I stumbled upon the following problem when running cppcheck 1.87 on a C++ source using Boost libraries:

[/usr/include/boost/preprocessor/arithmetic/sub.hpp:47]: (error) failed to expand 'BOOST_MPL_AUX_NA_SPEC', Wrong number of parameters for macro 'BOOST_PP_TUPLE_ELEM'.

I checked this post in the forum, but the error message is not exactly the same. I guess it changed after the fix of #125, but issue #126 is still open and I think #108 is also related to the problem, because the problematic Boost header include/boost/preprocessor/arithmetic/sub.hpp uses ## operator.

Can I somehow workaround the problem until it's fixed in a new cppcheck version?

@danmar
Copy link
Owner

danmar commented Mar 14, 2019

I wasn't sure whether to report this here or in cppcheck project, so please excuse me if I chose the wrong place.

I don't know. If the "cppcheck -E" output looks wrong then it should be reported here. We can keep the issue here for now.

Can I somehow workaround the problem until it's fixed in a new cppcheck version?

Yes certainly.

In one project there is a workaround for boost macro problems. The project has this define:

#define VARS_TO_NAMES_AND_VARS(...) \
BOOST_PP_SEQ_FOR_EACH_I(VAR_TO_STRING_COMMA_VAR, % %, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

Cppcheck does not handle this code well.

To workaround this the code was updated to:

#ifndef __cppcheck__
/// @note Cppcheck does not like the syntax `% %` used here and reports a syntax error if that is seen in analysis.
/// Maybe someday Cppcheck will have a boost configuration that we can use instead of this ifndef
#define VARS_TO_NAMES_AND_VARS(...) \
    BOOST_PP_SEQ_FOR_EACH_I(VAR_TO_STRING_COMMA_VAR, % %, 
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
#endif

The preprocessor output is correct as far as I have seen but the % % operator usage is very odd and cppcheck does not like that! I think boost uses some magic here to make such weird syntax possible.

A -D__cppcheck__ is added on the cppcheck command line to activate this workaround.

So what will happen now after this workaround... whenever the code uses the macro VARS_TO_NAMES_AND_VARS then it can't be expanded. This is no big deal.

@gdsotirov
Copy link
Author

gdsotirov commented Mar 14, 2019

I meantime checked the manual and there (in section Include paths) it's written that "Actually, it is recommended to not give all include paths." and also "... passing standard library headers is highly discouraged because it will result in worse results and longer checking time.", so I'll proceed by passing only project's include paths to cppcheck, instead of all include paths like I was doing before. The issue could be closed.

@danmar
Copy link
Owner

danmar commented Mar 15, 2019

so I'll proceed by passing only project's include path to cppcheck

good!

@danmar danmar closed this as completed Mar 15, 2019
@Ken-Patrick
Copy link
Contributor

Hi,

I'm having this same issue when including boost headers. For some reason, we ended up having all of our header in the same directory, so now cppcheck finds boost headers and we have the above error (and more I think).

Is there a way to tell cppcheck to not use headers in headers_directory/boost ? Or can part of it be overridden via configuration ?

The only thing I can think of is -D<BOOST_INCLUDES_GUARDS> for all the boost files (that we are using), but it sounds ugly.

Any idea ?

@danmar
Copy link
Owner

danmar commented Jun 27, 2019

This preprocessor does not have any option to skip includes.

I am not sure what you should do.

You could probably generate the -D<BOOST_INCLUDES_GUARDS> with a script. I would put them in a file that you include with -include=...

Unless you have lots of files, this might be an option:

cp -R headers_directory temp
rm -rf temp/boost
cppcheck -I temp ...

Maybe adding some option to cppcheck would be ok also. Although I never got this feature request before so others seem to solve it somehow. I like to get inspiration from GCC so do you know if you can skip certain headers in GCC?

@Ken-Patrick
Copy link
Contributor

Using -include=... is be a good idea, thanks

Unless you have lots of files, this might be an option: [skip]

We do, it might still be an option, thanks !

Maybe adding some option to cppcheck would be ok also. Although I never got this feature request before so others seem to solve it somehow. I like to get inspiration from GCC so do you know if you can skip certain headers in GCC?

To my knowledge, GCC does not support this "feature" (which, TBH, would be a bit weird for a compiler, if you don't want the include, don't pass -I...), the only thing a bit close is -iquote (which allow to pass some directory that GCC will search first.
Thinking about it, I can also do something like this: replicate the boost tree, but with empty files, and give this directory first to cppcheck.

Is it worth trying to fix cppcheck so that I can understand the boost headers ? I have the feeling the it would be painful and that the answer is no.

We'll probably go for one of the hack we discussed.

@danmar
Copy link
Owner

danmar commented Jun 28, 2019

Is it worth trying to fix cppcheck so that I can understand the boost headers ? I have the feeling the it would be painful and that the answer is no.

That really sounds like a good solution. This is how I would do it personally.. I just thought from a "user" perspective before.

If this is caused by a problem in simplecpp or cppcheck I think it should be fixed.

@Ken-Patrick
Copy link
Contributor

Ok. I might give it a try (and I'll probably die in the middle because of all the preprocessing stuff that I won't understand :p).

@Ken-Patrick
Copy link
Contributor

I've found something:
Inside expandHashHash, (or expandArg ?), in some cases a '(' is appended to a token name, for instance strAB becomes for instance "BOOST_PP_IIF_I(" (there are many of them, and I don't have a small test case, so I don't know which one exactly causes the pb. Any way, they all seem wrong).
I guess this happens when simplecpp is trying to expand ##( or something that was expanded to ##(.

Then, getMacroParameters is confused and in
https://github.com/boostorg/preprocessor/blob/develop/include/boost/preprocessor/arithmetic/sub.hpp#L47

# define BOOST_PP_SUB_D_I(d, x, y) BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_WHILE_ ## d(BOOST_PP_SUB_P, BOOST_PP_SUB_O, (x, y)))

it believes BOOST_PP_TUPLE_ELEM is called with 6 parameters, hence the error.

The following fixes the issue. I've close to no idea what I am doing here, so it's probably not the right fix. I hope it gives you enough insight so that we can work out the right fix.

--- a/simplecpp.cpp
+++ b/simplecpp.cpp
@@ -1719,6 +1719,9 @@ namespace simplecpp {
             if (variadic && argnr + 1U >= parametertokens.size())
                 return true;
 
+            if (parametertokens[argnr]->next->op == '(')
+                return false;
+
             for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next)
                 output->push_back(new Token(*partok));
 

@danmar
Copy link
Owner

danmar commented Jun 29, 2019

hmm.. that is interesting. The fix does not look wrong but I think I want to investigate this also. A short code example that reproduce the problem would be awesome!

@danmar
Copy link
Owner

danmar commented Jun 29, 2019

can I reproduce somehow? Is it enough to include some boost header and write some code?

@Ken-Patrick
Copy link
Contributor

I believe including #include <boost/foreach.hpp> or boost/config.hpp is enough (I have not really tested though /o).

Then there are other issues, in https://www.boost.org/doc/libs/1_52_0/boost/type_traits/detail/cv_traits_impl.hpp
the preprocessing of

#if !(BOOST_WORKAROUND(__GNUC__,== 3) && BOOST_WORKAROUND(__GNUC_MINOR__, <= 2))

goes wrong, then the evaluation also, and it defaults to 0.
Then in the same file, same thing with

#if BOOST_WORKAROUND(__GNUC__,== 3) && BOOST_WORKAROUND(__GNUC_MINOR__, <= 2)

and we end up with an extra '{', and the parsing fails with an error.

Then, some boost files (at least cstdint.hpp) expects some constants defined b standard headers (things like UCHAR_MAX, UINT_MAX, etc).

I stopped there and will make cppcheck ignore these files (either playing with -DBOOST... and --include, or removing the file).

It might even make sense to ignore boost in simplecpp:

--- a/simplecpp.cpp
+++ b/simplecpp.cpp
@@ -2440,6 +2443,9 @@ std::map<std::string, simplecpp::TokenList*> simplecpp::load(const simplecpp::To
         if (hasFile(ret, sourcefile, header, dui, systemheader))
             continue;

+        if (header.rfind("boost/", 0) == 0)
+            continue;
+
         std::ifstream f;
         const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader);
         if (!f.is_open())

@danmar
Copy link
Owner

danmar commented Jun 30, 2019

I believe including #include <boost/foreach.hpp> or boost/config.hpp is enough (I have not really tested though /o).

ok.. when I include foreach.hpp I get this error message from simplecpp:

../include/boost_1_69_0/boost/preprocessor/logical/bool.hpp:24: syntax error: failed to expand 'BOOST_MPL_AUX_NA_SPEC', Wrong number of parameters for macro 'BOOST_PP_BOOL_OO'.

Sounds like your problem.

@danmar
Copy link
Owner

danmar commented Jul 1, 2019

Here is a reduced code:



# define BOOST_PP_CONFIG_STRICT() 0x0001
# define BOOST_PP_CONFIG_MSVC() 0x0004
# define BOOST_PP_CONFIG_MWCC() 0x0008
# define BOOST_PP_CONFIG_BCC() 0x0010
# define BOOST_PP_CONFIG_EDG() 0x0020
# define BOOST_PP_CONFIG_DMC() 0x0040
# define BOOST_PP_CONFIG_FLAGS() (BOOST_PP_CONFIG_STRICT())

#define BOOST_PP_CAT(a, b)        BOOST_PP_CAT_I(a, b)
#define BOOST_PP_CAT_I(a, b)      a ## b


# if ~BOOST_PP_CONFIG_FLAGS() & BOOST_PP_CONFIG_MWCC()
#    define BOOST_PP_IIF(bit, t, f) BOOST_PP_IIF_I(bit, t, f)
# else
#    define BOOST_PP_IIF(bit, t, f) BOOST_PP_IIF_OO((bit, t, f))
#    define BOOST_PP_IIF_OO(par) BOOST_PP_IIF_I ## par
# endif


# define BOOST_PP_AUTO_REC(pred, n)  BOOST_PP_NODE_ENTRY_ ## n(pred)
# define BOOST_PP_NODE_ENTRY_256(p)  BOOST_PP_NODE_128(p)(p)(p)(p)(p)(p)(p)(p)
# define BOOST_PP_NODE_ENTRY_128(p)  BOOST_PP_NODE_64(p)(p)(p)(p)(p)(p)(p)
# define BOOST_PP_NODE_128(p)        BOOST_PP_IIF(p(128), BOOST_PP_NODE_64, BOOST_PP_NODE_192)
# define BOOST_PP_NODE_64(p)         BOOST_PP_IIF(p(64), BOOST_PP_NODE_32, BOOST_PP_NODE_96)



#define BOOST_PP_WHILE BOOST_PP_CAT(BOOST_PP_WHILE_, BOOST_PP_AUTO_REC(BOOST_PP_WHILE_P, 256))
#define BOOST_PP_WHILE_P(n) BOOST_PP_BITAND(BOOST_PP_CAT(BOOST_PP_WHILE_CHECK_, BOOST_PP_WHILE_ ## n(BOOST_PP_WHILE_F, BOOST_PP_NIL, BOOST_PP_NIL)), BOOST_PP_CAT(BOOST_PP_LIST_FOLD_LEFT_CHECK_, BOOST_PP_LIST_FOLD_LEFT_ ## n(BOOST_PP_NIL, BOOST_PP_NIL, BOOST_PP_NIL)))
# define BOOST_PP_WHILE_F(d, _) 0




#define BOOST_PP_SUB(x, y)        BOOST_PP_SUB_I(x, y)
#define BOOST_PP_SUB_I(x, y)      BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_WHILE(BOOST_PP_SUB_P, BOOST_PP_SUB_O, (x, y)))
# define BOOST_PP_SUB_P(d, xy)    BOOST_PP_TUPLE_ELEM(2, 1, xy)


#define BOOST_MPL_PP_SUB(i,j)       BOOST_PP_SUB(i,j)

#   define BOOST_MPL_PP_DEF_PARAMS_TAIL_IMPL(i, param, value_func) \
    BOOST_MPL_PP_DEF_PARAMS_TAIL_DELAY_1( \
          i \
        , BOOST_MPL_PP_SUB(BOOST_MPL_LIMIT_METAFUNCTION_ARITY,i) \
        , param \
        , value_func \
        )

#define BOOST_MPL_PP_DEF_PARAMS_TAIL(i, param, value) \
    BOOST_MPL_PP_DEF_PARAMS_TAIL_IMPL(i, param, BOOST_PP_IDENTITY(=value))


#   define BOOST_MPL_PP_NESTED_DEF_PARAMS_TAIL(i, param, value) \
    BOOST_MPL_PP_DEF_PARAMS_TAIL_IMPL(i, param, BOOST_PP_IDENTITY(=value))


#define BOOST_MPL_AUX_NA_SPEC_MAIN(i, name)     BOOST_MPL_AUX_NA_PARAMS(i) > \
        BOOST_MPL_PP_NESTED_DEF_PARAMS_TAIL(i, typename T, na) \
        BOOST_MPL_PP_PARAMS(i, T)

#define BOOST_MPL_AUX_NA_SPEC_NO_ETI(i, name)    BOOST_MPL_AUX_NA_SPEC_MAIN(i, name)
#define BOOST_MPL_AUX_NA_SPEC(i, name)           BOOST_MPL_AUX_NA_SPEC_NO_ETI(i, name)

BOOST_MPL_AUX_NA_SPEC(3, if_)

I hope it can be reduced further.

But with gcc -E I get no preprocessor error. Simplecpp writes an error for this code:

 159.cpp:11: syntax error: failed to expand 'BOOST_MPL_AUX_NA_SPEC', Wrong number of parameters for macro 'BOOST_PP_CAT_I'.

I am not sure but I have the feeling the #if condition is not calculated properly in simplecpp.

I hope this code that I inserted can be reduced to ~ 2-5 lines

@danmar
Copy link
Owner

danmar commented Jul 3, 2019

@Ken-Patrick Can you try with the latest changes. After 229d41f , simplecpp seems to preprocess this code fine:

#include <boost/foreach.hpp>

@SpareSimian
Copy link

I ran into a similar issue with the wxWidgets macros that create a path for an include directive and found this similar bug report.

My code (extracted from wx's setup.h and cpp.h) reproducing the problem using cppcheck:

// simulate wx setup.h/cpp.h macros
#define wxCONCAT_HELPER(text, line) text ## line
#define wxCONCAT(x1, x2) \
    wxCONCAT_HELPER(x1, x2)
#define wxCONCAT3(x1, x2, x3) \
    wxCONCAT(wxCONCAT(x1, x2), x3)
#define wxCONCAT4(x1, x2, x3, x4) \
    wxCONCAT(wxCONCAT3(x1, x2, x3), x4)
#define wxCONCAT5(x1, x2, x3, x4, x5) \
    wxCONCAT(wxCONCAT4(x1, x2, x3, x4), x5)
#define wxCONCAT6(x1, x2, x3, x4, x5, x6) \
    wxCONCAT(wxCONCAT5(x1, x2, x3, x4, x5), x6)
#define wxCONCAT7(x1, x2, x3, x4, x5, x6, x7) \
    wxCONCAT(wxCONCAT6(x1, x2, x3, x4, x5, x6), x7)
/* wxSTRINGIZE works as the preprocessor # operator but also works with macros */
#define wxSTRINGIZE_HELPER(x)       #x
#define wxSTRINGIZE(x)              wxSTRINGIZE_HELPER(x)
#define wxCOMPILER_PREFIX vc142
#define wxARCH_SUFFIX _x64
#define wxCFG
#define wxLIB_SUBDIR \
    wxCONCAT4(wxCOMPILER_PREFIX, wxARCH_SUFFIX, _lib, wxCFG)
#define wxTOOLKIT_PREFIX msw
#define wxSUFFIX ud
#define wxSETUPH_PATH \
    wxCONCAT6(../../../lib/, wxLIB_SUBDIR, /, wxTOOLKIT_PREFIX, wxSUFFIX, /wx/setup.h)
#define wxSETUPH_PATH_STR wxSTRINGIZE(wxSETUPH_PATH)
#include wxSETUPH_PATH_STR

@danmar
Copy link
Owner

danmar commented Aug 4, 2021

@SpareSimian can you please try to create some little code sample I can test locally with simplecpp? and then please report it in a new issue.

I assume the code you show is supposed to include some file but it's not easy to manually see which file..

@SpareSimian
Copy link

SpareSimian commented Aug 4, 2021

Put the above sample in a .cpp file and cppcheck throws an error. It's trying to compute a string that looks like "../../../lib/vc142_x64_lib/mswud/setup.h".

I copied the above into token_concat.cpp, loaded a clone of master into Visual Studio 2019, and got this:

f:\devel\simplecpp\out\build\x64-Debug>.\simplecpp.exe token_concat.cpp 

token_concat.cpp:2: syntax error: failed to expand 'wxSETUPH_PATH_STR', Invalid ## usage when expanding 'wxCONCAT_HELPER'.

@SpareSimian
Copy link

You can replace the include directive with this to make it easier to see. I can reproduce it in the debugger in Visual Studio but I'm not familiar enough with the code to see how it fails. The final token is ")", though.

static const char path[] = wxSETUPH_PATH_STR;

@SpareSimian
Copy link

I managed to shrink the failing sample down to this:

#define wxCONCAT(text1, text2)       text1 ## text2
#define wxCONCAT3(x1, x2, x3)       wxCONCAT(wxCONCAT(x1, x2), x3)
#define wxSETUPH_PATH               wxCONCAT3(vc142, _x64, _lib)
extern int wxSETUPH_PATH;

@danmar
Copy link
Owner

danmar commented Aug 4, 2021

great! can you please create a new issue with that code.

@marek22k
Copy link

Hi, I am running cppcheck with meson and have also encountered this false positive error. While searching for it on the internet I landed on this issue here. Has the error already been forwarded to cppcheck?

@danmar
Copy link
Owner

danmar commented Sep 29, 2024

I reopen this issue because people still have problems we'll need to solve it properly.

@danmar danmar reopened this Sep 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants