Menu

Erroneous Syntax Error for Template in Nlohmann JSON Header

2024-07-09
2025-02-12
  • Scott Ehlert

    Scott Ehlert - 2024-07-09

    I recently came across an issue where cppcheck is reporting a syntax error in an included header from the nlohmann JSON library. This ends up stopping analysis of any source file that includes it.

    nlohmann/include/nlohmann/json_fwd.hpp:49:54: error: syntax error [syntaxError]
             class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
                                                         ^
    

    That line can be found here: https://github.com/nlohmann/json/blob/960b763ecd144f156d05ec61f577b04107290137/include/nlohmann/json_fwd.hpp#L49

    It is interesting that there is already an inline suppression there. Has this possibly been reported before and I missed it?

    In any case, I initially found this problem when using version 2.13.0 from Ubuntu 24.04, but it is also present in the latest version from the git repository (currently 73682dadd14f3f191f28549a1e32d2203d5b664a).

    I came up with a hacky patch to address the problem, but it is probably not the best solution.

    diff --git a/lib/token.cpp b/lib/token.cpp
    index aee3150bd..089bd66e1 100644
    --- a/lib/token.cpp
    +++ b/lib/token.cpp
    @@ -916,7 +916,7 @@ const Token * Token::findClosingBracket() const
             }
             // save named template parameter
             else if (templateParameter && depth == 1 && Token::Match(closing, "[,=]") &&
    -                 closing->previous()->isName() && !Token::Match(closing->previous(), "class|typename|."))
    +                 closing->previous()->isName() && !Token::Match(closing->previous(), "class|typename|.") && !Token::Match(closing->previous()->previous(), "=|::"))
                 templateParameters.insert(closing->strAt(-1));
         }
    

    The patch basically stops Token::findClosingBracket from saving names that appear after an equal sign in template parameters.

    The issue with the current code is that with a previous parameter on line 41 of the json_fwd header:

    template<typename U, typename... Args> class ArrayType = std::vector,
    

    The name vector ends up being saved to the templateParameters vector. Then when the opening bracket < appears on line 49, it is interpreted as a less-than comparison operator because vector had previously been stored in the templateParameters vector.

    The patch may address the initial problem, but there are still other problematic cases I have found such as:

    template <template<size_t A = 7, bool B = A < 10> class C>
    class foo;
    

    In this case, the third < is not interpreted as a less-than operator because names are only stored in the templateParamaters vector when the depth is 1. I suppose the depth == 1 condition in findClosingBracket could be loosened if you were to store parameters for each depth. I initially thought about doing this, but then I realized that the following is also problematic:

    constexpr size_t A = 7;
    template <bool B = A < 10>
    class bar;
    

    This one couldn't really be solved by storing known template parameters because A is not a template parameter at all.

    So I guess the main issue is that there does not seem to be an easy way to get previously defined symbols from within findClosingBracket in order to parse less-than operators correctly. But since I have only started diving into the cppcheck code very recently, I could have easily missed something. I should also note that I am not an expert in parsing C++, so there may be other solutions to this that I had not considered.

    Any thoughts on this would be greatly appreciated. Thank you.

     

    Last edit: Scott Ehlert 2024-07-09
  • CHR

    CHR - 2024-07-10

    Thanks for reporting, ticket is here: https://trac.cppcheck.net/ticket/12923
    Please feel free to open a PR at https://github.com/danmar/cppcheck

     

    Last edit: CHR 2024-07-10
  • therisktaker

    therisktaker - 2024-07-15

    Came here to report same issue. This is not the first time I've encountered such a problem. The problem is more general than this particular example.

    As an extensive Cppcheck user, I often do:

    cppcheck --suppress=*:*lib/* ...
    or
    cppcheck --inline-suppr ...
    

    And having this particular JSON library in lib directory. I have the false impression that everything is ok, but it's not. Which, as a result causes to potentially have undetected problems in code.


    @danielmarjamaki @chrchr maybe Cppcheck needs some new kind of error type like "breaking". Which when triggered (like on [syntaxError] and other breaking analysis) will inform the user that the analysis is broken even if suppressions are used.


    In production CI pipelines I used to do extra step to check if "configuration" is fine doing something like:

    cppcheck <no explicit global suppressions like *:*lib/*> <no --enable=all to only report hard errors> <eventually --suppress=some_not_analysis_breaking_suppress if 3rd party has some issues> ...
    

    Which then informed me if analysis is broken or not. But it would be great if we had some Cppcheck mechanism for this.

     
    • therisktaker

      therisktaker - 2025-02-12

      Today I found out that there is a --safety option that solves the problems I described.

       

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.