@@ -359,7 +359,7 @@ private module Persistence {
359359 }
360360
361361 /**
362- * Holds if `call` has a keyword argument of with value `value`.
362+ * Holds if `call` has a keyword argument with value `value`.
363363 */
364364 private predicate keywordArgumentWithValue ( DataFlow:: CallNode call , DataFlow:: ExprNode value ) {
365365 exists ( ExprNodes:: PairCfgNode pair | pair = call .getArgument ( _) .asExpr ( ) |
@@ -412,6 +412,28 @@ private module Persistence {
412412 }
413413 }
414414
415+ /**
416+ * A call to `ActiveRecord::Relation#touch_all`, which updates the `updated_at`
417+ * attribute on all records in the relation, setting it to the current time or
418+ * the time specified. If passed additional attribute names, they will also be
419+ * updated with the time.
420+ * Examples:
421+ * ```rb
422+ * Person.all.touch_all
423+ * Person.where(name: "David").touch_all
424+ * Person.all.touch_all(:created_at)
425+ * Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
426+ * ```
427+ */
428+ private class TouchAllCall extends DataFlow:: CallNode , PersistentWriteAccess:: Range {
429+ TouchAllCall ( ) {
430+ exists ( this .asExpr ( ) .getExpr ( ) .( ActiveRecordModelClassMethodCall ) .getReceiverClass ( ) ) and
431+ this .getMethodName ( ) = "touch_all"
432+ }
433+
434+ override DataFlow:: Node getValue ( ) { result = this .getKeywordArgument ( "time" ) }
435+ }
436+
415437 /** A call to e.g. `User.insert_all([{name: "foo"}, {name: "bar"}])` */
416438 private class InsertAllLikeCall extends DataFlow:: CallNode , PersistentWriteAccess:: Range {
417439 private ExprNodes:: ArrayLiteralCfgNode arr ;
0 commit comments