class RuboCop::Cop::Lint::NonAtomicFileOperation


FileUtils.rm_f(path)
# good - atomic and idempotent removal
end
FileUtils.remove(path)
if File.exist?(path)
# bad - race condition with another process may result in an error in ‘remove`
FileUtils.mkdir_p(path)
# good - atomic and idempotent creation
end
FileUtils.mkdir(path)
unless Dir.exist?(path)
# bad - race condition with another process may result in an error in `mkdir`
@example
to be strictly equivalent to that before the replacement.
The atomic processing of the replacement destination is not guaranteed
This cop is unsafe, because autocorrection change to atomic processing.
@safety
but someone else deleted it shortly afterwards.
when the directory existed at the time of `exist?`,
Subsequent processes are executed without the directory that should be there
but someone else created it before `mkdir` was executed.
An exception occurs when the directory didn’t exist at the time of ‘exist?`,
For examples: creating a directory if there is none, has the following problems
such as test runs with parallel_rspec.
especially in cases of frequent file operations in parallel,
These can cause problems that are difficult to reproduce,
And then replace it with a nearly equivalent and atomic method.
Checks for non-atomic file operation.

def allowable_use_with_if?(if_node)

def allowable_use_with_if?(if_node)
  if_node.condition.and_type? || if_node.condition.or_type? || if_node.else_branch
end

def autocorrect(corrector, node, range)

def autocorrect(corrector, node, range)
  corrector.remove(range)
  autocorrect_replace_method(corrector, node)
  if node.parent.modifier_form?
    corrector.remove(node.source_range.end.join(node.parent.loc.keyword.begin))
  else
    corrector.remove(node.parent.loc.end)
  end
end

def autocorrect_replace_method(corrector, node)

def autocorrect_replace_method(corrector, node)
  return if force_method?(node)
  corrector.replace(node.child_nodes.first.loc.name, 'FileUtils')
  corrector.replace(node.loc.selector, replacement_method(node))
end

def force_method?(node)

def force_method?(node)
  force_method_name?(node) || force_option?(node)
end

def force_method_name?(node)

def force_method_name?(node)
  (MAKE_FORCE_METHODS + REMOVE_FORCE_METHODS).include?(node.method_name)
end

def force_option?(node)

def force_option?(node)
  node.arguments.any? { |arg| force?(arg) }
end

def if_node_child?(node)

def if_node_child?(node)
  return false unless (parent = node.parent)
  parent.if_type? && !allowable_use_with_if?(parent)
end

def message_change_force_method(node)

def message_change_force_method(node)
  format(MSG_CHANGE_FORCE_METHOD, method_name: replacement_method(node))
end

def message_remove_file_exist_check(node)

def message_remove_file_exist_check(node)
  receiver, method_name = receiver_and_method_name(node)
  format(MSG_REMOVE_FILE_EXIST_CHECK, receiver: receiver, method_name: method_name)
end

def on_send(node)

def on_send(node)
  return unless if_node_child?(node)
  return if explicit_not_force?(node)
  return unless (exist_node = send_exist_node(node.parent).first)
  return unless exist_node.first_argument == node.first_argument
  register_offense(node, exist_node)
end

def register_offense(node, exist_node)

def register_offense(node, exist_node)
  add_offense(node, message: message_change_force_method(node)) unless force_method?(node)
  parent = node.parent
  range = parent.loc.keyword.begin.join(parent.condition.source_range.end)
  add_offense(range, message: message_remove_file_exist_check(exist_node)) do |corrector|
    autocorrect(corrector, node, range) unless parent.elsif?
  end
end

def replacement_method(node)

def replacement_method(node)
  if MAKE_METHODS.include?(node.method_name)
    'mkdir_p'
  elsif REMOVE_METHODS.include?(node.method_name)
    'rm_f'
  elsif RECURSIVE_REMOVE_METHODS.include?(node.method_name)
    'rm_rf'
  else
    node.method_name
  end
end