I have a project I'm developing in C99 and I'm trying to make it compliant with the MISRA 2012 standard.
In one file I define an enum where each value should be treated as a flag:
/**
* Enumerates the configurable options for performing calibration.
*/
typedef enum
{
CALIBRATION_DEFAULT_OPTIONS=0, /**< Calibrate with default options */
CALIBRATION_RESET_POSITION=1, /**< Ensure window is fully open and motor re-homed */
CALIBRATION_FORCE_RECALIBRATE=2 /**< Force recalibration even if calibration data exists */
} CALIBRATION_OPTIONS_T;
I'd like to be able to declare something like:
CALIBRATION_OPTIONS_T options = CALIBRATION_RESET_POSITION | CALIBRATION_FORCE_RECALIBRATE;
I also define one function that accepts a CALIBRATION_OPTIONS_T parameter and performs different logic depending on which flags are set:
// If forced to recalibrate, do so regardless of whether metrics exist in
// EEPROM or not.
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
{
MOTION_ResetCalibrationData();
calibration = performCalibrationRoutine();
}
// Otherwise try fetching existing metrics from EEPROM. If they exist, return
// these metrics.
else if (tryFetchStoredMetrics(&calibration))
{
if ((options & CALIBRATION_RESET_POSITION) != 0U)
{
calibration.lastPosition = 0;
resetMotorPosition();
storeMetrics(calibration);
}
}
However, when I lint my project with PC-lint Plus I get the following output explaining that this code violates MISRA 2012 Rule 10.1:
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
~~~~~~~ ^
*** LINT: src\c\motionCalibrator.c(645) note 9027: an enum value is not an appropriate left operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*** LINT: src\c\motionCalibrator.c(645) note 9027: an enum value is not an appropriate right operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_FORCE_RECALIBRATE) != 0U)
^
*** LINT: src\c\motionCalibrator.c(645) warning 641: implicit conversion of enum 'CALIBRATION_OPTIONS_T' to integral type 'unsigned int'
if ((options & CALIBRATION_RESET_POSITION) != 0U)
~~~~~~~ ^
*** LINT: src\c\motionCalibrator.c(655) note 9027: an enum value is not an appropriate left operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_RESET_POSITION) != 0U)
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
*** LINT: src\c\motionCalibrator.c(655) note 9027: an enum value is not an appropriate right operand to & [MISRA 2012 Rule 10.1, required]
if ((options & CALIBRATION_RESET_POSITION) != 0U)
^
*** LINT: src\c\motionCalibrator.c(655) warning 641: implicit conversion of enum 'CALIBRATION_OPTIONS_T' to integral type 'unsigned int'
In particular, the MISRA 2012 standard advises against using & with enums for these two reasons:
An operand of essentially enum type should not be used in an
arithmetic operation because an enum object uses an
implementation-defined integer type. An operation involving an enum
object may therefore yield a result with an unexpected type. Note that
an enumeration constant from an anonymous enum has essentially signed
type.
Shift and bitwise operations should only be performed on operands of
essentially unsigned type. The numeric value resulting from their use
on essentially signed types is implementation-defined.
I'd like to know if there's a MISRA-compliant way I can use flag-like enums and test that specific flags are set.
This boils down to the essential type model and rule 10.1. You are only allowed to do bitwise operations on types that are essentially unsigned. Enums are treated as their own unique type by MISRA-C.
Doing things like CALIBRATION_OPTIONS_T options = CALIBRATION_RESET_POSITION | CALIBRATION_FORCE_RECALIBRATE; is otherwise fine and pretty canonical C, but you have to resort to using unsigned constants. To take type safety a bit to the extreme, you could do this:
typedef uint32_t CALIBRATION_OPTIONS_T;
#define CALIBRATION_DEFAULT_OPTIONS ((CALIBRATION_OPTIONS_T)0x00u) /**< Calibrate with default options */
#define CALIBRATION_RESET_POSITION ((CALIBRATION_OPTIONS_T)0x01u) /**< Ensure window is fully open and motor re-homed */
#define CALIBRATION_FORCE_RECALIBRATE ((CALIBRATION_OPTIONS_T)0x02u) /**< Force recalibration even if calibration data exists */
where the hex notation is self-documentating code showing that these are bit masks, the u suffix is required by MISRA in some circumstances and the uint32_t is there to block potential implicit type promotions.
Please note that using enums don't necessarily give increased type safety, but rather the opposite. They are in many cases treated like plain int, in other cases as implementation-defined size integers. Their type safety is pretty much broken by C language design. though you can make them safe with some tricks, see my posts at How to create type safe enums?.
Related
I've been having a lot of trouble with MISRA when doing << and & operations.
Definitions of types for context
typedef uint32_t uint32;
typedef volatile uint32* canreg_txrx_pointer;
canreg_txrx_pointer canreg_txrx;
uint8_t valueRead, value;
#define CAN_DIR_BIT_POS ((uint8_t)2U)
#define CAN_OUT_BIT_POS ((uint8_t)1U)
#define CAN_IN_BIT_POS ((uint8_t)0U)
When trying to perform a series of << and & operations on the registers, I continually get the error #1397-D (MISRA-C:2004 10.5/R) If the bitwise operators ~ and << are applied to an operand of underlying type unsigned char or unsigned short, the result shall be immediately cast to the underlying type of the operand
An example that doesn't pass the misra check and then an example that does is given below
*canreg_txrx = (uint32)((*canreg_txrx & (~(uint32)(1U << CAN_OUT_BIT_POS))) | (uint32)(value << CAN_OUT_BIT_POS)); //doesn't pass MISRA
valueRead = (uint8_t)(((uint8_t)*regToOperateOn & (uint8_t)(1U << CAN_OUT_BIT_POS)) >> CAN_OUT_BIT_POS); //passes MISRA
I've tried multiple changes to the offending line where none of them have resulted in MISRA letting it through (except randomly where it just gives me no warnings) to the point where I decided to cast everything and that still doesn't work
*canreg_txrx = (uint32)((*canreg_txrx & (uint32)(~(uint32)((uint32)1U << (uint32)CAN_OUT_BIT_POS))) | (uint32)((uint32)value << (uint32)CAN_OUT_BIT_POS)); //doesn't pass MISRA
I'm really confused on how to fix this code so that it will pass MISRA as this bitwise logic is used in multiple places and all of them don't pass. I assumed that if everything is cast to the same type then there should really be no issue since it's all operating at the same type but for some reason that doesn't work.
I have never used Misra but I find your code hard to read with all that noise of typedefs, casts and parenthesis. My suggestion would be to simplify and split the problem into smaller parts along these lines:
*canreg_txrx = (uint32)((*canreg_txrx & (~(uint32)(1U << CAN_OUT_BIT_POS))) | (uint32)(value << CAN_OUT_BIT_POS)); // =>
*canreg_txrx &= ~(1U << CAN_OUT_BIT_POS);
*canreg_txrx |= value << CAN_OUT_BIT_POS; // maybe =>
#define BIT_CLEAR(val, pos) ((val) &= ~(1U << (pos)))
BIT_CLEAR(*canreg_txrx, CAN_OUT_BIT_POS);
*canreg_txrx |= value << CAN_OUT_BIT_POS; // *
'*' If value is a bit, then this is just bit set, otherwise can't think of good name.
Then run it through Misra and only fix what it complains about.
MISRA-C is a bit confused since it insists on a cast to "underlying type" (MISRA-C:2004 term) after the shift has been done. I prefer to do the cast to a large unsigned type before the shift, since that rules out all problems that can happen.
What these rules strive to prevent is an accidental conversion to a signed type through Implicit type promotion rules followed by something like a bit shift, which could be dangerous and cause various bugs, including change of signedness, accidental sign extension, undefined behavior upon left shifting signed values, impl.defined behavior upon right shifting them, and so on.
In your case, you get these problems specifically because you are not casting the result of the ~ to the underlying type here: ~(uint32)(1U << CAN_OUT_BIT_POS)). It appears that your static analyser is giving a false positive, since the underlying type here appears to be uint32_t. There is no smaller type to account for unless since you appear to have have 32 bit integers (since you have 32 bit CAN registers) , but the tool might not know that your integers are 32 bits, in which case 1U could have been a 16 bit value.
Otherwise, maybe the tool is getting confused because you use confused casts when defining those macros... but for the shift operators specifically, the type of the right operand does not matter (though it's promoted too).
In this particular case, none of this should be dangerous since you expect unsigned 32. The warnings, the casts, everything - it's just noise, turning your code brittle and unreadable.
I'd suggest you salvage the code in the following manner:
Get rid of all the uint8 casts from the macros, that's just noise and potentially causing problems.
Get rid of the home-brewed uint32 etc types and use standard C uint32_t. typedef uint32_t uint32; is rotten code that shouldn't exist in any C program. Use the existing industry standards, don't invent new ones.
Don't hide pointers behind typedefs, that's very dangerous practice which makes the code hard to read.
Get rid of superfluous casts. Assuming this is a 32 bitter, you shouldn't cast 1U types to uint32_t since they are the same type already.
Break up unreadable lines into several expressions. For example we can do const uint32_t mask = ~(1U << CAN_OUT_BIT_POS);. There should be no need to cast since the underlying type is already uint32_t. Your MISRA checker might be too dumb to realize that you have 32 bit integers though, so you might have to add a cast (uint32_t)~ .... Not for MISRA-C:2004 compliance but to silence a potentially dumb tool.
In case you just need to cast to silence a particular tool, you should make an explicit comment about that: /* this cast just to silence BrokenAnalyser from Bugworks */. To show that you know what you are doing and that you separate false positives from actual necessary casts.
Another valid concern of MISRA-C is that you shouldn't be doing multiple accesses to volatile registers in the same expression, since this introduces unsequenced side effects, plus you can't really tell when and where on that line the actual register access happens, plus it blocks compiler optimizations. This is another MISRA violation that you have not yet spotted. This can also be solved by breaking up the long expression into several.
One example:
const uint32_t mask = ~(1U << CAN_OUT_BIT_POS);
uint32_t txrx = *canreg_txrx; // volatile access on a line of its own
txrx = (txrx & mask) | ((uint32_t)value << CAN_OUT_BIT_POS);
*canreg_txrx = txrx;
The middle txrx line will get optimized and in the machine code you'll just get a few instructions anyway. This should be MISRA-C:2004 and 2012 compliant, far as I can tell. This also doesn't shift the implicitly promoted value and then cast afterwards.
I have taken over code that has a lot of compound assignment operators in it.
I think that compound operators are not 'really' MISRA compliant.
I can't seem to find any reference to them.
I believe I understand what is happening and that it should actually be separated.
UINT16 A |= B; /* Incorrect */
UINT16 A = (UINT16)((UINT32)A | (UINT32)B); /* Correct */
UINT16 A <<= 1u; /* Incorrect */
UINT16 A = (UINT16)((UINT32)A << (UINT32)1u); /* Correct */
So, my questions are:
Does MISRA frown upon compound assignments?
Is there any kind of quick fix, instead of ignoring the warning?
Thank you,
Does MISRA frown upon compound assignments?
Not as such. The rules for compound assignment are similar to the rules for simple assignment. MISRA speaks of assignment operators in general times, including all of them.
MISRA does however frown upon implicit type promotions, see Implicit type promotion rules. You can't understand these MISRA warnings unless you understand implict promotions.
Is there any kind of quick fix, instead of ignoring the warning?
You can't really fix this without understanding the warning. The only quick fix is to only use uint32_t everywhere and never signed or small integer types, but that isn't always feasible. Your original code would have been compliant if all variables were uint32_t.
In general, the latest MISRA only allows various conversions between types of the same essential type category. Unsigned integers is one such category, signed integers in another, and so on.
It isn't easy to tell how exactly your code violates MISRA without knowing the type of B and sizeof(int). This has nothing to do with compound assignment as such, except that compound assignment operators are a bit cumbersome to work with when it comes to implicit promotions.
MISRA frowns upon assigning the value of an expression (after implicit promotion) to a narrower essential type of the same category or to a different category. In plain English you shouldn't for example assign the result of a uint32_t operation to a uint16_t variable or a signed result to an unsigned variable. This is most often fixed with casting at appropriate places.
Regarding your specific examples, assuming B is uint16_t and the CPU is 32 bit, you get problems with implicit type promotion.
Since A |= B is equivalent to A | B, the usual arithmetic conversions will promote the operands to int in case of 32 bit CPU. So it is a signed 32 bit type.
Assume you had A << 31u - then this would actually have invoked an undefined behavior bug, which the rule seeks to protect against.
Sufficient fixes for MISRA-C compliance:
A = (uint16_t) (A | B); // compliant
A = (uint16_t) ((uint32_t)A << 1u) // compliant
Observing below Misra warning for below part of code.
Unpermitted operand to operator '!' [MISRA 2012 Rule 10.1, required]
Not sure what could be the fix here to get rid of this warning.
#define C_BYTE unsigned char
C_BYTE SessionStatus;
#define DISCONNECTED 0x10
if((!(SessionStatus & (C_BYTE) DISCONNECTED)))
{
//Do something
}
I tried a few chances but didn't work as below.
1)
if((~(SessionStatus & (C_BYTE) DISCONNECTED)))
{
//Do something
}
2)
if((!(SessionStatus & (C_BYTE) DISCONNECTED)) != 0u)
{
//Do something
}
The reason for the warning is that MISRA-C rule 10.1 expects the operand to the ! && || operators to be "essentially boolean". In your case it is actually an int, because of implicit type promotion rules.
Your 2nd example almost solved it, but you must convert to essentially boolean before applying !. That is:
if(!( (SessionStatus & (C_BYTE)DISCONNECTED) != 0u ))
This is ok since the result of the != operator is to be regarded as essentially boolean. So that code is MISRA-C compliant, but a bit hard to read. I would instead recommend this:
#define DISCONNECTED 0x10u // u suffix makes this essentially unsigned
...
bool disconnected = (bool) (SessionStatus & DISCONNECTED);
if(!disconnected)
By adding the u suffix to the integer constant, it is essentially unsigned, same type category as unsigned char. Therefore the & operation is valid without using any casts. However, we are not allowed to implicitly convert from essentially unsigned to essentially boolean, therefore add the cast to bool.
EDIT
Since SessionStatus & DISCONNECTED is a "composite expression", MISRA doesn't allow the result to be assigned or cast to a different or wider essential type. The rationale is that they fear incompetent programmers who believe that the calculation in for example (uint32_t)(u16a + u16b) is carried out with uint32_t because of the cast, which is of course nonsense. (It would be better to educate the programmers about basic C than to come up with artificial rules, but anyway...)
I'd advise to ignore this rule entirely, but if you can't for whatever reason, here's an alternative fix:
#define DISCONNECTED 0x10u
...
unsigned char disconnected = SessionStatus & DISCONNECTED;
if(!(bool)disconnected)
But of course this is worse code than my first example.
I'm troubleshooting some code and I encountered this line
uint8var = ((constUint8[0]) >> 7) & 0x01u;
I was wondering what the correct practice in this is. Is the & 0x01u necessary for proper implementation when writing MISRA compliant code, considering I'm shifting uint8 by 7 digits to the right?
Right-shifting a uint8_t can never be problematic in itself. However, MISRA-C aims to block you from writing bugs caused by implicit integer promotion. In your case constUint8[0] will get implicitly promoted to int which is signed. This will cause various MISRA compliance problems that are easiest avoided by ensuring your code contains no implicit promotions in the first place.
When it comes to shifts, that means casting to a large integer type before shifting:
(uint32_t)constUint8[0] >> 7.
The mask with 0x01u is superfluous and adds no value. It can be safely removed.
To achieve MISRA-C compliance, the best way is to re-write the code like this:
uint8var = (uint8_t) ((uint32_t)constUint8[0] >> 7);
where the (uint8_t) cast ensures that there's no implicit conversion, but that we explicitly go back to the intended type. MISRA-C doesn't allow implicit assignment from larger types to smaller ones.
For more info see Implicit type promotion rules.
In a huge macro I have in a program aimed for a 16-bit processor, the following code (simplified) appears several times:
typedef unsigned short int uint16_t;
uint16_t var;
var = ~0xFFFF;
MISRA complains with the warning 12.4: integer conversion resulted in truncation. The tool used to get this is Coverity.
I have checked the forum but I really need a solution (instead of changing the negation by the actual value) as this line is inside a macro with varying parameters.
I have tried many things and here is the final attempt which fails also:
var = (uint16_t)((~(uint16_t)(0xFFFFu))&(uint16_t)0xFFFFu);
(the value 0xFFFF is just an example. In the actual code, the value is a variable which can take whatever value (but 16 bits))
Do you have any other idea please? Thanks.
EDIT:
I have tried then to use 32bits value and the result is the same with the following code:
typedef unsigned int uint32_t;
uint32_t var;
var = (uint32_t)(~(uint32_t)(0xFFFF0000u));
Summary:
Assuming you are using a static analyser for MISRA-C:2012, you should have gotten warnings for violations against rule 10.3 and 7.2.
Rule 12.4 is only concerned with wrap-around of unsigned integer constants, which can only occur with the binary + and - operators. It seems irrelevant here.
The warning text doesn't seem to make sense for neither MISRA-C:2004 12.4 nor MISRA-C:2012 12.4. Possibly, the tool is displaying the wrong warning.
There is however a MISRA:2012 rule 10.3 that forbids to assign a value to a variable that is of a smaller type than intended in the expression.
To use MISRA terms, the essential type of ~0xFFFF is unsigned, because the hex literal is of type unsigned int. On your system, unsigned int is apparently larger than uint16_t (int is a "greater ranked" integer type than short in the standard 6.3.1.1, even if they are of the same size). That is, uint16_t is of a narrower essential type than unsigned int, so your code does not conform to rule 10.3. This is what your tool should have reported.
The actual technical issue, which is hidden behind the MISRA terms, is that the ~ operator is dangerous because it comes with an implicit integer promotion. Which in turn causes code like for example
uint8_t x=0xFF;
~x << n; // BAD, always a bug
to invoke undefined behavior when the value 0xFFFFFF00 is left shifted.
It is therefore always good practice to cast the result of the ~ operator to the correct, intended type. There was even an explicit rule about this in MISRA 2004, which has now merged into the "essential type" rules.
In addition, MISRA (7.2) states that all integer constants should have an u or U suffix.
MISRA-C:2012 compliant code would look like this:
uint16_t var;
var = (uint16_t)~0xFFFFu;
or overly pedantic:
var = (uint16_t)~(uint16_t)0xFFFFu;
When the compiler looks at the right side, first it sees the literal 0xFFFF. It is automatically promoted to an integer which is (obvious from the warning) 32-bit in your system. Now we can imagine that value as 0x0000FFFF (whole 32-bit). When the compiler does the ~ operation on it, it becomes 0xFFFF0000 (whole 32-bit). When you write var = ~0xFFFF; the compiler in fact sees var = 0xFFFF0000; just before the assign operation. And of course a truncation happens during this assignment...