@@ -153,12 +153,39 @@ predicate exprMayThrow(Expr e) {
153
153
)
154
154
}
155
155
156
+ /** The `std::nothrow_t` class and its `bsl` variant. */
157
+ class NoThrowType extends Struct {
158
+ NoThrowType ( ) { this .hasGlobalOrStdOrBslName ( "nothrow_t" ) }
159
+ }
160
+
156
161
/** An allocator that might throw an exception. */
157
162
class ThrowingAllocator extends Function {
158
163
ThrowingAllocator ( ) {
159
164
exists ( NewOrNewArrayExpr newExpr |
160
165
newExpr .getAllocator ( ) = this and
161
- functionMayThrow ( this )
166
+ // Exclude custom overloads of `operator new`.
167
+ // What we really want here is to only include the functions that satisfy `functionMayThrow`, but
168
+ // there seems to be examples where `throw()` isn't extracted (which causes false positives).
169
+ //
170
+ // As noted in the QLDoc for `Function.getAllocatorCall`:
171
+ //
172
+ // "As a rule of thumb, there will be an allocator call precisely when the type
173
+ // being allocated has a custom `operator new`, or when an argument list appears
174
+ // after the `new` keyword and before the name of the type being allocated.
175
+ //
176
+ // In particular note that uses of placement-new and nothrow-new will have an
177
+ // allocator call."
178
+ //
179
+ // So we say an allocator might throw if:
180
+ // 1. It doesn't have a body
181
+ // 2. there isn't a parameter with type `nothrow_t`
182
+ // 3. the allocator isn't marked with `throw()` or `noexcept`.
183
+ not exists ( this .getBlock ( ) ) and
184
+ not exists ( Parameter p | p = this .getAParameter ( ) |
185
+ p .getUnspecifiedType ( ) instanceof NoThrowType
186
+ ) and
187
+ not this .isNoExcept ( ) and
188
+ not this .isNoThrow ( )
162
189
)
163
190
}
164
191
}
0 commit comments