Files
codeql/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb
Harry Maclean ef6f0e5b30 Ruby: Add Improper Memoization query
This query finds cases where a method memoizes its result but fails to
include one or more of its parameters in the memoization key (or doesn't
use memoization keys at all). This can lead to the method returning
incorrect results when subsequently called with different arguments.
2022-06-16 12:44:33 +12:00

104 lines
2.1 KiB
Ruby

# GOOD - Should not trigger CodeQL rule
# No arguments passed to method
def m1
@m1 ||= long_running_method
end
# No arguments passed to method
def m2
@m2 ||= begin
long_running_method
end
end
# OK: argument used in key.
# May be incorrect if arg is `false` or `nil`.
def m3(arg)
@m3 ||= {}
@m3[arg] ||= long_running_method(arg)
end
# OK: both arguments used in key.
# May be incorrect if either arg is `false` or `nil`.
def m4(arg1, arg2)
@m4 ||= {}
@m4[[arg1, arg2]] ||= result(arg1, arg2)
end
# OK: argument used in key.
# Still correct if arg is `false` or `nil`.
def m5(arg)
@m5 ||= Hash.new do |h1, key|
h1[key] = long_running_method(key)
end
@m5[arg]
end
# OK: both arguments used in key.
# Still correct if either arg is `false` or `nil`.
def m6(arg1, arg2)
@m6 ||= Hash.new do |h1, arg1|
h1[arg1] = Hash.new do |h2, arg2|
h2[arg2] = result(arg1, arg2)
end
end
@m6[arg1][arg2]
end
# Bad: method has parameter but only one result is memoized.
def m7(arg) # $result=BAD
@m7 ||= begin
arg += 3
end
@m7
end
# Bad: method has parameter but only one result is memoized.
def m8(arg) # $result=BAD
@m8 ||= begin
long_running_method(arg)
end
@m8
end
# Bad: method has parameter but only one result is memoized.
def m9(arg) # $result=BAD
@m9 ||= long_running_method(arg)
end
# Bad: method has parameter but only one result is memoized.
def m10(arg1, arg2) # $result=BAD
@m10 ||= long_running_method(arg1, arg2)
end
# Bad: `arg2` not used in key.
def m11(arg1, arg2) # $result=BAD
@m11 ||= {}
@m11[arg1] ||= long_running_method(arg1, arg2)
end
# Bad: `arg2` not used in key.
def m12(arg1, arg2) # $result=BAD
@m12 ||= Hash.new do |h1, arg1|
h1[arg1] = result(arg1, arg2)
end
@m12[arg1]
end
# Bad: arg not used in key.
def m13(id:) # $result=BAD
@m13 ||= Rails.cache.fetch("product_sku/#{id}", expires_in: 30.minutes) do
ActiveRecord::Base.transaction do
ProductSku.find_by(id: id)
end
end
@m13
end
# Good (FP): arg is used in key via string interpolation.
def m14(arg)
@m14 ||= {}
key = "foo/#{arg}"
@m14[key] ||= long_running_method(arg)
end