diff --git a/core/file.rbs b/core/file.rbs
index 06e084d94..a0c94094b 100644
--- a/core/file.rbs
+++ b/core/file.rbs
@@ -810,6 +810,30 @@
# * #truncate: Truncates `self` to the given size.
#
class File < IO
+ # The string that `File.ftype` and `File::Stat#ftype` return
+ type ftype = 'file' | 'directory' | 'characterSpecial' | 'blockSpecial'
+ | 'fifo' | 'link' | 'socket' | 'unknown'
+
+ #
+ # platform specific alternative separator
+ #
+ ALT_SEPARATOR: String?
+
+ #
+ # path list separator
+ #
+ PATH_SEPARATOR: String
+
+ #
+ # separates directory parts in path
+ #
+ SEPARATOR: String
+
+ #
+ # separates directory parts in path
+ #
+ Separator: String
+
#
-# platform specific alternative separator
-#
-File::ALT_SEPARATOR: String?
-
-#
-# path list separator
-#
-File::PATH_SEPARATOR: String
-
-#
-# separates directory parts in path
-#
-File::SEPARATOR: String
-
-#
-# separates directory parts in path
-#
-File::Separator: String
-
-#
-# Module File::Constants defines file-related constants.
-#
-# There are two families of constants here:
-#
-# * Those having to do with [file
-# access](rdoc-ref:File::Constants@File+Access).
-# * Those having to do with [filename
-# globbing](rdoc-ref:File::Constants@Filename+Globbing+Constants+-28File-3A-
-# 3AFNM_-2A-29).
-#
-# File constants defined for the local process may be retrieved with method
-# File::Constants.constants:
-#
-# File::Constants.constants.take(5)
-# # => [:RDONLY, :WRONLY, :RDWR, :APPEND, :CREAT]
-#
-# ## File Access
-#
-# File-access constants may be used with optional argument `mode` in calls to
-# the following methods:
-#
-# * File.new.
-# * File.open.
-# * IO.for_fd.
-# * IO.new.
-# * IO.open.
-# * IO.popen.
-# * IO.reopen.
-# * IO.sysopen.
-# * StringIO.new.
-# * StringIO.open.
-# * StringIO#reopen.
-#
-# ### Read/Write Access
-#
-# Read-write access for a stream may be specified by a file-access constant.
-#
-# The constant may be specified as part of a bitwise OR of other such constants.
-#
-# Any combination of the constants in this section may be specified.
-#
-# #### File::RDONLY
-#
-# Flag File::RDONLY specifies the stream should be opened for reading only:
-#
-# filepath = '/tmp/t.tmp'
-# f = File.new(filepath, File::RDONLY)
-# f.write('Foo') # Raises IOError (not opened for writing).
-#
-# #### File::WRONLY
-#
-# Flag File::WRONLY specifies that the stream should be opened for writing only:
-#
-# f = File.new(filepath, File::WRONLY)
-# f.read # Raises IOError (not opened for reading).
-#
-# #### File::RDWR
-#
-# Flag File::RDWR specifies that the stream should be opened for both reading
-# and writing:
-#
-# f = File.new(filepath, File::RDWR)
-# f.write('Foo') # => 3
-# f.rewind # => 0
-# f.read # => "Foo"
-#
-# ### File Positioning
-#
-# #### File::APPEND
-#
-# Flag File::APPEND specifies that the stream should be opened in append mode.
-#
-# Before each write operation, the position is set to end-of-stream. The
-# modification of the position and the following write operation are performed
-# as a single atomic step.
-#
-# #### File::TRUNC
-#
-# Flag File::TRUNC specifies that the stream should be truncated at its
-# beginning. If the file exists and is successfully opened for writing, it is to
-# be truncated to position zero; its ctime and mtime are updated.
-#
-# There is no effect on a FIFO special file or a terminal device. The effect on
-# other file types is implementation-defined. The result of using File::TRUNC
-# with File::RDONLY is undefined.
-#
-# ### Creating and Preserving
-#
-# #### File::CREAT
-#
-# Flag File::CREAT specifies that the stream should be created if it does not
-# already exist.
-#
-# If the file exists:
-#
-# - Raise an exception if File::EXCL is also specified.
-# - Otherwise, do nothing.
-#
-# If the file does not exist, then it is created. Upon successful completion,
-# the atime, ctime, and mtime of the file are updated, and the ctime and mtime
-# of the parent directory are updated.
-#
-# #### File::EXCL
-#
-# Flag File::EXCL specifies that the stream should not already exist; If flags
-# File::CREAT and File::EXCL are both specified and the stream already exists,
-# an exception is raised.
-#
-# The check for the existence and creation of the file is performed as an atomic
-# operation.
-#
-# If both File::EXCL and File::CREAT are specified and the path names a symbolic
-# link, an exception is raised regardless of the contents of the symbolic link.
-#
-# If File::EXCL is specified and File::CREAT is not specified, the result is
-# undefined.
-#
-# ### POSIX File Constants
-#
-# Some file-access constants are defined only on POSIX-compliant systems; those
-# are:
-#
-# * File::SYNC.
-# * File::DSYNC.
-# * File::RSYNC.
-# * File::DIRECT.
-# * File::NOATIME.
-# * File::NOCTTY.
-# * File::NOFOLLOW.
-# * File::TMPFILE.
-#
-# #### File::SYNC, File::RSYNC, and File::DSYNC
-#
-# Flag File::SYNC, File::RSYNC, or File::DSYNC specifies synchronization of I/O
-# operations with the underlying file system.
-#
-# These flags are valid only for POSIX-compliant systems.
-#
-# * File::SYNC specifies that all write operations (both data and metadata)
-# are immediately to be flushed to the underlying storage device. This means
-# that the data is written to the storage device, and the file's metadata
-# (e.g., file size, timestamps, permissions) are also synchronized. This
-# guarantees that data is safely stored on the storage medium before
-# returning control to the calling program. This flag can have a significant
-# impact on performance since it requires synchronous writes, which can be
-# slower compared to asynchronous writes.
-#
-# * File::RSYNC specifies that any read operations on the file will not return
-# until all outstanding write operations (those that have been issued but
-# not completed) are also synchronized. This is useful when you want to read
-# the most up-to-date data, which may still be in the process of being
-# written.
-#
-# * File::DSYNC specifies that all *data* write operations are immediately to
-# be flushed to the underlying storage device; this differs from File::SYNC,
-# which requires that *metadata* also be synchronized.
-#
-# Note that the behavior of these flags may vary slightly depending on the
-# operating system and filesystem being used. Additionally, using these flags
-# can have an impact on performance due to the synchronous nature of the I/O
-# operations, so they should be used judiciously, especially in
-# performance-critical applications.
-#
-# #### File::NOCTTY
-#
-# Flag File::NOCTTY specifies that if the stream is a terminal device, that
-# device does not become the controlling terminal for the process.
-#
-# Defined only for POSIX-compliant systems.
-#
-# #### File::DIRECT
-#
-# Flag File::DIRECT requests that cache effects of the I/O to and from the
-# stream be minimized.
-#
-# Defined only for POSIX-compliant systems.
-#
-# #### File::NOATIME
-#
-# Flag File::NOATIME specifies that act of opening the stream should not modify
-# its access time (atime).
-#
-# Defined only for POSIX-compliant systems.
-#
-# #### File::NOFOLLOW
-#
-# Flag File::NOFOLLOW specifies that if path is a symbolic link, it should not
-# be followed.
-#
-# Defined only for POSIX-compliant systems.
-#
-# #### File::TMPFILE
-#
-# Flag File::TMPFILE specifies that the opened stream should be a new temporary
-# file.
-#
-# Defined only for POSIX-compliant systems.
-#
-# ### Other File-Access Constants
-#
-# #### File::NONBLOCK
-#
-# When possible, the file is opened in nonblocking mode. Neither the open
-# operation nor any subsequent I/O operations on the file will cause the calling
-# process to wait.
-#
-# #### File::BINARY
-#
-# Flag File::BINARY specifies that the stream is to be accessed in binary mode.
-#
-# #### File::SHARE_DELETE
-#
-# Flag File::SHARE_DELETE enables other processes to open the stream with delete
-# access.
-#
-# Windows only.
-#
-# If the stream is opened for (local) delete access without File::SHARE_DELETE,
-# and another process attempts to open it with delete access, the attempt fails
-# and the stream is not opened for that process.
-#
-# ## Locking
-#
-# Four file constants relate to stream locking; see File#flock:
-#
-# #### File::LOCK_EX
-#
-# Flag File::LOCK_EX specifies an exclusive lock; only one process a a time may
-# lock the stream.
-#
-# #### File::LOCK_NB
-#
-# Flag File::LOCK_NB specifies non-blocking locking for the stream; may be
-# combined with File::LOCK_EX or File::LOCK_SH.
-#
-# #### File::LOCK_SH
-#
-# Flag File::LOCK_SH specifies that multiple processes may lock the stream at
-# the same time.
-#
-# #### File::LOCK_UN
-#
-# Flag File::LOCK_UN specifies that the stream is not to be locked.
-#
-# ## Filename Globbing Constants (File::FNM_*)
-#
-# Filename-globbing constants may be used with optional argument `flags` in
-# calls to the following methods:
-#
-# * Dir.glob.
-# * File.fnmatch.
-# * Pathname#fnmatch.
-# * Pathname.glob.
-# * Pathname#glob.
-#
-# The constants are:
-#
-# #### File::FNM_CASEFOLD
-#
-# Flag File::FNM_CASEFOLD makes patterns case insensitive for File.fnmatch (but
-# not Dir.glob).
-#
-# #### File::FNM_DOTMATCH
-#
-# Flag File::FNM_DOTMATCH makes the '*' pattern match a filename
-# starting with '.'.
-#
-# #### File::FNM_EXTGLOB
-#
-# Flag File::FNM_EXTGLOB enables pattern '{_a_,_b_}', which matches
-# pattern '*a*' and pattern '*b*'; behaves like a [regexp
-# union](rdoc-ref:Regexp.union) (e.g., '(?:_a_|_b_)'):
-#
-# pattern = '{LEGAL,BSDL}'
-# Dir.glob(pattern) # => ["LEGAL", "BSDL"]
-# Pathname.glob(pattern) # => [#, #]
-# pathname.glob(pattern) # => [#, #]
-#
-# #### File::FNM_NOESCAPE
-#
-# Flag File::FNM_NOESCAPE disables '\' escaping.
-#
-# #### File::FNM_PATHNAME
-#
-# Flag File::FNM_PATHNAME specifies that patterns '*' and
-# '?' do not match the directory separator (the value of constant
-# File::SEPARATOR).
-#
-# #### File::FNM_SHORTNAME
-#
-# Flag File::FNM_SHORTNAME allows patterns to match short names if they exist.
-#
-# Windows only.
-#
-# #### File::FNM_SYSCASE
-#
-# Flag File::FNM_SYSCASE specifies that case sensitivity is the same as in the
-# underlying operating system; effective for File.fnmatch, but not Dir.glob.
-#
-# ## Other Constants
-#
-# #### File::NULL
-#
-# Flag File::NULL contains the string value of the null device:
-#
-# * On a Unix-like OS, '/dev/null'.
-# * On Windows, 'NUL'.
-#
-module File::Constants
-end
-
-#
-# [File::APPEND](rdoc-ref:File::Constants@File-3A-3AAPPEND)
-#
-File::Constants::APPEND: Integer
-
-#
-# [File::BINARY](rdoc-ref:File::Constants@File-3A-3ABINARY)
-#
-File::Constants::BINARY: Integer
-
-#
-# [File::CREAT](rdoc-ref:File::Constants@File-3A-3ACREAT)
-#
-File::Constants::CREAT: Integer
-
-#
-# [File::DIRECT](rdoc-ref:File::Constants@File-3A-3ADIRECT)
-#
-File::Constants::DIRECT: Integer
-
-#
-# [File::DSYNC](rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+an
-# d+File-3A-3ADSYNC)
-#
-File::Constants::DSYNC: Integer
-
-#
-# [File::EXCL](rdoc-ref:File::Constants@File-3A-3AEXCL)
-#
-File::Constants::EXCL: Integer
-
-#
-# [File::FNM_CASEFOLD](rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD)
-#
-File::Constants::FNM_CASEFOLD: Integer
-
-#
-# [File::FNM_DOTMATCH](rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH)
-#
-File::Constants::FNM_DOTMATCH: Integer
-
-#
-# [File::FNM_EXTGLOB](rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB)
-#
-File::Constants::FNM_EXTGLOB: Integer
-
-#
-# [File::FNM_NOESCAPE](rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE)
-#
-File::Constants::FNM_NOESCAPE: Integer
-
-#
-# [File::FNM_PATHNAME](rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME)
-#
-File::Constants::FNM_PATHNAME: Integer
-
-#
-# [File::FNM_SHORTNAME](rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME)
-#
-File::Constants::FNM_SHORTNAME: Integer
-
-#
-# [File::FNM_SYSCASE](rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE)
-#
-File::Constants::FNM_SYSCASE: Integer
-
-#
-# [File::LOCK_EX](rdoc-ref:File::Constants@File-3A-3ALOCK_EX)
-#
-File::Constants::LOCK_EX: Integer
-
-#
-# [File::LOCK_NB](rdoc-ref:File::Constants@File-3A-3ALOCK_NB)
-#
-File::Constants::LOCK_NB: Integer
-
-#
-# [File::LOCK_SH](rdoc-ref:File::Constants@File-3A-3ALOCK_SH)
-#
-File::Constants::LOCK_SH: Integer
-
-#
-# [File::LOCK_UN](rdoc-ref:File::Constants@File-3A-3ALOCK_UN)
-#
-File::Constants::LOCK_UN: Integer
-
-#
-# [File::NOATIME](rdoc-ref:File::Constants@File-3A-3ANOATIME)
-#
-File::Constants::NOATIME: Integer
-
-#
-# [File::NOCTTY](rdoc-ref:File::Constants@File-3A-3ANOCTTY)
-#
-File::Constants::NOCTTY: Integer
-
-#
-# [File::NOFOLLOW](rdoc-ref:File::Constants@File-3A-3ANOFOLLOW)
-#
-File::Constants::NOFOLLOW: Integer
-
-#
-# [File::NONBLOCK](rdoc-ref:File::Constants@File-3A-3ANONBLOCK)
-#
-File::Constants::NONBLOCK: Integer
-
-#
-# [File::NULL](rdoc-ref:File::Constants@File-3A-3ANULL)
-#
-File::Constants::NULL: String
-
-#
-# [File::RDONLY](rdoc-ref:File::Constants@File-3A-3ARDONLY)
-#
-File::Constants::RDONLY: Integer
-
-#
-# [File::RDWR](rdoc-ref:File::Constants@File-3A-3ARDWR)
-#
-File::Constants::RDWR: Integer
-
-#
-# [File::RSYNC](rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+an
-# d+File-3A-3ADSYNC)
-#
-File::Constants::RSYNC: Integer
-
-#
-# [File::SHARE_DELETE](rdoc-ref:File::Constants@File-3A-3ASHARE_DELETE)
-#
-File::Constants::SHARE_DELETE: Integer
-
-#
-# [File::SYNC](rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and
-# +File-3A-3ADSYNC)
-#
-File::Constants::SYNC: Integer
-
-#
-# [File::TMPFILE](rdoc-ref:File::Constants@File-3A-3ATMPFILE)
-#
-File::Constants::TMPFILE: Integer
-
-#
-# [File::TRUNC](rdoc-ref:File::Constants@File-3A-3ATRUNC)
-#
-File::Constants::TRUNC: Integer
-
-#
-# [File::WRONLY](rdoc-ref:File::Constants@File-3A-3AWRONLY)
-#
-File::Constants::WRONLY: Integer
-
-#
-# Objects of class File::Stat encapsulate common status information for File
-# objects. The information is recorded at the moment the File::Stat object is
-# created; changes made to the file after that point will not be reflected.
-# File::Stat objects are returned by IO#stat, File::stat, File#lstat, and
-# File::lstat. Many of these methods return platform-specific values, and not
-# all values are meaningful on all systems. See also Kernel#test.
-#
-class File::Stat < Object
- include Comparable
-
- #
- # Create a File::Stat object for the given file name (raising an exception if
- # the file doesn't exist).
- #
- def initialize: (String file) -> void
-
- #
- # Compares `self` and `other`, by comparing their modification times; that is,
- # by comparing self.mtime and other.mtime.
- #
- # Returns:
- #
- # * -1, if self.mtime is earlier.
- # * `0`, if the two values are equal.
- # * `1`, if self.mtime is later.
- # * `nil`, if `other` is not a File::Stat object.
- #
- # Examples:
- #
- # stat0 = File.stat('README.md')
- # stat1 = File.stat('NEWS.md')
- # stat0.mtime # => 2025-12-20 15:33:05.6972341 -0600
- # stat1.mtime # => 2025-12-20 16:02:08.2672945 -0600
- # stat0 <=> stat1 # => -1
- # stat0 <=> stat0.dup # => 0
- # stat1 <=> stat0 # => 1
- # stat0 <=> :foo # => nil
- #
- # Class File::Stat includes module Comparable, each of whose methods uses
- # File::Stat#<=> for comparison.
- #
- def <=>: (File::Stat other) -> Integer
- | (untyped) -> nil
-
- #
- # Returns the last access time for this file as an object of class Time.
- #
- # File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
- #
- def atime: () -> Time
-
- #
- # Returns the birth time for *stat*.
- #
- # If the platform doesn't have birthtime, raises NotImplementedError.
- #
- # File.write("testfile", "foo")
- # sleep 10
- # File.write("testfile", "bar")
- # sleep 10
- # File.chmod(0644, "testfile")
- # sleep 10
- # File.read("testfile")
- # File.stat("testfile").birthtime #=> 2014-02-24 11:19:17 +0900
- # File.stat("testfile").mtime #=> 2014-02-24 11:19:27 +0900
- # File.stat("testfile").ctime #=> 2014-02-24 11:19:37 +0900
- # File.stat("testfile").atime #=> 2014-02-24 11:19:47 +0900
- #
- def birthtime: () -> Time
-
- #
- # Returns the native file system's block size. Will return `nil` on platforms
- # that don't support this information.
- #
- # File.stat("testfile").blksize #=> 4096
- #
- def blksize: () -> Integer?
-
- #
- # Returns `true` if the file is a block device, `false` if it isn't or if the
- # operating system doesn't support this feature.
- #
- # File.stat("testfile").blockdev? #=> false
- # File.stat("/dev/hda1").blockdev? #=> true
- #
- def blockdev?: () -> bool
-
- #
- # Returns the number of native file system blocks allocated for this file, or
- # `nil` if the operating system doesn't support this feature.
- #
- # File.stat("testfile").blocks #=> 2
- #
- def blocks: () -> Integer?
-
- #
- # Returns `true` if the file is a character device, `false` if it isn't or if
- # the operating system doesn't support this feature.
- #
- # File.stat("/dev/tty").chardev? #=> true
- #
- def chardev?: () -> bool
-
- #
- # Returns the change time for *stat* (that is, the time directory information
- # about the file was changed, not the file itself).
- #
- # Note that on Windows (NTFS), returns creation time (birth time).
- #
- # File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
- #
- def ctime: () -> Time
-
- #
- # Returns an integer representing the device on which *stat* resides.
- #
- # File.stat("testfile").dev #=> 774
- #
- def dev: () -> Integer
-
- #
- # Returns the major part of File_Stat#dev or `nil`.
- #
- # File.stat("/dev/fd1").dev_major #=> 2
- # File.stat("/dev/tty").dev_major #=> 5
- #
- def dev_major: () -> Integer
-
- #
- # Returns the minor part of File_Stat#dev or `nil`.
- #
- # File.stat("/dev/fd1").dev_minor #=> 1
- # File.stat("/dev/tty").dev_minor #=> 0
- #
- def dev_minor: () -> Integer
-
- #
- # Returns `true` if *stat* is a directory, `false` otherwise.
- #
- # File.stat("testfile").directory? #=> false
- # File.stat(".").directory? #=> true
- #
- def directory?: () -> bool
-
- #
- # Returns `true` if *stat* is executable or if the operating system doesn't
- # distinguish executable files from nonexecutable files. The tests are made
- # using the effective owner of the process.
- #
- # File.stat("testfile").executable? #=> false
- #
- def executable?: () -> bool
-
- #
- # Same as executable?, but tests using the real owner of the
- # process.
- #
- def executable_real?: () -> bool
-
- #
- # Returns `true` if *stat* is a regular file (not a device file, pipe, socket,
- # etc.).
- #
- # File.stat("testfile").file? #=> true
- #
- def file?: () -> bool
-
- #
- # Identifies the type of *stat*. The return string is one of: ```file`'',
- # ```directory`'', ```characterSpecial`'', ```blockSpecial`'', ```fifo`'',
- # ```link`'', ```socket`'', or ```unknown`''.
- #
- # File.stat("/dev/tty").ftype #=> "characterSpecial"
- #
- def ftype: () -> String
-
- #
- # Returns the numeric group id of the owner of *stat*.
- #
- # File.stat("testfile").gid #=> 500
- #
- def gid: () -> Integer
-
- #
- # Returns true if the effective group id of the process is the same as the group
- # id of *stat*. On Windows, returns `false`.
- #
- # File.stat("testfile").grpowned? #=> true
- # File.stat("/etc/passwd").grpowned? #=> false
- #
- def grpowned?: () -> bool
-
- #
- # Returns the inode number for *stat*.
- #
- # File.stat("testfile").ino #=> 1083669
- #
- def ino: () -> Integer
-
- #
- # Produce a nicely formatted description of *stat*.
- #
- # File.stat("/etc/passwd").inspect
- # #=> "#"
- #
- def inspect: () -> String
-
- #
- # Returns an integer representing the permission bits of *stat*. The meaning of
- # the bits is platform dependent; on Unix systems, see stat(2).
- #
- # File.chmod(0644, "testfile") #=> 1
- # s = File.stat("testfile")
- # sprintf("%o", s.mode) #=> "100644"
- #
- def mode: () -> Integer
-
- #
- # Returns the modification time of *stat*.
- #
- # File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
- #
- def mtime: () -> Time
-
- #
- # Returns the number of hard links to *stat*.
- #
- # File.stat("testfile").nlink #=> 1
- # File.link("testfile", "testfile.bak") #=> 0
- # File.stat("testfile").nlink #=> 2
- #
- def nlink: () -> Integer
-
- #
- # Returns `true` if the effective user id of the process is the same as the
- # owner of *stat*.
- #
- # File.stat("testfile").owned? #=> true
- # File.stat("/etc/passwd").owned? #=> false
- #
- def owned?: () -> bool
-
- #
- # Returns `true` if the operating system supports pipes and *stat* is a pipe;
- # `false` otherwise.
- #
- def pipe?: () -> bool
-
- #
- # Returns an integer representing the device type on which *stat* resides.
- # Returns `nil` if the operating system doesn't support this feature.
- #
- # File.stat("/dev/fd1").rdev #=> 513
- # File.stat("/dev/tty").rdev #=> 1280
- #
- def rdev: () -> Integer?
-
- #
- # Returns the major part of File_Stat#rdev or `nil`.
- #
- # File.stat("/dev/fd1").rdev_major #=> 2
- # File.stat("/dev/tty").rdev_major #=> 5
- #
- def rdev_major: () -> Integer
-
- #
- # Returns the minor part of File_Stat#rdev or `nil`.
- #
- # File.stat("/dev/fd1").rdev_minor #=> 1
- # File.stat("/dev/tty").rdev_minor #=> 0
- #
- def rdev_minor: () -> Integer
-
- #
- # Returns `true` if *stat* is readable by the effective user id of this process.
- #
- # File.stat("testfile").readable? #=> true
- #
- def readable?: () -> bool
-
- #
- # Returns `true` if *stat* is readable by the real user id of this process.
- #
- # File.stat("testfile").readable_real? #=> true
- #
- def readable_real?: () -> bool
-
- #
- # Returns `true` if *stat* has the set-group-id permission bit set, `false` if
- # it doesn't or if the operating system doesn't support this feature.
- #
- # File.stat("/usr/sbin/lpc").setgid? #=> true
- #
- def setgid?: () -> bool
-
- #
- # Returns `true` if *stat* has the set-user-id permission bit set, `false` if it
- # doesn't or if the operating system doesn't support this feature.
- #
- # File.stat("/bin/su").setuid? #=> true
- #
- def setuid?: () -> bool
-
- #
- # Returns the size of *stat* in bytes.
- #
- # File.stat("testfile").size #=> 66
- #
- def size: () -> Integer
-
- #
- # Returns `nil` if *stat* is a zero-length file, the size of the file otherwise.
- #
- # File.stat("testfile").size? #=> 66
- # File.stat(File::NULL).size? #=> nil
- #
- def size?: () -> Integer?
-
- #
- # Returns `true` if *stat* is a socket, `false` if it isn't or if the operating
- # system doesn't support this feature.
- #
- # File.stat("testfile").socket? #=> false
- #
- def socket?: () -> bool
-
- #
- # Returns `true` if *stat* has its sticky bit set, `false` if it doesn't or if
- # the operating system doesn't support this feature.
- #
- # File.stat("testfile").sticky? #=> false
- #
- def sticky?: () -> bool
-
- #
- # Returns `true` if *stat* is a symbolic link, `false` if it isn't or if the
- # operating system doesn't support this feature. As File::stat automatically
- # follows symbolic links, #symlink? will always be `false` for an object
- # returned by File::stat.
- #
- # File.symlink("testfile", "alink") #=> 0
- # File.stat("alink").symlink? #=> false
- # File.lstat("alink").symlink? #=> true
- #
- def symlink?: () -> bool
-
- #
- # Returns the numeric user id of the owner of *stat*.
- #
- # File.stat("testfile").uid #=> 501
- #
- def uid: () -> Integer
-
- #
- # If *stat* is readable by others, returns an integer representing the file
- # permission bits of *stat*. Returns `nil` otherwise. The meaning of the bits is
- # platform dependent; on Unix systems, see stat(2).
- #
- # m = File.stat("/etc/passwd").world_readable? #=> 420
- # sprintf("%o", m) #=> "644"
- #
- def world_readable?: () -> Integer?
-
- #
- # If *stat* is writable by others, returns an integer representing the file
- # permission bits of *stat*. Returns `nil` otherwise. The meaning of the bits is
- # platform dependent; on Unix systems, see stat(2).
- #
- # m = File.stat("/tmp").world_writable? #=> 511
- # sprintf("%o", m) #=> "777"
- #
- def world_writable?: () -> Integer?
-
- #
- # Returns `true` if *stat* is writable by the effective user id of this process.
- #
- # File.stat("testfile").writable? #=> true
- #
- def writable?: () -> bool
-
- #
- # Returns `true` if *stat* is writable by the real user id of this process.
- #
- # File.stat("testfile").writable_real? #=> true
- #
- def writable_real?: () -> bool
-
- #
- # Returns `true` if *stat* is a zero-length file; `false` otherwise.
- #
- # File.stat("testfile").zero? #=> false
- #
- def zero?: () -> bool
-end
diff --git a/core/file_constants.rbs b/core/file_constants.rbs
new file mode 100644
index 000000000..03b15aa0f
--- /dev/null
+++ b/core/file_constants.rbs
@@ -0,0 +1,463 @@
+#
+# Module File::Constants defines file-related constants.
+#
+# There are two families of constants here:
+#
+# * Those having to do with [file
+# access](rdoc-ref:File::Constants@File+Access).
+# * Those having to do with [filename
+# globbing](rdoc-ref:File::Constants@Filename+Globbing+Constants+-28File-3A-
+# 3AFNM_-2A-29).
+#
+# File constants defined for the local process may be retrieved with method
+# File::Constants.constants:
+#
+# File::Constants.constants.take(5)
+# # => [:RDONLY, :WRONLY, :RDWR, :APPEND, :CREAT]
+#
+# ## File Access
+#
+# File-access constants may be used with optional argument `mode` in calls to
+# the following methods:
+#
+# * File.new.
+# * File.open.
+# * IO.for_fd.
+# * IO.new.
+# * IO.open.
+# * IO.popen.
+# * IO.reopen.
+# * IO.sysopen.
+# * StringIO.new.
+# * StringIO.open.
+# * StringIO#reopen.
+#
+# ### Read/Write Access
+#
+# Read-write access for a stream may be specified by a file-access constant.
+#
+# The constant may be specified as part of a bitwise OR of other such constants.
+#
+# Any combination of the constants in this section may be specified.
+#
+# #### File::RDONLY
+#
+# Flag File::RDONLY specifies the stream should be opened for reading only:
+#
+# filepath = '/tmp/t.tmp'
+# f = File.new(filepath, File::RDONLY)
+# f.write('Foo') # Raises IOError (not opened for writing).
+#
+# #### File::WRONLY
+#
+# Flag File::WRONLY specifies that the stream should be opened for writing only:
+#
+# f = File.new(filepath, File::WRONLY)
+# f.read # Raises IOError (not opened for reading).
+#
+# #### File::RDWR
+#
+# Flag File::RDWR specifies that the stream should be opened for both reading
+# and writing:
+#
+# f = File.new(filepath, File::RDWR)
+# f.write('Foo') # => 3
+# f.rewind # => 0
+# f.read # => "Foo"
+#
+# ### File Positioning
+#
+# #### File::APPEND
+#
+# Flag File::APPEND specifies that the stream should be opened in append mode.
+#
+# Before each write operation, the position is set to end-of-stream. The
+# modification of the position and the following write operation are performed
+# as a single atomic step.
+#
+# #### File::TRUNC
+#
+# Flag File::TRUNC specifies that the stream should be truncated at its
+# beginning. If the file exists and is successfully opened for writing, it is to
+# be truncated to position zero; its ctime and mtime are updated.
+#
+# There is no effect on a FIFO special file or a terminal device. The effect on
+# other file types is implementation-defined. The result of using File::TRUNC
+# with File::RDONLY is undefined.
+#
+# ### Creating and Preserving
+#
+# #### File::CREAT
+#
+# Flag File::CREAT specifies that the stream should be created if it does not
+# already exist.
+#
+# If the file exists:
+#
+# - Raise an exception if File::EXCL is also specified.
+# - Otherwise, do nothing.
+#
+# If the file does not exist, then it is created. Upon successful completion,
+# the atime, ctime, and mtime of the file are updated, and the ctime and mtime
+# of the parent directory are updated.
+#
+# #### File::EXCL
+#
+# Flag File::EXCL specifies that the stream should not already exist; If flags
+# File::CREAT and File::EXCL are both specified and the stream already exists,
+# an exception is raised.
+#
+# The check for the existence and creation of the file is performed as an atomic
+# operation.
+#
+# If both File::EXCL and File::CREAT are specified and the path names a symbolic
+# link, an exception is raised regardless of the contents of the symbolic link.
+#
+# If File::EXCL is specified and File::CREAT is not specified, the result is
+# undefined.
+#
+# ### POSIX File Constants
+#
+# Some file-access constants are defined only on POSIX-compliant systems; those
+# are:
+#
+# * File::SYNC.
+# * File::DSYNC.
+# * File::RSYNC.
+# * File::DIRECT.
+# * File::NOATIME.
+# * File::NOCTTY.
+# * File::NOFOLLOW.
+# * File::TMPFILE.
+#
+# #### File::SYNC, File::RSYNC, and File::DSYNC
+#
+# Flag File::SYNC, File::RSYNC, or File::DSYNC specifies synchronization of I/O
+# operations with the underlying file system.
+#
+# These flags are valid only for POSIX-compliant systems.
+#
+# * File::SYNC specifies that all write operations (both data and metadata)
+# are immediately to be flushed to the underlying storage device. This means
+# that the data is written to the storage device, and the file's metadata
+# (e.g., file size, timestamps, permissions) are also synchronized. This
+# guarantees that data is safely stored on the storage medium before
+# returning control to the calling program. This flag can have a significant
+# impact on performance since it requires synchronous writes, which can be
+# slower compared to asynchronous writes.
+#
+# * File::RSYNC specifies that any read operations on the file will not return
+# until all outstanding write operations (those that have been issued but
+# not completed) are also synchronized. This is useful when you want to read
+# the most up-to-date data, which may still be in the process of being
+# written.
+#
+# * File::DSYNC specifies that all *data* write operations are immediately to
+# be flushed to the underlying storage device; this differs from File::SYNC,
+# which requires that *metadata* also be synchronized.
+#
+# Note that the behavior of these flags may vary slightly depending on the
+# operating system and filesystem being used. Additionally, using these flags
+# can have an impact on performance due to the synchronous nature of the I/O
+# operations, so they should be used judiciously, especially in
+# performance-critical applications.
+#
+# #### File::NOCTTY
+#
+# Flag File::NOCTTY specifies that if the stream is a terminal device, that
+# device does not become the controlling terminal for the process.
+#
+# Defined only for POSIX-compliant systems.
+#
+# #### File::DIRECT
+#
+# Flag File::DIRECT requests that cache effects of the I/O to and from the
+# stream be minimized.
+#
+# Defined only for POSIX-compliant systems.
+#
+# #### File::NOATIME
+#
+# Flag File::NOATIME specifies that act of opening the stream should not modify
+# its access time (atime).
+#
+# Defined only for POSIX-compliant systems.
+#
+# #### File::NOFOLLOW
+#
+# Flag File::NOFOLLOW specifies that if path is a symbolic link, it should not
+# be followed.
+#
+# Defined only for POSIX-compliant systems.
+#
+# #### File::TMPFILE
+#
+# Flag File::TMPFILE specifies that the opened stream should be a new temporary
+# file.
+#
+# Defined only for POSIX-compliant systems.
+#
+# ### Other File-Access Constants
+#
+# #### File::NONBLOCK
+#
+# When possible, the file is opened in nonblocking mode. Neither the open
+# operation nor any subsequent I/O operations on the file will cause the calling
+# process to wait.
+#
+# #### File::BINARY
+#
+# Flag File::BINARY specifies that the stream is to be accessed in binary mode.
+#
+# #### File::SHARE_DELETE
+#
+# Flag File::SHARE_DELETE enables other processes to open the stream with delete
+# access.
+#
+# Windows only.
+#
+# If the stream is opened for (local) delete access without File::SHARE_DELETE,
+# and another process attempts to open it with delete access, the attempt fails
+# and the stream is not opened for that process.
+#
+# ## Locking
+#
+# Four file constants relate to stream locking; see File#flock:
+#
+# #### File::LOCK_EX
+#
+# Flag File::LOCK_EX specifies an exclusive lock; only one process a a time may
+# lock the stream.
+#
+# #### File::LOCK_NB
+#
+# Flag File::LOCK_NB specifies non-blocking locking for the stream; may be
+# combined with File::LOCK_EX or File::LOCK_SH.
+#
+# #### File::LOCK_SH
+#
+# Flag File::LOCK_SH specifies that multiple processes may lock the stream at
+# the same time.
+#
+# #### File::LOCK_UN
+#
+# Flag File::LOCK_UN specifies that the stream is not to be locked.
+#
+# ## Filename Globbing Constants (File::FNM_*)
+#
+# Filename-globbing constants may be used with optional argument `flags` in
+# calls to the following methods:
+#
+# * Dir.glob.
+# * File.fnmatch.
+# * Pathname#fnmatch.
+# * Pathname.glob.
+# * Pathname#glob.
+#
+# The constants are:
+#
+# #### File::FNM_CASEFOLD
+#
+# Flag File::FNM_CASEFOLD makes patterns case insensitive for File.fnmatch (but
+# not Dir.glob).
+#
+# #### File::FNM_DOTMATCH
+#
+# Flag File::FNM_DOTMATCH makes the '*' pattern match a filename
+# starting with '.'.
+#
+# #### File::FNM_EXTGLOB
+#
+# Flag File::FNM_EXTGLOB enables pattern '{_a_,_b_}', which matches
+# pattern '*a*' and pattern '*b*'; behaves like a [regexp
+# union](rdoc-ref:Regexp.union) (e.g., '(?:_a_|_b_)'):
+#
+# pattern = '{LEGAL,BSDL}'
+# Dir.glob(pattern) # => ["LEGAL", "BSDL"]
+# Pathname.glob(pattern) # => [#, #]
+# pathname.glob(pattern) # => [#, #]
+#
+# #### File::FNM_NOESCAPE
+#
+# Flag File::FNM_NOESCAPE disables '\' escaping.
+#
+# #### File::FNM_PATHNAME
+#
+# Flag File::FNM_PATHNAME specifies that patterns '*' and
+# '?' do not match the directory separator (the value of constant
+# File::SEPARATOR).
+#
+# #### File::FNM_SHORTNAME
+#
+# Flag File::FNM_SHORTNAME allows patterns to match short names if they exist.
+#
+# Windows only.
+#
+# #### File::FNM_SYSCASE
+#
+# Flag File::FNM_SYSCASE specifies that case sensitivity is the same as in the
+# underlying operating system; effective for File.fnmatch, but not Dir.glob.
+#
+# ## Other Constants
+#
+# #### File::NULL
+#
+# Flag File::NULL contains the string value of the null device:
+#
+# * On a Unix-like OS, '/dev/null'.
+# * On Windows, 'NUL'.
+#
+module File::Constants
+ #
+ # [File::APPEND](rdoc-ref:File::Constants@File-3A-3AAPPEND)
+ #
+ APPEND: Integer
+
+ #
+ # [File::BINARY](rdoc-ref:File::Constants@File-3A-3ABINARY)
+ #
+ BINARY: Integer
+
+ #
+ # [File::CREAT](rdoc-ref:File::Constants@File-3A-3ACREAT)
+ #
+ CREAT: Integer
+
+ #
+ # [File::DIRECT](rdoc-ref:File::Constants@File-3A-3ADIRECT)
+ #
+ DIRECT: Integer
+
+ #
+ # [File::DSYNC](rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+an
+ # d+File-3A-3ADSYNC)
+ #
+ DSYNC: Integer
+
+ #
+ # [File::EXCL](rdoc-ref:File::Constants@File-3A-3AEXCL)
+ #
+ EXCL: Integer
+
+ #
+ # [File::FNM_CASEFOLD](rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD)
+ #
+ FNM_CASEFOLD: Integer
+
+ #
+ # [File::FNM_DOTMATCH](rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH)
+ #
+ FNM_DOTMATCH: Integer
+
+ #
+ # [File::FNM_EXTGLOB](rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB)
+ #
+ FNM_EXTGLOB: Integer
+
+ #
+ # [File::FNM_NOESCAPE](rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE)
+ #
+ FNM_NOESCAPE: Integer
+
+ #
+ # [File::FNM_PATHNAME](rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME)
+ #
+ FNM_PATHNAME: Integer
+
+ #
+ # [File::FNM_SHORTNAME](rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME)
+ #
+ FNM_SHORTNAME: Integer
+
+ #
+ # [File::FNM_SYSCASE](rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE)
+ #
+ FNM_SYSCASE: Integer
+
+ #
+ # [File::LOCK_EX](rdoc-ref:File::Constants@File-3A-3ALOCK_EX)
+ #
+ LOCK_EX: Integer
+
+ #
+ # [File::LOCK_NB](rdoc-ref:File::Constants@File-3A-3ALOCK_NB)
+ #
+ LOCK_NB: Integer
+
+ #
+ # [File::LOCK_SH](rdoc-ref:File::Constants@File-3A-3ALOCK_SH)
+ #
+ LOCK_SH: Integer
+
+ #
+ # [File::LOCK_UN](rdoc-ref:File::Constants@File-3A-3ALOCK_UN)
+ #
+ LOCK_UN: Integer
+
+ #
+ # [File::NOATIME](rdoc-ref:File::Constants@File-3A-3ANOATIME)
+ #
+ NOATIME: Integer
+
+ #
+ # [File::NOCTTY](rdoc-ref:File::Constants@File-3A-3ANOCTTY)
+ #
+ NOCTTY: Integer
+
+ #
+ # [File::NOFOLLOW](rdoc-ref:File::Constants@File-3A-3ANOFOLLOW)
+ #
+ NOFOLLOW: Integer
+
+ #
+ # [File::NONBLOCK](rdoc-ref:File::Constants@File-3A-3ANONBLOCK)
+ #
+ NONBLOCK: Integer
+
+ #
+ # [File::NULL](rdoc-ref:File::Constants@File-3A-3ANULL)
+ #
+ NULL: String
+
+ #
+ # [File::RDONLY](rdoc-ref:File::Constants@File-3A-3ARDONLY)
+ #
+ RDONLY: Integer
+
+ #
+ # [File::RDWR](rdoc-ref:File::Constants@File-3A-3ARDWR)
+ #
+ RDWR: Integer
+
+ #
+ # [File::RSYNC](rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+an
+ # d+File-3A-3ADSYNC)
+ #
+ RSYNC: Integer
+
+ #
+ # [File::SHARE_DELETE](rdoc-ref:File::Constants@File-3A-3ASHARE_DELETE)
+ #
+ SHARE_DELETE: Integer
+
+ #
+ # [File::SYNC](rdoc-ref:File::Constants@File-3A-3ASYNC-2C+File-3A-3ARSYNC-2C+and
+ # +File-3A-3ADSYNC)
+ #
+ SYNC: Integer
+
+ #
+ # [File::TMPFILE](rdoc-ref:File::Constants@File-3A-3ATMPFILE)
+ #
+ TMPFILE: Integer
+
+ #
+ # [File::TRUNC](rdoc-ref:File::Constants@File-3A-3ATRUNC)
+ #
+ TRUNC: Integer
+
+ #
+ # [File::WRONLY](rdoc-ref:File::Constants@File-3A-3AWRONLY)
+ #
+ WRONLY: Integer
+end
diff --git a/core/file_stat.rbs b/core/file_stat.rbs
new file mode 100644
index 000000000..03e6ccd79
--- /dev/null
+++ b/core/file_stat.rbs
@@ -0,0 +1,534 @@
+#
+# Objects of class File::Stat encapsulate common status information for File
+# objects. The information is recorded at the moment the File::Stat object is
+# created; changes made to the file after that point will not be reflected.
+# File::Stat objects are returned by IO#stat, File::stat, File#lstat, and
+# File::lstat. Many of these methods return platform-specific values, and not
+# all values are meaningful on all systems. See also Kernel#test.
+#
+class File::Stat
+ include Comparable
+
+ #
+ # Create a File::Stat object for the given file name (raising an exception if
+ # the file doesn't exist).
+ #
+ def initialize: (path file_name) -> void
+
+ def initialize_copy: (self other) -> void
+
+ #
+ # Compares `self` and `other`, by comparing their modification times; that is,
+ # by comparing self.mtime and other.mtime.
+ #
+ # Returns:
+ #
+ # * -1, if self.mtime is earlier.
+ # * `0`, if the two values are equal.
+ # * `1`, if self.mtime is later.
+ # * `nil`, if `other` is not a File::Stat object.
+ #
+ # Examples:
+ #
+ # stat0 = File.stat('README.md')
+ # stat1 = File.stat('NEWS.md')
+ # stat0.mtime # => 2025-12-20 15:33:05.6972341 -0600
+ # stat1.mtime # => 2025-12-20 16:02:08.2672945 -0600
+ # stat0 <=> stat1 # => -1
+ # stat0 <=> stat0.dup # => 0
+ # stat1 <=> stat0 # => 1
+ # stat0 <=> :foo # => nil
+ #
+ # Class File::Stat includes module Comparable, each of whose methods uses
+ # File::Stat#<=> for comparison.
+ #
+ def <=>: (self other) -> (-1 | 0 | 1)
+ | (untyped) -> (-1 | 0 | 1)?
+
+ #
+ # Returns the last access time for this file as an object of class Time.
+ #
+ # File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
+ #
+ def atime: () -> Time
+
+ #
+ # Returns the birth time for *stat*.
+ #
+ # If the platform doesn't have birthtime, raises NotImplementedError.
+ #
+ # File.write("testfile", "foo")
+ # sleep 10
+ # File.write("testfile", "bar")
+ # sleep 10
+ # File.chmod(0644, "testfile")
+ # sleep 10
+ # File.read("testfile")
+ # File.stat("testfile").birthtime #=> 2014-02-24 11:19:17 +0900
+ # File.stat("testfile").mtime #=> 2014-02-24 11:19:27 +0900
+ # File.stat("testfile").ctime #=> 2014-02-24 11:19:37 +0900
+ # File.stat("testfile").atime #=> 2014-02-24 11:19:47 +0900
+ #
+ def birthtime: () -> Time
+
+ #
+ # Returns the native file system's block size. Will return `nil` on platforms
+ # that don't support this information.
+ #
+ # File.stat("testfile").blksize #=> 4096
+ #
+ def blksize: () -> Integer?
+
+ #
+ # Returns `true` if the file is a block device, `false` if it isn't or if the
+ # operating system doesn't support this feature.
+ #
+ # File.stat("testfile").blockdev? #=> false
+ # File.stat("/dev/hda1").blockdev? #=> true
+ #
+ def blockdev?: () -> bool
+
+ #
+ # Returns the number of native file system blocks allocated for this file, or
+ # `nil` if the operating system doesn't support this feature.
+ #
+ # File.stat("testfile").blocks #=> 2
+ #
+ def blocks: () -> Integer?
+
+ #
+ # Returns `true` if the file is a character device, `false` if it isn't or if
+ # the operating system doesn't support this feature.
+ #
+ # File.stat("/dev/tty").chardev? #=> true
+ #
+ def chardev?: () -> bool
+
+ #
+ # Returns the change time for *stat* (that is, the time directory information
+ # about the file was changed, not the file itself).
+ #
+ # Note that on Windows (NTFS), returns creation time (birth time).
+ #
+ # File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
+ #
+ def ctime: () -> Time
+
+ #
+ # Returns an integer representing the device on which *stat* resides.
+ #
+ # File.stat("testfile").dev #=> 774
+ #
+ def dev: () -> Integer
+
+ #
+ # Returns the major part of File_Stat#dev or `nil`.
+ #
+ # File.stat("/dev/fd1").dev_major #=> 2
+ # File.stat("/dev/tty").dev_major #=> 5
+ #
+ def dev_major: () -> Integer?
+
+ #
+ # Returns the minor part of File_Stat#dev or `nil`.
+ #
+ # File.stat("/dev/fd1").dev_minor #=> 1
+ # File.stat("/dev/tty").dev_minor #=> 0
+ #
+ def dev_minor: () -> Integer?
+
+ #
+ # Returns `true` if *stat* is a directory, `false` otherwise.
+ #
+ # File.stat("testfile").directory? #=> false
+ # File.stat(".").directory? #=> true
+ #
+ def directory?: () -> bool
+
+ #
+ # Returns `true` if *stat* is executable or if the operating system doesn't
+ # distinguish executable files from nonexecutable files. The tests are made
+ # using the effective owner of the process.
+ #
+ # File.stat("testfile").executable? #=> false
+ #
+ def executable?: () -> bool
+
+ #
+ # Same as executable?, but tests using the real owner of the
+ # process.
+ #
+ def executable_real?: () -> bool
+
+ #
+ # Returns `true` if *stat* is a regular file (not a device file, pipe, socket,
+ # etc.).
+ #
+ # File.stat("testfile").file? #=> true
+ #
+ def file?: () -> bool
+
+ #
+ # Identifies the type of *stat*. The return string is one of: ```file`'',
+ # ```directory`'', ```characterSpecial`'', ```blockSpecial`'', ```fifo`'',
+ # ```link`'', ```socket`'', or ```unknown`''.
+ #
+ # File.stat("/dev/tty").ftype #=> "characterSpecial"
+ #
+ def ftype: () -> File::ftype
+
+ #
+ # Returns the numeric group id of the owner of *stat*.
+ #
+ # File.stat("testfile").gid #=> 500
+ #
+ def gid: () -> Integer
+
+ #
+ # Returns true if the effective group id of the process is the same as the group
+ # id of *stat*. On Windows, returns `false`.
+ #
+ # File.stat("testfile").grpowned? #=> true
+ # File.stat("/etc/passwd").grpowned? #=> false
+ #
+ def grpowned?: () -> bool
+
+ #
+ # Returns the inode number for *stat*.
+ #
+ # File.stat("testfile").ino #=> 1083669
+ #
+ def ino: () -> Integer
+
+ #
+ # Produce a nicely formatted description of *stat*.
+ #
+ # File.stat("/etc/passwd").inspect
+ # #=> "#"
+ #
+ def inspect: () -> String
+
+ #
+ # Returns an integer representing the permission bits of *stat*. The meaning of
+ # the bits is platform dependent; on Unix systems, see stat(2).
+ #
+ # File.chmod(0644, "testfile") #=> 1
+ # s = File.stat("testfile")
+ # sprintf("%o", s.mode) #=> "100644"
+ #
+ def mode: () -> Integer
+
+ #
+ # Returns the modification time of *stat*.
+ #
+ # File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
+ #
+ def mtime: () -> Time
+
+ #
+ # Returns the number of hard links to *stat*.
+ #
+ # File.stat("testfile").nlink #=> 1
+ # File.link("testfile", "testfile.bak") #=> 0
+ # File.stat("testfile").nlink #=> 2
+ #
+ def nlink: () -> Integer
+
+ #
+ # Returns `true` if the effective user id of the process is the same as the
+ # owner of *stat*.
+ #
+ # File.stat("testfile").owned? #=> true
+ # File.stat("/etc/passwd").owned? #=> false
+ #
+ def owned?: () -> bool
+
+ #
+ # Returns `true` if the operating system supports pipes and *stat* is a pipe;
+ # `false` otherwise.
+ #
+ def pipe?: () -> bool
+
+ #
+ # Returns an integer representing the device type on which *stat* resides.
+ # Returns `nil` if the operating system doesn't support this feature.
+ #
+ # File.stat("/dev/fd1").rdev #=> 513
+ # File.stat("/dev/tty").rdev #=> 1280
+ #
+ def rdev: () -> Integer?
+
+ #
+ # Returns the major part of File_Stat#rdev or `nil`.
+ #
+ # File.stat("/dev/fd1").rdev_major #=> 2
+ # File.stat("/dev/tty").rdev_major #=> 5
+ #
+ def rdev_major: () -> Integer?
+
+ #
+ # Returns the minor part of File_Stat#rdev or `nil`.
+ #
+ # File.stat("/dev/fd1").rdev_minor #=> 1
+ # File.stat("/dev/tty").rdev_minor #=> 0
+ #
+ def rdev_minor: () -> Integer?
+
+ #
+ # Returns `true` if *stat* is readable by the effective user id of this process.
+ #
+ # File.stat("testfile").readable? #=> true
+ #
+ def readable?: () -> bool
+
+ #
+ # Returns `true` if *stat* is readable by the real user id of this process.
+ #
+ # File.stat("testfile").readable_real? #=> true
+ #
+ def readable_real?: () -> bool
+
+ #
+ # Returns `true` if *stat* has the set-group-id permission bit set, `false` if
+ # it doesn't or if the operating system doesn't support this feature.
+ #
+ # File.stat("/usr/sbin/lpc").setgid? #=> true
+ #
+ def setgid?: () -> bool
+
+ #
+ # Returns `true` if *stat* has the set-user-id permission bit set, `false` if it
+ # doesn't or if the operating system doesn't support this feature.
+ #
+ # File.stat("/bin/su").setuid? #=> true
+ #
+ def setuid?: () -> bool
+
+ #
+ # Returns the size of *stat* in bytes.
+ #
+ # File.stat("testfile").size #=> 66
+ #
+ def size: () -> Integer
+
+ #
+ # Returns `nil` if *stat* is a zero-length file, the size of the file otherwise.
+ #
+ # File.stat("testfile").size? #=> 66
+ # File.stat(File::NULL).size? #=> nil
+ #
+ def size?: () -> Integer?
+
+ #
+ # Returns `true` if *stat* is a socket, `false` if it isn't or if the operating
+ # system doesn't support this feature.
+ #
+ # File.stat("testfile").socket? #=> false
+ #
+ def socket?: () -> bool
+
+ #
+ # Returns `true` if *stat* has its sticky bit set, `false` if it doesn't or if
+ # the operating system doesn't support this feature.
+ #
+ # File.stat("testfile").sticky? #=> false
+ #
+ def sticky?: () -> bool
+
+ #
+ # Returns `true` if *stat* is a symbolic link, `false` if it isn't or if the
+ # operating system doesn't support this feature. As File::stat automatically
+ # follows symbolic links, #symlink? will always be `false` for an object
+ # returned by File::stat.
+ #
+ # File.symlink("testfile", "alink") #=> 0
+ # File.stat("alink").symlink? #=> false
+ # File.lstat("alink").symlink? #=> true
+ #
+ def symlink?: () -> bool
+
+ #
+ # Returns the numeric user id of the owner of *stat*.
+ #
+ # File.stat("testfile").uid #=> 501
+ #
+ def uid: () -> Integer
+
+ #
+ # If *stat* is readable by others, returns an integer representing the file
+ # permission bits of *stat*. Returns `nil` otherwise. The meaning of the bits is
+ # platform dependent; on Unix systems, see stat(2).
+ #
+ # m = File.stat("/etc/passwd").world_readable? #=> 420
+ # sprintf("%o", m) #=> "644"
+ #
+ def world_readable?: () -> Integer?
+
+ #
+ # If *stat* is writable by others, returns an integer representing the file
+ # permission bits of *stat*. Returns `nil` otherwise. The meaning of the bits is
+ # platform dependent; on Unix systems, see stat(2).
+ #
+ # m = File.stat("/tmp").world_writable? #=> 511
+ # sprintf("%o", m) #=> "777"
+ #
+ def world_writable?: () -> Integer?
+
+ #
+ # Returns `true` if *stat* is writable by the effective user id of this process.
+ #
+ # File.stat("testfile").writable? #=> true
+ #
+ def writable?: () -> bool
+
+ #
+ # Returns `true` if *stat* is writable by the real user id of this process.
+ #
+ # File.stat("testfile").writable_real? #=> true
+ #
+ def writable_real?: () -> bool
+
+ #
+ # Returns `true` if *stat* is a zero-length file; `false` otherwise.
+ #
+ # File.stat("testfile").zero? #=> false
+ #
+ def zero?: () -> bool
+end
diff --git a/test/stdlib/File_Constants_test.rb b/test/stdlib/File_Constants_test.rb
new file mode 100644
index 000000000..8405450a0
--- /dev/null
+++ b/test/stdlib/File_Constants_test.rb
@@ -0,0 +1,140 @@
+require_relative "test_helper"
+
+class FileCosntantsSingletonTest < Test::Unit::TestCase
+ include TestHelper
+
+ testing "singleton(::File::Constants)"
+
+ def test_const_RDONLY
+ assert_const_type 'Integer',
+ 'File::Constants::RDONLY'
+ end
+
+ def test_const_WRONLY
+ assert_const_type 'Integer',
+ 'File::Constants::WRONLY'
+ end
+
+ def test_const_RDWR
+ assert_const_type 'Integer',
+ 'File::Constants::RDWR'
+ end
+
+ def test_const_APPEND
+ assert_const_type 'Integer',
+ 'File::Constants::APPEND'
+ end
+
+ def test_const_CREAT
+ assert_const_type 'Integer',
+ 'File::Constants::CREAT'
+ end
+
+ def test_const_EXCL
+ assert_const_type 'Integer',
+ 'File::Constants::EXCL'
+ end
+
+ def test_const_NONBLOCK
+ omit 'NONBLOCK not defined' unless defined? File::Constants::NONBLOCK
+
+ assert_const_type 'Integer',
+ 'File::Constants::NONBLOCK'
+ end
+
+ def test_const_TRUNC
+ assert_const_type 'Integer',
+ 'File::Constants::TRUNC'
+ end
+
+ def test_const_NOCTTY
+ omit 'NOCTTY not defined' unless defined? File::Constants::NOCTTY
+
+ assert_const_type 'Integer',
+ 'File::Constants::NOCTTY'
+ end
+
+ def test_const_BINARY
+ assert_const_type 'Integer',
+ 'File::Constants::BINARY'
+ end
+
+ def test_const_SHARE_DELETE
+ assert_const_type 'Integer',
+ 'File::Constants::SHARE_DELETE'
+ end
+
+ def test_const_SYNC
+ omit 'SYNC not defined' unless defined? File::Constants::SYNC
+
+ assert_const_type 'Integer',
+ 'File::Constants::SYNC'
+ end
+
+ def test_const_DSYNC
+ omit 'DSYNC not defined' unless defined? File::Constants::DSYNC
+
+ assert_const_type 'Integer',
+ 'File::Constants::DSYNC'
+ end
+
+ def test_const_RSYNC
+ omit 'RSYNC not defined' unless defined? File::Constants::RSYNC
+
+ assert_const_type 'Integer',
+ 'File::Constants::RSYNC'
+ end
+
+ def test_const_NOFOLLOW
+ omit 'NOFOLLOW not defined' unless defined? File::Constants::NOFOLLOW
+
+ assert_const_type 'Integer',
+ 'File::Constants::NOFOLLOW'
+ end
+
+ def test_const_NOATIME
+ omit 'NOATIME not defined' unless defined? File::Constants::NOATIME
+
+ assert_const_type 'Integer',
+ 'File::Constants::NOATIME'
+ end
+
+ def test_const_DIRECT
+ omit 'DIRECT not defined' unless defined? File::Constants::DIRECT
+
+ assert_const_type 'Integer',
+ 'File::Constants::DIRECT'
+ end
+
+ def test_const_TMPFILE
+ omit 'TMPFILE not defined' unless defined? File::Constants::TMPFILE
+
+ assert_const_type 'Integer',
+ 'File::Constants::TMPFILE'
+ end
+
+ def test_const_LOCK_SH
+ assert_const_type 'Integer',
+ 'File::Constants::LOCK_SH'
+ end
+
+ def test_const_LOCK_EX
+ assert_const_type 'Integer',
+ 'File::Constants::LOCK_EX'
+ end
+
+ def test_const_LOCK_UN
+ assert_const_type 'Integer',
+ 'File::Constants::LOCK_UN'
+ end
+
+ def test_const_LOCK_NB
+ assert_const_type 'Integer',
+ 'File::Constants::LOCK_NB'
+ end
+
+ def test_const_NULL
+ assert_const_type 'String',
+ 'File::Constants::NULL'
+ end
+end
diff --git a/test/stdlib/File_Stat_test.rb b/test/stdlib/File_Stat_test.rb
index cdd6b2d1b..a92b51cd6 100644
--- a/test/stdlib/File_Stat_test.rb
+++ b/test/stdlib/File_Stat_test.rb
@@ -17,7 +17,7 @@ class FileStatInstanceTest < Test::Unit::TestCase
testing "::File::Stat"
def test_spaceship
- assert_send_type "(File::Stat) -> Integer",
+ assert_send_type "(File::Stat) -> (-1 | 0 | 1)",
File::Stat.new(File.expand_path(__FILE__)), :<=>, File::Stat.new(File.expand_path(__FILE__))
assert_send_type "(untyped) -> nil",
File::Stat.new(File.expand_path(__FILE__)), :<=>, "not a File::Stat object"
@@ -65,12 +65,12 @@ def test_dev
end
def test_dev_major
- assert_send_type "() -> Integer",
+ assert_send_type "() -> Integer?",
File::Stat.new(File.expand_path(__FILE__)), :dev_major
end
def test_dev_minor
- assert_send_type "() -> Integer",
+ assert_send_type "() -> Integer?",
File::Stat.new(File.expand_path(__FILE__)), :dev_minor
end
@@ -95,7 +95,7 @@ def test_file?
end
def test_ftype
- assert_send_type "() -> String",
+ assert_send_type "() -> File::ftype",
File::Stat.new(File.expand_path(__FILE__)), :ftype
end
@@ -150,12 +150,12 @@ def test_rdev
end
def test_rdev_major
- assert_send_type "() -> Integer",
+ assert_send_type "() -> Integer?",
File::Stat.new(File.expand_path(__FILE__)), :rdev_major
end
def test_rdev_minor
- assert_send_type "() -> Integer",
+ assert_send_type "() -> Integer?",
File::Stat.new(File.expand_path(__FILE__)), :rdev_minor
end
diff --git a/test/stdlib/File_test.rb b/test/stdlib/File_test.rb
index 0f18b74a8..863026779 100644
--- a/test/stdlib/File_test.rb
+++ b/test/stdlib/File_test.rb
@@ -6,6 +6,26 @@ class FileSingletonTest < Test::Unit::TestCase
testing "singleton(::File)"
+ def test_const_ALT_SEPARATOR
+ assert_const_type 'String?',
+ 'File::ALT_SEPARATOR'
+ end
+
+ def test_const_SEPARATOR
+ assert_const_type 'String',
+ 'File::SEPARATOR'
+ end
+
+ def test_const_Separator
+ assert_const_type 'String',
+ 'File::Separator'
+ end
+
+ def test_const_PATH_SEPARATOR
+ assert_const_type 'String',
+ 'File::PATH_SEPARATOR'
+ end
+
def test_new
assert_send_type "(String) -> File",
File, :new, File.expand_path(__FILE__)