|
| 1 | +/** |
| 2 | + * @id cpp/misra/non-static-member-not-init-before-use |
| 3 | + * @name RULE-15-1-4: All direct, non-static data members of a class should be initialized before the class object is accessible |
| 4 | + * @description Explicit initialization of all non-static data members reduces the risk of an |
| 5 | + * invalid state existing after successful construction. |
| 6 | + * @kind problem |
| 7 | + * @precision high |
| 8 | + * @problem.severity warning |
| 9 | + * @tags external/misra/id/rule-15-1-4 |
| 10 | + * correctness |
| 11 | + * scope/single-translation-unit |
| 12 | + * external/misra/enforcement/decidable |
| 13 | + * external/misra/obligation/advisory |
| 14 | + */ |
| 15 | + |
| 16 | +import cpp |
| 17 | +import codingstandards.cpp.misra |
| 18 | +import codingstandards.cpp.types.TrivialType |
| 19 | + |
| 20 | +/** |
| 21 | + * A type needs initialization if it is: |
| 22 | + * - a scalar type |
| 23 | + * - an array of types that need initialization |
| 24 | + * - an aggregate class with a field that needs initialization |
| 25 | + */ |
| 26 | +private predicate needsInitialization(Type t) { |
| 27 | + isScalarType(t) |
| 28 | + or |
| 29 | + needsInitialization(t.getUnspecifiedType().(ArrayType).getBaseType()) |
| 30 | + or |
| 31 | + t instanceof RelevantAggregate |
| 32 | +} |
| 33 | + |
| 34 | +/** |
| 35 | + * An aggregate must be validated at construction time if it has a field that needs initialization. |
| 36 | + */ |
| 37 | +class RelevantAggregate extends Class { |
| 38 | + CheckedField f; |
| 39 | + |
| 40 | + RelevantAggregate() { |
| 41 | + isAggregateClass(this) and |
| 42 | + f = getAField() |
| 43 | + } |
| 44 | + |
| 45 | + CheckedField getField() { result = f } |
| 46 | +} |
| 47 | + |
| 48 | +/** |
| 49 | + * A field must be checked for initialization if its type needs initialization, is not static, and |
| 50 | + * it does not have a default member initializer. |
| 51 | + */ |
| 52 | +class CheckedField extends Field { |
| 53 | + CheckedField() { |
| 54 | + this instanceof Field and |
| 55 | + needsInitialization(this.getUnspecifiedType()) and |
| 56 | + not exists(this.getInitializer()) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +/** |
| 61 | + * Holds if `f` is initialized in constructor `ctor` via the member initialization list |
| 62 | + * or has a default member initializer (NSDMI). |
| 63 | + */ |
| 64 | +predicate ctorInitializesCheckedField(Constructor ctor, CheckedField f) { |
| 65 | + // Field appears in the member initialization list |
| 66 | + exists(ConstructorFieldInit init | |
| 67 | + init = ctor.getAnInitializer() and |
| 68 | + init.getTarget() = f and |
| 69 | + not init.isCompilerGenerated() |
| 70 | + ) |
| 71 | +} |
| 72 | + |
| 73 | +/** |
| 74 | + * Represents an AST element that does not initialize a non-static data member that requires initialization. |
| 75 | + * |
| 76 | + * This may be a constructor definition, or an aggregate creation, etc. |
| 77 | + */ |
| 78 | +abstract class IncompleteInitialization extends Element { |
| 79 | + abstract CheckedField getField(); |
| 80 | + |
| 81 | + abstract string getKindStr(); |
| 82 | +} |
| 83 | + |
| 84 | +/** |
| 85 | + * A constructor that does not initialize a field that requires initialization. |
| 86 | + * |
| 87 | + * Non-aggregate class constructors must either: |
| 88 | + * - belong to an aggregate class, or |
| 89 | + * - delegate to another constructor in the same class, or |
| 90 | + * - initialize all fields that require initialization, or |
| 91 | + * - be a defaulted move/copy constructor (which we assume satisfies the above) |
| 92 | + * |
| 93 | + * Constructors that don't meet these criteria are non-compliant. |
| 94 | + */ |
| 95 | +class IncompleteConstructor extends Constructor, IncompleteInitialization { |
| 96 | + CheckedField checkedField; |
| 97 | + |
| 98 | + IncompleteConstructor() { |
| 99 | + checkedField = this.getDeclaringType().getAField() and |
| 100 | + not ctorInitializesCheckedField(this, checkedField) and |
| 101 | + // aggregate classes are allowed and do not initialize members |
| 102 | + not isAggregateClass(getDeclaringType()) and |
| 103 | + this.getDeclaringType().hasDefinition() and |
| 104 | + not this.isDeleted() and |
| 105 | + // Delegating constructors do not need to initialize members |
| 106 | + not any(ConstructorDelegationInit init) = this.getAnInitializer() and |
| 107 | + // exclude defaulted move and copy constructors. |
| 108 | + not ( |
| 109 | + ( |
| 110 | + this.isDefaulted() or |
| 111 | + this.isCompilerGenerated() |
| 112 | + ) and |
| 113 | + ( |
| 114 | + this instanceof MoveConstructor or |
| 115 | + this instanceof CopyConstructor |
| 116 | + ) |
| 117 | + ) |
| 118 | + } |
| 119 | + |
| 120 | + override CheckedField getField() { result = checkedField } |
| 121 | + |
| 122 | + override string getKindStr() { result = "Constructor" } |
| 123 | +} |
| 124 | + |
| 125 | +/** |
| 126 | + * A using declaration that introduces a base class constructor and skips initialization of a field. |
| 127 | + * |
| 128 | + * A declaration `using BaseClass::BaseClass;` in a derived class allows the derived class to |
| 129 | + * inherit the base class constructors. These will not initialize any fields declared in the derived |
| 130 | + * class, so if any checked field exists, the using declaration is non-compliant. |
| 131 | + */ |
| 132 | +class UsingBaseConstructor extends UsingDeclarationEntry, IncompleteInitialization { |
| 133 | + Class baseClass; |
| 134 | + Class containerClass; |
| 135 | + CheckedField checkedField; |
| 136 | + |
| 137 | + UsingBaseConstructor() { |
| 138 | + getEnclosingElement() = containerClass and |
| 139 | + baseClass = getDeclaration() and |
| 140 | + checkedField = containerClass.getAField() |
| 141 | + } |
| 142 | + |
| 143 | + override CheckedField getField() { result = checkedField } |
| 144 | + |
| 145 | + override string getKindStr() { result = "Using declaration with base constructor" } |
| 146 | +} |
| 147 | + |
| 148 | +/** |
| 149 | + * Handles the scenarios where an aggregate may be initialized by value based on a type. |
| 150 | + * |
| 151 | + * For instance, `const Agg`, `Agg[]`, and `Agg[][]` will be initialized by value, but `Agg*` and |
| 152 | + * `Agg&` will not. This is important to verify that aggregates are properly initialized |
| 153 | + */ |
| 154 | +predicate typeContainsAggregate(Type t, RelevantAggregate aggregate) { |
| 155 | + t.getUnderlyingType() = aggregate |
| 156 | + or |
| 157 | + not t.getUnderlyingType() instanceof RelevantAggregate and |
| 158 | + typeContainsAggregate(t.getUnderlyingType().(ArrayType).getBaseType(), aggregate) |
| 159 | +} |
| 160 | + |
| 161 | +/** |
| 162 | + * A declaration of an aggregate that does not initialize necessary fields. |
| 163 | + * |
| 164 | + * By rule, aggregates are checked at construction time, rather than non-aggregates which are |
| 165 | + * checked at constructor definition. |
| 166 | + * |
| 167 | + * A declaration of `aggregate agg;` does not zero-initialize members, and may be non-compliant, |
| 168 | + * while `aggregate agg{};` does zero-initialize members and is compliant. |
| 169 | + * |
| 170 | + * Note that `aggregate agg;` as a member of an aggregate class is compliant, and as a member of a |
| 171 | + * non-aggregate class will be checked at the outer class constructor definition. |
| 172 | + */ |
| 173 | +class IncompleteAggregateInit extends Variable, IncompleteInitialization { |
| 174 | + RelevantAggregate aggregate; |
| 175 | + |
| 176 | + IncompleteAggregateInit() { |
| 177 | + typeContainsAggregate(getType(), aggregate) and |
| 178 | + // agg{} is allowed, and agg; is not. |
| 179 | + not this.hasInitializer() and |
| 180 | + // Aggregate members may be initialized by constructor or belong to another aggregate. |
| 181 | + not this instanceof MemberVariable |
| 182 | + } |
| 183 | + |
| 184 | + override CheckedField getField() { result = aggregate.getField() } |
| 185 | + |
| 186 | + override string getKindStr() { result = "Aggregate variable" } |
| 187 | +} |
| 188 | + |
| 189 | +/** |
| 190 | + * An aggregate created by a new or new[] expression that does not initialize necessary fields. |
| 191 | + * |
| 192 | + * For examyple, `new Aggregate;` does not zero-initialize members and may be non-compliant, while |
| 193 | + * `new Aggregate{};` does zero-initialize members and is compliant. |
| 194 | + */ |
| 195 | +class IncompleteAggregateNew extends NewOrNewArrayExpr, IncompleteInitialization { |
| 196 | + RelevantAggregate aggregate; |
| 197 | + |
| 198 | + IncompleteAggregateNew() { |
| 199 | + typeContainsAggregate(this.getAllocatedType().getUnspecifiedType(), aggregate) and |
| 200 | + not exists(getAChild()) |
| 201 | + } |
| 202 | + |
| 203 | + override CheckedField getField() { result = aggregate.getField() } |
| 204 | + |
| 205 | + override string getKindStr() { result = "Aggregate new expression" } |
| 206 | +} |
| 207 | + |
| 208 | +from IncompleteInitialization init, CheckedField f |
| 209 | +where |
| 210 | + not isExcluded(init, Classes4Package::nonStaticMemberNotInitBeforeUseQuery()) and |
| 211 | + f = init.getField() |
| 212 | +select init, init.getKindStr() + " does not initialize non-static data member $@", f, f.getName() |
0 commit comments