@@ -153,12 +153,39 @@ predicate exprMayThrow(Expr e) {
153153 )
154154}
155155
156+ /** The `std::nothrow_t` class and its `bsl` variant. */
157+ class NoThrowType extends Struct {
158+ NoThrowType ( ) { this .hasGlobalOrStdOrBslName ( "nothrow_t" ) }
159+ }
160+
156161/** An allocator that might throw an exception. */
157162class ThrowingAllocator extends Function {
158163 ThrowingAllocator ( ) {
159164 exists ( NewOrNewArrayExpr newExpr |
160165 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 ( )
162189 )
163190 }
164191}
0 commit comments