|
| 1 | +/** |
| 2 | + * @id c/misra/invalid-literal-for-integer-constant-macro-argument |
| 3 | + * @name RULE-7-5: The argument of an integer constant macro shall be a decimal, hex, or octal literal |
| 4 | + * @description Integer constant macro arguments should be a decimal, hex, or octal literal. |
| 5 | + * @kind problem |
| 6 | + * @precision very-high |
| 7 | + * @problem.severity error |
| 8 | + * @tags external/misra/id/rule-7-5 |
| 9 | + * correctness |
| 10 | + * external/misra/c/2012/amendment3 |
| 11 | + * external/misra/obligation/required |
| 12 | + */ |
| 13 | + |
| 14 | +import cpp |
| 15 | +import codingstandards.c.misra |
| 16 | +import codingstandards.cpp.IntegerConstantMacro |
| 17 | +import codingstandards.cpp.Literals |
| 18 | + |
| 19 | +/** |
| 20 | + * Floating point literals are not allowed. Neither are char or string |
| 21 | + * literals, although those are not `NumericLiteral`s and therefore detected in |
| 22 | + * `InvalidIntegerConstantMacroArgument.ql`. |
| 23 | + */ |
| 24 | +predicate validLiteralType(PossiblyNegativeLiteral literal) { |
| 25 | + literal.getBaseLiteral() instanceof Cpp14Literal::DecimalLiteral or |
| 26 | + literal.getBaseLiteral() instanceof Cpp14Literal::OctalLiteral or |
| 27 | + literal.getBaseLiteral() instanceof Cpp14Literal::HexLiteral or |
| 28 | + // Ignore cases where the AST/extractor don't give us enough information: |
| 29 | + literal.getBaseLiteral() instanceof Cpp14Literal::UnrecognizedNumericLiteral |
| 30 | +} |
| 31 | + |
| 32 | +/** |
| 33 | + * Clang accepts `xINTsize_C(0b01)`, and expands the argument into a decimal |
| 34 | + * literal. Binary literals are not standard c nor are they allowed by rule 7-5. |
| 35 | + * Detect this pattern before macro expansion. |
| 36 | + */ |
| 37 | +predicate seemsBinaryLiteral(MacroInvocation invoke) { |
| 38 | + invoke.getUnexpandedArgument(0).regexpMatch("-?0[bB][01]+") |
| 39 | +} |
| 40 | + |
| 41 | +/** |
| 42 | + * Extractor converts `xINTsize_C('a')` to a decimal literal. Therefore, detect |
| 43 | + * this pattern before macro expansion. |
| 44 | + */ |
| 45 | +predicate seemsCharLiteral(MacroInvocation invoke) { |
| 46 | + invoke.getUnexpandedArgument(0).regexpMatch("-?'\\\\?.'") |
| 47 | +} |
| 48 | + |
| 49 | +string explainIncorrectArgument(MacroInvocation invoke) { |
| 50 | + if seemsBinaryLiteral(invoke) |
| 51 | + then result = "binary literal" |
| 52 | + else |
| 53 | + if seemsCharLiteral(invoke) |
| 54 | + then result = "char literal" |
| 55 | + else |
| 56 | + exists(PossiblyNegativeLiteral literal | |
| 57 | + literal = invoke.getExpr() and |
| 58 | + if literal.getBaseLiteral() instanceof Cpp14Literal::FloatingLiteral |
| 59 | + then result = "floating point literal" |
| 60 | + else result = "invalid literal" |
| 61 | + ) |
| 62 | +} |
| 63 | + |
| 64 | +from MacroInvocation invoke, PossiblyNegativeLiteral literal |
| 65 | +where |
| 66 | + not isExcluded(invoke, Types2Package::invalidLiteralForIntegerConstantMacroArgumentQuery()) and |
| 67 | + invoke.getMacro() instanceof IntegerConstantMacro and |
| 68 | + literal = invoke.getExpr() and |
| 69 | + ( |
| 70 | + not validLiteralType(literal) or |
| 71 | + seemsBinaryLiteral(invoke) or |
| 72 | + seemsCharLiteral(invoke) |
| 73 | + ) |
| 74 | +select literal, |
| 75 | + "Integer constant macro " + invoke.getMacroName() + " used with " + |
| 76 | + explainIncorrectArgument(invoke) + |
| 77 | + " argument, only decimal, octal, or hex integer literal allowed." |
0 commit comments