Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IO::Error#target #13865

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions spec/std/file_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ describe "File" do
pending! "Spec cannot run as superuser"
end
{% end %}
expect_raises(File::AccessDeniedError) { File.read(path) }
expect_raises(File::AccessDeniedError, path) { File.read(path) }
end
end
{% end %}
Expand All @@ -946,7 +946,7 @@ describe "File" do
pending! "Spec cannot run as superuser"
end
{% end %}
expect_raises(File::AccessDeniedError) { File.write(path, "foo") }
expect_raises(File::AccessDeniedError, path) { File.write(path, "foo") }
end
end

Expand Down
6 changes: 3 additions & 3 deletions src/crystal/system/unix/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ module Crystal::System::File
sleep 0.1
end
else
flock(op) || raise IO::Error.from_errno("Error applying file lock: file is already locked")
flock(op) || raise IO::Error.from_errno("Error applying file lock: file is already locked", target: self)
end
end

Expand All @@ -251,7 +251,7 @@ module Crystal::System::File
if errno.in?(Errno::EAGAIN, Errno::EWOULDBLOCK)
false
else
raise IO::Error.from_os_error("Error applying or removing file lock", errno)
raise IO::Error.from_os_error("Error applying or removing file lock", errno, target: self)
end
end
end
Expand All @@ -269,7 +269,7 @@ module Crystal::System::File
end

if ret != 0
raise IO::Error.from_errno("Error syncing file")
raise IO::Error.from_errno("Error syncing file", target: self)
end
end
end
10 changes: 5 additions & 5 deletions src/crystal/system/unix/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module Crystal::System::FileDescriptor
evented_read(slice, "Error reading file") do
LibC.read(fd, slice, slice.size).tap do |return_code|
if return_code == -1 && Errno.value == Errno::EBADF
raise IO::Error.new "File not open for reading"
raise IO::Error.new "File not open for reading", target: self
end
end
end
Expand All @@ -22,7 +22,7 @@ module Crystal::System::FileDescriptor
evented_write(slice, "Error writing file") do |slice|
LibC.write(fd, slice, slice.size).tap do |return_code|
if return_code == -1 && Errno.value == Errno::EBADF
raise IO::Error.new "File not open for writing"
raise IO::Error.new "File not open for writing", target: self
end
end
end
Expand Down Expand Up @@ -87,13 +87,13 @@ module Crystal::System::FileDescriptor
seek_value = LibC.lseek(fd, offset, whence)

if seek_value == -1
raise IO::Error.from_errno "Unable to seek"
raise IO::Error.from_errno "Unable to seek", target: self
end
end

private def system_pos
pos = LibC.lseek(fd, 0, IO::Seek::Current).to_i64
raise IO::Error.from_errno "Unable to tell" if pos == -1
raise IO::Error.from_errno("Unable to tell", target: self) if pos == -1
pos
end

Expand Down Expand Up @@ -144,7 +144,7 @@ module Crystal::System::FileDescriptor
when Errno::EINTR, Errno::EINPROGRESS
# ignore
else
raise IO::Error.from_errno("Error closing file")
raise IO::Error.from_errno("Error closing file", target: self)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/crystal/system/win32/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ module Crystal::System::File
if winerror == WinError::ERROR_LOCK_VIOLATION
false
else
raise IO::Error.from_os_error("LockFileEx", winerror)
raise IO::Error.from_os_error("LockFileEx", winerror, target: self)
end
end
end
Expand All @@ -509,7 +509,7 @@ module Crystal::System::File

private def system_fsync(flush_metadata = true) : Nil
if LibC._commit(fd) != 0
raise IO::Error.from_errno("Error syncing file")
raise IO::Error.from_errno("Error syncing file", target: self)
end
end
end
18 changes: 9 additions & 9 deletions src/crystal/system/win32/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ module Crystal::System::FileDescriptor
bytes_read = LibC._read(fd, slice, slice.size)
if bytes_read == -1
if Errno.value == Errno::EBADF
raise IO::Error.new "File not open for reading"
raise IO::Error.new "File not open for reading", target: self
else
raise IO::Error.from_errno("Error reading file")
raise IO::Error.from_errno("Error reading file", target: self)
end
end
bytes_read
Expand All @@ -36,9 +36,9 @@ module Crystal::System::FileDescriptor
bytes_written = LibC._write(fd, slice, slice.size)
if bytes_written == -1
if Errno.value == Errno::EBADF
raise IO::Error.new "File not open for writing"
raise IO::Error.new "File not open for writing", target: self
else
raise IO::Error.from_errno("Error writing file")
raise IO::Error.from_errno("Error writing file", target: self)
end
end
else
Expand Down Expand Up @@ -106,7 +106,7 @@ module Crystal::System::FileDescriptor

if file_type == LibC::FILE_TYPE_UNKNOWN
error = WinError.value
raise IO::Error.from_os_error("Unable to get info", error) unless error == WinError::ERROR_SUCCESS
raise IO::Error.from_os_error("Unable to get info", error, target: self) unless error == WinError::ERROR_SUCCESS
end
end

Expand All @@ -129,13 +129,13 @@ module Crystal::System::FileDescriptor
seek_value = LibC._lseeki64(fd, offset, whence)

if seek_value == -1
raise IO::Error.from_errno "Unable to seek"
raise IO::Error.from_errno "Unable to seek", target: self
end
end

private def system_pos
pos = LibC._lseeki64(fd, 0, IO::Seek::Current)
raise IO::Error.from_errno "Unable to tell" if pos == -1
raise IO::Error.from_errno("Unable to tell", target: self) if pos == -1
pos
end

Expand Down Expand Up @@ -165,7 +165,7 @@ module Crystal::System::FileDescriptor
when Errno::EINTR
# ignore
else
raise IO::Error.from_errno("Error closing file")
raise IO::Error.from_errno("Error closing file", target: self)
end
end
end
Expand Down Expand Up @@ -204,7 +204,7 @@ module Crystal::System::FileDescriptor
if LibC.ReadFile(handle, buffer, buffer.size, out bytes_read, pointerof(overlapped)) == 0
error = WinError.value
return 0_i64 if error == WinError::ERROR_HANDLE_EOF
raise IO::Error.from_os_error "Error reading file", error
raise IO::Error.from_os_error "Error reading file", error, target: self
end

bytes_read.to_i64
Expand Down
14 changes: 8 additions & 6 deletions src/file/error.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ class File < IO::FileDescriptor
end

class File::Error < IO::Error
getter file : String
getter other : String?

def file : String
target.not_nil!
end

private def self.new_from_os_error(message, os_error, **opts)
case os_error
when Errno::ENOENT, WinError::ERROR_FILE_NOT_FOUND, WinError::ERROR_PATH_NOT_FOUND
Expand All @@ -20,6 +23,10 @@ class File::Error < IO::Error
end
end

def initialize(message, *, file : String | Path, @other : String? = nil)
super message, target: file
end

protected def self.build_message(message, *, file : String) : String
"#{message}: '#{file.inspect_unquoted}'"
end
Expand All @@ -38,11 +45,6 @@ class File::Error < IO::Error
end
end
{% end %}

def initialize(message, *, file : String | Path, @other : String? = nil)
@file = file.to_s
super message
end
end

class File::NotFoundError < File::Error
Expand Down
20 changes: 20 additions & 0 deletions src/io/error.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
class IO
class Error < Exception
include SystemError

getter target : String?

protected def self.build_message(message, *, target : File) : String
build_message(message, target: target.path)
end

protected def self.build_message(message, *, target : Nil) : String
message
end

protected def self.build_message(message, *, target) : String
"#{message} (#{target})"
end

def initialize(message : String? = nil, *, target = nil)
@target = target.try(&.to_s)

super message
end
end

# Raised when an `IO` operation times out.
Expand Down
4 changes: 2 additions & 2 deletions src/io/evented.cr
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ module IO::Evented
if Errno.value == Errno::EAGAIN
wait_readable
else
raise IO::Error.from_errno(errno_msg)
raise IO::Error.from_errno(errno_msg, target: self)
end
end
ensure
Expand All @@ -79,7 +79,7 @@ module IO::Evented
if Errno.value == Errno::EAGAIN
wait_writable
else
raise IO::Error.from_errno(errno_msg)
raise IO::Error.from_errno(errno_msg, target: self)
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions src/io/overlapped.cr
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,9 @@ module IO::Overlapped
when .error_io_pending?
# the operation is running asynchronously; do nothing
when .error_access_denied?
raise IO::Error.new "File not open for #{writing ? "writing" : "reading"}"
raise IO::Error.new "File not open for #{writing ? "writing" : "reading"}", target: self
else
raise IO::Error.from_os_error(method, error)
raise IO::Error.from_os_error(method, error, target: self)
end
else
operation.synchronous = true
Expand Down Expand Up @@ -224,7 +224,7 @@ module IO::Overlapped
when .wsa_io_pending?
# the operation is running asynchronously; do nothing
else
raise IO::Error.from_os_error(method, error)
raise IO::Error.from_os_error(method, error, target: self)
end
else
operation.synchronous = true
Expand Down
Loading