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