Menu

Constant expression treated as composite expression (false positive on misra-c2012-10.7 and inconsistensies with 7.2, 10.3, 10.6 ?)

2022-02-02
2022-02-12
  • Henrik Nyholm

    Henrik Nyholm - 2022-02-02

    Hi,
    I have an issue with ccpcheck where I think there are some inconsistencies with how the MISRA rules are checked and what is treated as a composite expression.

    Looking at the MISRA 2012 standard, it states:

    A composite expression is defined as a non-contant expression which is the direct result of a composite operator (* , / , %, binary +, binary -, bitwise &, bitwise |, bitwise ^, bitwise ~, <<, >> and conditional (?:) if either the second or third operand is a composite expression)

    It also specifies that:

    A parenthesized composite expression is also a composite expression
    A unary + or unary - expression whose operand is a composite expression is also a composite expression
    The result of assignment and compound assignment, postfix and prefix increment and decrement, and cast are not composite expressions
    A constant expression is not a composite expression

    Unless I have misunderstood, the tool does not (at least consistently) account for the fact that constant expressions are not composite expressions. I thus get failures on misra-2012c-10.7 when using several macro constants to calculate the value of other macro constants which I then use in the code. There are also some issues with the U/u suffix and uint16_t vs uint8_t I don't understand, which seems to compound the issue. Thus I did some testing (sorry for not being able to come up with a cleaner example):

    //foo.c
    static void bar(void)
    {
        uint16_t a16b = (0x100ul - 0x80ul);
        uint16_t b16b = (0x100u - 0x80u);
        uint16_t c16b = (0x100 - 0x80);
        uint16_t d16b = 0x80ul;
        uint16_t e16b = 0x80u;
        uint16_t f16b = 0x80;
    
        uint8_t a8b = (0x100ul - 0x80ul);
        uint8_t b8b = (0x100u - 0x80u);
        uint8_t c8b = (0x100 - 0x80);
        uint8_t d8b = 0x80ul;
        uint8_t e8b = 0x80u;
        uint8_t f8b = 0x80;
    }
    

    Which gives the following output (I have numbered the violations to be able to refer back to it:)

    1a: main.c:12:20: [misra-c2012-7.2] Required: A "u" or "U" suffix shall be applied to all integer constants that are represented in an unsigned type
    uint8_t c8b = (0x100 - 0x80);
    ^
    2a: main.c:15:19: [misra-c2012-7.2] Required: A "u" or "U" suffix shall be applied to all integer constants that are represented in an unsigned type
    uint8_t f8b = 0x80;
    ^
    3a: main.c:6:19: [misra-c2012-10.3] Required: The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category
    uint16_t d16b = 0x80ul;
    ^
    4a: main.c:13:17: [misra-c2012-10.3] Required: The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category
    uint8_t d8b = 0x80ul;
    ^
    5a: main.c:3:19: [misra-c2012-10.6] Required: The value of a composite expression shall not be an assigned to an object with wider essential type
    uint16_t a16b = (0x100ul - 0x80ul);
    ^
    6a: main.c:4:19: [misra-c2012-10.6] Required: The value of a composite expression shall not be an assigned to an object with wider essential type
    uint16_t b16b = (0x100u - 0x80u);
    ^

    So let me go though the violatons:
    1a. Correct, however, why doesn't "uint16_t c16b = (0x100 - 0x80);" cause a violation of 7.2 as well?
    case1: c8b = 0b10000000 - MSB (sign bit) = 1 which will cause problem
    case2: c16b = 0b000000010000000 - MSB (sign bit) = 0 which is no problem
    is the checker configured to ignore 7.2 on case2 since there will be no problem? This is not consistent with the formulation of 7.2 as I understand it?
    2a. Correct, however, why doesn't "uint16_t f16b = 0x80;" cause a violation of 7.2?
    3a. Correct
    4a. Correct
    5a. As I understand it, this should not fail on 10.6, but rather 10.3? Moreover, the RHS of the assignment is not a composite expression since the expression is constant? It seems like the tool evaluates the rhs expression as an 8bit even though the operands are specified as unsigned long?
    6a. As I understand it, this should not fail since neither "uint16_t e16b = 0x80u;" nor "uint8_t e8b = 0x80u;" causes a violation? Moreover the RHS of the assignment is not a composite expression since the expression is constant? It seems like the tool evaluates the rhs of the expression as 8bit even though the operands are specified as unsigned and, as I said, "uint16_t e16b = 0x80u;" does not fail?

    Finally, here is my original issue with 10.7, which I think is connected to the issues above, but someone with more familiarity of the tool might understand better if they are connected or not:

    //bar.c 
    #define A 400U
    #define B 100U
    #define C 200U
    
    #define AB_DIFF (A - B)
    #define AC_DIFF (A - C)
    
    #define MAX_VALUE_1 (A/B)
    #define MAX_VALUE_2 4U
    
    static void foobar(void)
    {
        uint16_t var = 2U; 
        uint16_t ab_result = var * AB_DIFF; //AB_DIFF does not fit in 8 bits
        uint16_t ac_result = var * AC_DIFF; //AC_DIFF is small enough to fit in 8 bits
        uint16_t result = var * 200U; //200U can fit in 8 bits
        var++; 
        var %= MAX_VALUE_1;
        var %= MAX_VALUE_2;
    }
    

    gives the following violations:

    1b: main.c:15:30: [misra-c2012-10.7] Required: If a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed, then the other operand shall not have a wider essential type
    uint16_t ac_result = var * AC_DIFF; //AC_DIFF is small enough to fit in 8 bits
    ^
    2b: main.c:18:12: [misra-c2012-10.7] Required: If a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed, then the other operand shall not have a wider essential type
    var %= MAX_VALUE_1;
    ^

    1b. How come "uint16_t ab_result = var * AB_DIFF;" does not fail while " uint16_t ac_result = var * AC_DIFF" do? How come "uint16_t result = var * 200U;" does not fail, when it produces that same code as " uint16_t ac_result = var * AC_DIFF"? Again, it seems like AC_DIFF is treated as a composite expression when my claim is that it is not, as it is a constant expression.
    How can I handle that case when I'm trying to create a library that is configured through macros similar to A/B/C is used to calculate macros similar to AB/AC_DIFF and is used in a similar fashion as the assignment of ab/ac_result? In other words, how can I account for the fact that there are valid combinations of values for A/B/C that can both yield an AB/AC_diff that fits in 8 bits and other combinations that fit in 16 bit? Surely the correct answer is to just use uint16_t for ab/ac_result? However, the tool gives me errors when values for A/B/C is selected such that AB/AC_DIFF fit in 8 bits. Trying to do any casting just yields 10.8 violations in my experience. If this is not a failing of the tool, how are you supposed to resolve this issue?
    2b. How come "var %= MAX_VALUE_1;" fails while "var %= MAX_VALUE_2;" does not when they are effectively the same? Again, there seems to be an issue where it treats MAX_VALUE_1 as a composite expression when I think it is a constant expression. If this is how the tool should work, what is the correct approach here?

    I appreciate any feedback on this, mainly a confirmation that the issues with 10.7 above are false positives so that I may suppress them?

    Thanks!

     

    Last edit: Henrik Nyholm 2022-02-02
  • Daniel Marjamäki

    I appreciate any feedback on this, mainly a confirmation that the issues with 10.7 above are false positives so that I may suppress them?

    I investigated the 10.7 warnings. And yes in deed it seems these are false positives. I'll need to adjust that.

     
    👍
    1
  • Henrik Nyholm

    Henrik Nyholm - 2022-02-03

    Thanks for the reply @danielmarjamaki!
    Is there a ticket associated with this so that I can use that as documentation for suppressing the violations?

     
  • Henrik Nyholm

    Henrik Nyholm - 2022-02-10

    Hi again @danielmarjamaki,
    just asking again in case you did not see my last question?
    Thank you for your support!

     
  • Daniel Marjamäki

    I think these observations are very good and I wanted to fix this asap. But I have not fixed it yet.

     

    Last edit: Daniel Marjamäki 2022-02-11
    • Henrik Nyholm

      Henrik Nyholm - 2022-02-11

      Thanks!
      Glad you want to fix it asap, but there is no need to rush on my side, just wondering if it would be possible to add a ticket on https://trac.cppcheck.net/ so that I may document this false positive as a known issue in my project?

       
  • Daniel Marjamäki

    I have updated the checking for 10.6. Feel free to test if it works properly. A fix for 7.2 is on the way.

     
    • Daniel Marjamäki

      7.2 has been tweaked. Feel free to test that out also.

       

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.