Skip to content

Implement Conversions rule package #919

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

Open
wants to merge 57 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
1d2aa2e
RULE-7-0-1 - NoConversionFromBool
lcartey Jun 10, 2025
5b810c3
Rule 7.0.1: Address review issues
lcartey Jun 10, 2025
62d5dcc
RULE-7-0-2 - NoImplicitBoolConversion
lcartey Jun 11, 2025
f92a2c9
Rule 7.0.2: Extend test case, support member function pointers
lcartey Jun 11, 2025
b62394a
Add Conversions exclusions file
lcartey Jun 12, 2025
a2c9912
RULE-7-0-6 - NumericAssignmentTypeMismatch
lcartey Jun 17, 2025
229705f
Rule 7.0.6: Update test with better variable names
lcartey Jun 17, 2025
f870023
Rule 7.0.6: Fix id-expression detection
lcartey Jun 17, 2025
0ed1689
Rule 7.0.6: Support reference types
lcartey Jun 17, 2025
778b344
Rule 7.0.6: Ignore compound expressions
lcartey Jun 17, 2025
acfb253
Rule 7.0.6: Additional function return test cases
lcartey Jun 17, 2025
9dbc39f
Rule 7.0.6: Clarify pass-by-value on parameters
lcartey Jun 17, 2025
b310e14
Rule 7.0.6: Add user defined operator tests
lcartey Jun 17, 2025
af2ff95
MISRA C++ 2023: Create StandardConversions library
lcartey Jun 17, 2025
ea7d168
MISRA C++ StandardConversions - improve detection of bitfield types
lcartey Jun 17, 2025
5843258
Rule 7.0.6: Improve bitfield support
lcartey Jun 18, 2025
01b517d
StandardConversions: Improve detection of numeric type category
lcartey Jun 18, 2025
06eddfa
Rule 7.0.6: Add a test case for non-numeric (not covered)
lcartey Jun 18, 2025
a65c4cc
StandardConversions: Handle aggregate initialization
lcartey Jun 18, 2025
04613cd
Add a library for determining constant expressions
lcartey Jun 19, 2025
4be1395
Rule 7.0.6: Use BigInt for constant expressions
lcartey Jun 19, 2025
e87892e
Rule 7.0.6: add tests for cv-qualified types
lcartey Jun 19, 2025
916fb3d
Rule 7.0.6: Support pointer to member cases
lcartey Jun 19, 2025
2ec2814
Rule 7.0.6: Support functions with default parameters
lcartey Jun 19, 2025
e659944
Rule 7.0.6: Ignore deleted overloads
lcartey Jun 19, 2025
c0fe44d
Rule 7.0.6: Refactor overload independent code
lcartey Jun 19, 2025
06ffbfb
Rule 7.0.6: Move aggregate tests to new file
lcartey Jun 19, 2025
fc63db1
Rule 7.0.6: Move operator tests to separate file
lcartey Jun 19, 2025
f68658a
Rule 7.0.6: Support constructor field initializers
lcartey Jun 19, 2025
3fdaa98
Rule 7.0.6: Handle explicit conversions
lcartey Jun 19, 2025
063a5cc
Rule 7.0.6: Improve tests for templates
lcartey Jun 20, 2025
96d5c1b
MISRA C++ 2023: Rename StandardConversions library
lcartey Jun 20, 2025
7c5fb87
Rule 7.0.6: Address performance issues
lcartey Jun 20, 2025
6ddab35
Create Call library
lcartey Jun 20, 2025
983f256
Extend ios stubs for C++
lcartey Jun 25, 2025
bb9f5a1
RULE-7-11-3 - FunctionPointerConversionContext
lcartey Jun 25, 2025
2e75310
C++: Improve char_traits stubs
lcartey Jun 25, 2025
4bf8d51
Improve C++ stubs for locales
lcartey Jun 6, 2025
4d5e35f
Extend C++ stubs for locale
lcartey Jun 6, 2025
f2b5410
C++: Add optional stubs
lcartey Jun 25, 2025
74946cf
Rule 7.0.3: NoCharacterNumericalValue.ql
lcartey Jun 25, 2025
a2d7ee3
RULE-7-0-5 - NoSignednessChangeFromPromotion
lcartey Jun 26, 2025
c25057d
Add sizeOfInt() predicate
lcartey Jun 27, 2025
3a8dab1
Rule 7.0.5: Refactor to enable non-Conversions
lcartey Jun 27, 2025
3cf9eac
Improve detection of integer promotions and usual arithmetic conversions
lcartey Jun 27, 2025
f50baa9
Format test case
lcartey Jun 27, 2025
a7ce6f5
Rule 7.0.5: Expand test cases
lcartey Jun 27, 2025
9aed463
Ruley 7.0.5: Support lvalue conversions on assign operations
lcartey Jun 27, 2025
aac2dc2
Refactor to use NumericType
lcartey Jun 27, 2025
92c7dac
Rule 7.0.5: Add pointer tests (should be ignored)
lcartey Jun 27, 2025
4be88a3
Rule 7.0.5: Add failing test case
lcartey Jun 27, 2025
f1502d6
Rule 7.0.5: Add test cases for enum conversions
lcartey Jul 1, 2025
89485d5
Conversions: Swap some queries around
lcartey Jul 1, 2025
6845cdc
RULE-7-0-4 - InappropriateBitwiseOrShiftOperands
lcartey Jul 4, 2025
db58704
Test large and negative constants, use BigInt
lcartey Jul 4, 2025
3d2616a
Rule 7.0.4: Add support for shift-assignment operators
lcartey Jul 7, 2025
05cfc2b
Rule 7.0.5: Add an implementation_scope
lcartey Jul 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions cpp/common/src/codingstandards/cpp/Call.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import cpp
import codingstandards.cpp.types.Type

/**
* Gets the `FunctionType` of an expression call.
*/
FunctionType getExprCallFunctionType(ExprCall call) {
// A standard expression call
// Returns a FunctionPointerIshType
result = call.(ExprCall).getExpr().getType()
or
// An expression call using the pointer to member operator (.* or ->*)
// This special handling is required because we don't have a CodeQL class representing the call
// to a pointer to member function, but the right hand side is extracted as the -1 child of the
// call.
// Returns a RoutineType
result = call.(ExprCall).getChild(-1).getType().(PointerToMemberType).getBaseType()
}
97 changes: 97 additions & 0 deletions cpp/common/src/codingstandards/cpp/ConstantExpressions.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import cpp

final private class FinalExpr = Expr;

/**
* An integer constant expression as defined by the C++17 standard.
*/
class IntegerConstantExpr extends FinalExpr {
IntegerConstantExpr() {
// An integer constant expression is a constant expression that has an
// integral type.
this.isConstant() and
exists(Type unspecifiedType | unspecifiedType = this.getUnspecifiedType() |
unspecifiedType instanceof IntegralType
or
// Unscoped enum type
unspecifiedType instanceof Enum and
not unspecifiedType instanceof ScopedEnum
)
}

/**
* Gets the value of this integer constant expression.
*
* This is only defined for expressions that are constant expressions, and
* that have a value that can be represented as a `BigInt`.
*/
QlBuiltins::BigInt getConstantValue() {
if exists(getPreConversionConstantValue())
then result = getPreConversionConstantValue()
else result = this.getValue().toBigInt()
}

/**
* Gets the pre-conversion constant value of this integer constant expression, if it is different
* from `getValue()`.
*
* This is required because `Expr.getValue()` returns the _converted constant expression value_
* for non-literal constant expressions, which is the expression value after conversions have been
* applied, but for validating conversions we need the _pre-conversion constant expression value_.
*/
private QlBuiltins::BigInt getPreConversionConstantValue() {
// Access of a variable that has a constant initializer
result =
this.(VariableAccess)
.getTarget()
.getInitializer()
.getExpr()
.getFullyConverted()
.getValue()
.toBigInt()
or
result = this.(EnumConstantAccess).getTarget().getValue().toBigInt()
or
result = -this.(UnaryMinusExpr).getOperand().getFullyConverted().getValue().toBigInt()
or
result = this.(UnaryPlusExpr).getOperand().getFullyConverted().getValue().toBigInt()
or
result = this.(NotExpr).getOperand().getFullyConverted().getValue().toBigInt().bitNot()
or
exists(BinaryOperation op, QlBuiltins::BigInt left, QlBuiltins::BigInt right |
op = this and
left = op.getLeftOperand().getFullyConverted().getValue().toBigInt() and
right = op.getRightOperand().getFullyConverted().getValue().toBigInt()
|
op instanceof AddExpr and
result = left + right
or
op instanceof SubExpr and
result = left - right
or
op instanceof MulExpr and
result = left * right
or
op instanceof DivExpr and
result = left / right
or
op instanceof RemExpr and
result = left % right
or
op instanceof BitwiseAndExpr and
result = left.bitAnd(right)
or
op instanceof BitwiseOrExpr and
result = left.bitOr(right)
or
op instanceof BitwiseXorExpr and
result = left.bitXor(right)
or
op instanceof RShiftExpr and
result = left.bitShiftRightSigned(right.toInt())
or
op instanceof LShiftExpr and
result = left.bitShiftLeft(right.toInt())
)
}
}
129 changes: 129 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype ConversionsQuery =
TNoConversionFromBoolQuery() or
TNoImplicitBoolConversionQuery() or
TNoCharacterNumericalValueQuery() or
TInappropriateBitwiseOrShiftOperandsQuery() or
TNoSignednessChangeFromPromotionQuery() or
TNumericAssignmentTypeMismatchQuery() or
TFunctionPointerConversionContextQuery()

predicate isConversionsQueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `noConversionFromBool` query
ConversionsPackage::noConversionFromBoolQuery() and
queryId =
// `@id` for the `noConversionFromBool` query
"cpp/misra/no-conversion-from-bool" and
ruleId = "RULE-7-0-1" and
category = "required"
or
query =
// `Query` instance for the `noImplicitBoolConversion` query
ConversionsPackage::noImplicitBoolConversionQuery() and
queryId =
// `@id` for the `noImplicitBoolConversion` query
"cpp/misra/no-implicit-bool-conversion" and
ruleId = "RULE-7-0-2" and
category = "required"
or
query =
// `Query` instance for the `noCharacterNumericalValue` query
ConversionsPackage::noCharacterNumericalValueQuery() and
queryId =
// `@id` for the `noCharacterNumericalValue` query
"cpp/misra/no-character-numerical-value" and
ruleId = "RULE-7-0-3" and
category = "required"
or
query =
// `Query` instance for the `inappropriateBitwiseOrShiftOperands` query
ConversionsPackage::inappropriateBitwiseOrShiftOperandsQuery() and
queryId =
// `@id` for the `inappropriateBitwiseOrShiftOperands` query
"cpp/misra/inappropriate-bitwise-or-shift-operands" and
ruleId = "RULE-7-0-4" and
category = "required"
or
query =
// `Query` instance for the `noSignednessChangeFromPromotion` query
ConversionsPackage::noSignednessChangeFromPromotionQuery() and
queryId =
// `@id` for the `noSignednessChangeFromPromotion` query
"cpp/misra/no-signedness-change-from-promotion" and
ruleId = "RULE-7-0-5" and
category = "required"
or
query =
// `Query` instance for the `numericAssignmentTypeMismatch` query
ConversionsPackage::numericAssignmentTypeMismatchQuery() and
queryId =
// `@id` for the `numericAssignmentTypeMismatch` query
"cpp/misra/numeric-assignment-type-mismatch" and
ruleId = "RULE-7-0-6" and
category = "required"
or
query =
// `Query` instance for the `functionPointerConversionContext` query
ConversionsPackage::functionPointerConversionContextQuery() and
queryId =
// `@id` for the `functionPointerConversionContext` query
"cpp/misra/function-pointer-conversion-context" and
ruleId = "RULE-7-11-3" and
category = "required"
}

module ConversionsPackage {
Query noConversionFromBoolQuery() {
//autogenerate `Query` type
result =
// `Query` type for `noConversionFromBool` query
TQueryCPP(TConversionsPackageQuery(TNoConversionFromBoolQuery()))
}

Query noImplicitBoolConversionQuery() {
//autogenerate `Query` type
result =
// `Query` type for `noImplicitBoolConversion` query
TQueryCPP(TConversionsPackageQuery(TNoImplicitBoolConversionQuery()))
}

Query noCharacterNumericalValueQuery() {
//autogenerate `Query` type
result =
// `Query` type for `noCharacterNumericalValue` query
TQueryCPP(TConversionsPackageQuery(TNoCharacterNumericalValueQuery()))
}

Query inappropriateBitwiseOrShiftOperandsQuery() {
//autogenerate `Query` type
result =
// `Query` type for `inappropriateBitwiseOrShiftOperands` query
TQueryCPP(TConversionsPackageQuery(TInappropriateBitwiseOrShiftOperandsQuery()))
}

Query noSignednessChangeFromPromotionQuery() {
//autogenerate `Query` type
result =
// `Query` type for `noSignednessChangeFromPromotion` query
TQueryCPP(TConversionsPackageQuery(TNoSignednessChangeFromPromotionQuery()))
}

Query numericAssignmentTypeMismatchQuery() {
//autogenerate `Query` type
result =
// `Query` type for `numericAssignmentTypeMismatch` query
TQueryCPP(TConversionsPackageQuery(TNumericAssignmentTypeMismatchQuery()))
}

Query functionPointerConversionContextQuery() {
//autogenerate `Query` type
result =
// `Query` type for `functionPointerConversionContext` query
TQueryCPP(TConversionsPackageQuery(TFunctionPointerConversionContextQuery()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Comments
import Concurrency
import Conditionals
import Const
import Conversions
import DeadCode
import Declarations
import ExceptionSafety
Expand Down Expand Up @@ -67,6 +68,7 @@ newtype TCPPQuery =
TConcurrencyPackageQuery(ConcurrencyQuery q) or
TConditionalsPackageQuery(ConditionalsQuery q) or
TConstPackageQuery(ConstQuery q) or
TConversionsPackageQuery(ConversionsQuery q) or
TDeadCodePackageQuery(DeadCodeQuery q) or
TDeclarationsPackageQuery(DeclarationsQuery q) or
TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or
Expand Down Expand Up @@ -122,6 +124,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isConcurrencyQueryMetadata(query, queryId, ruleId, category) or
isConditionalsQueryMetadata(query, queryId, ruleId, category) or
isConstQueryMetadata(query, queryId, ruleId, category) or
isConversionsQueryMetadata(query, queryId, ruleId, category) or
isDeadCodeQueryMetadata(query, queryId, ruleId, category) or
isDeclarationsQueryMetadata(query, queryId, ruleId, category) or
isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or
Expand Down
19 changes: 1 addition & 18 deletions cpp/common/src/codingstandards/cpp/types/Compatible.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import cpp
import codeql.util.Boolean
import codingstandards.cpp.types.Graph
import codingstandards.cpp.types.Type

module TypeNamesMatchConfig implements TypeEquivalenceSig {
predicate resolveTypedefs() {
Expand Down Expand Up @@ -522,24 +523,6 @@ module FunctionDeclarationTypeEquivalence<
}
}

/**
* Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType`
* don't have a common ancestor.
*/
private class FunctionType extends Type {
FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType }

Type getReturnType() {
result = this.(RoutineType).getReturnType() or
result = this.(FunctionPointerIshType).getReturnType()
}

Type getParameterType(int i) {
result = this.(RoutineType).getParameterType(i) or
result = this.(FunctionPointerIshType).getParameterType(i)
}
}

private class LeafType extends Type {
LeafType() {
not this instanceof DerivedType and
Expand Down
48 changes: 48 additions & 0 deletions cpp/common/src/codingstandards/cpp/types/Type.qll
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,51 @@ int getPrecision(IntegralType type) {
or
type.isExplicitlySigned() and result = type.getSize() * 8 - 1
}

/**
* Determines the lower and upper bounds of an integral type.
*/
predicate integralTypeBounds(IntegralType integralType, QlBuiltins::BigInt lb, QlBuiltins::BigInt ub) {
exists(QlBuiltins::BigInt limit | limit = 2.toBigInt().pow(8 * integralType.getSize()) |
if integralType instanceof BoolType
then lb = 0.toBigInt() and ub = 1.toBigInt()
else
if integralType.isSigned()
then (
lb = -(limit / 2.toBigInt()) and ub = (limit / 2.toBigInt()) - 1.toBigInt()
) else (
lb = 0.toBigInt() and ub = limit - 1.toBigInt()
)
)
}

/**
* The size of the smallest `int` type in the database in bytes.
*/
int sizeOfInt() {
// The size of int is implementation-defined
result =
min(int size |
size = any(IntType i | i.isSigned()).getCanonicalArithmeticType().getSize()
|
size
)
}

/**
* Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType`
* don't have a common ancestor.
*/
class FunctionType extends Type {
FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType }

Type getReturnType() {
result = this.(RoutineType).getReturnType() or
result = this.(FunctionPointerIshType).getReturnType()
}

Type getParameterType(int i) {
result = this.(RoutineType).getParameterType(i) or
result = this.(FunctionPointerIshType).getParameterType(i)
}
}
5 changes: 4 additions & 1 deletion cpp/common/test/includes/standard-library/clocale
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#ifndef _GHLIBCPP_CLOCALE
#define _GHLIBCPP_CLOCALE

#define NULL 0
#define LC_ALL 0
Expand All @@ -15,3 +16,5 @@ using ::lconv;
using ::localeconv;
using ::setlocale;
} // namespace std

#endif // _GHLIBCPP_CLOCALE
23 changes: 23 additions & 0 deletions cpp/common/test/includes/standard-library/cwchar
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef _GHLIBCPP_CWCHAR
#define _GHLIBCPP_CWCHAR

#include "stddef.h"

namespace std {
// Character classification and conversion types
typedef struct {
int __count;
union {
unsigned int __wch;
char __wchb[4];
} __value;
} mbstate_t;

typedef unsigned int wint_t;

// Wide character constants
static const wint_t WEOF = static_cast<wint_t>(-1);

} // namespace std

#endif // _GHLIBCPP_CWCHAR
Loading
Loading