How can I resolve sorbet error: "Constants must have type annotations with T.let when specifying # typed: strict"?
How can I resolve sorbet error: "Constants must have type annotations with T.let when specifying # typed: strict"?
这与我在 中的问题类似,但针对常量。
我正在尝试将冰糕类型信息添加到我的 gem、pdf-reader。我不希望 sorbet 成为 gem 的运行时依赖项,因此所有类型注释都在 rbi/ 目录的外部文件中。我也无法在我的 类 中扩展 T::Sig
,我也无法在我的代码中使用 T.let
。
我想在某些文件中启用 typed: strict
,但这样做会标记常量没有类型注释:
$ be srb tc
./lib/pdf/reader/buffer.rb:41: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
41 | TOKEN_WHITESPACE=[0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Autocorrect: Use `-a` to autocorrect
./lib/pdf/reader/buffer.rb:41: Replace with T.let([0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20], T::Array[Integer])
41 | TOKEN_WHITESPACE=[0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
./lib/pdf/reader/buffer.rb:42: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
42 | TOKEN_DELIMITER=[0x25, 0x3C, 0x3E, 0x28, 0x5B, 0x7B, 0x29, 0x5D, 0x7D, 0x2F]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Autocorrect: Use `-a` to autocorrect
./lib/pdf/reader/buffer.rb:42: Replace with T.let([0x25, 0x3C, 0x3E, 0x28, 0x5B, 0x7B, 0x29, 0x5D, 0x7D, 0x2F], T::Array[Integer])
42 | TOKEN_DELIMITER=[0x25, 0x3C, 0x3E, 0x28, 0x5B, 0x7B, 0x29, 0x5D, 0x7D, 0x2F]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
./lib/pdf/reader/buffer.rb:55: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
55 | WHITE_SPACE = [LF, CR, ' ']
^^^^^^^^^^^^^
Autocorrect: Use `-a` to autocorrect
./lib/pdf/reader/buffer.rb:55: Replace with T.let([LF, CR, ' '], T::Array[String])
55 | WHITE_SPACE = [LF, CR, ' ']
^^^^^^^^^^^^^
Errors: 3
建议的修复是使用 T.let()
。但是我不能那样做,因为它需要对冰糕的运行时依赖。
我尝试在我的 RBI 文件中使用 T.let()
,类似于我们在链接问题中解决实例变量问题的方式。但是,这似乎对此错误没有影响:
diff --git a/rbi/pdf-reader.rbi b/rbi/pdf-reader.rbi
index 113f183..f392b0a 100644
--- a/rbi/pdf-reader.rbi
+++ b/rbi/pdf-reader.rbi
@@ -81,7 +81,7 @@ module PDF
CR = "\r"
LF = "\n"
CRLF = "\r\n"
- WHITE_SPACE = [LF, CR, ' ']
+ WHITE_SPACE = T.let(T.unsafe(nil), T::Array[String])
TRAILING_BYTECOUNT = 5000
sig { returns(Integer) }
额外研究
有趣的是,如果我将 RBI 文件中的 T.let()
更改为明显错误的内容,例如:
diff --git a/rbi/pdf-reader.rbi b/rbi/pdf-reader.rbi
index 113f183..251d80d 100644
--- a/rbi/pdf-reader.rbi
+++ b/rbi/pdf-reader.rbi
@@ -81,7 +81,7 @@ module PDF
CR = "\r"
LF = "\n"
CRLF = "\r\n"
- WHITE_SPACE = [LF, CR, ' ']
+ WHITE_SPACE = T.let(T.unsafe(nil), T::Array[Integer])
TRAILING_BYTECOUNT = 5000
sig { returns(Integer) }
然后我得到一个类型错误:
$ srb tc
./lib/pdf/reader/buffer.rb:55: Expected T::Array[Integer] but found T::Array[String] for field https://srb.help/7013
55 | WHITE_SPACE = [LF, CR, " "]
^^^^^^^^^^^^^
Expected T::Array[Integer] for field defined here:
./lib/pdf/reader/buffer.rb:55:
55 | WHITE_SPACE = [LF, CR, " "]
^^^^^^^^^^^
Got T::Array[String] originating from:
./lib/pdf/reader/buffer.rb:55:
55 | WHITE_SPACE = [LF, CR, " "]
^^^^^^^^^^^^^
打点文件中常量的T.let()
似乎并没有被忽略,但这还不足以满足对要定义的常量类型的严格要求。
# TLDR
# NOTE: temporary fix, because this looks like a sorbet bug/feature;
# if you're getting inconsistent results use --max-threads=1
$ srb typecheck --stress-incremental-resolver
这是重现问题的最小设置:
# Gemfile
source "https://rubygems.org"
gem 'sorbet'
# lib/my_gem.rb
module MyGem
WHITE_SPACE = [' ']
end
# sorbet/rbi/my_gem.rbi
module MyGem
# NOTE: Based on sorbet docs, this should tell sorbet the type of this constant
# and should be equivalent to doing this in `lib/my_gem.rb`:
# WHITE_SPACE = T.let([' '], T::Array[String])
WHITE_SPACE = T.let(T.unsafe(nil), T::Array[String])
end
# srb --version
# Sorbet typechecker 0.5.10010 git d2cd1e574d70b4485d961fdf1f457948e4d3988d debug_symbols=true clean=0
# ruby --version
# ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
运行 严格类型检查失败:
$ srb typecheck --typed=strict --dir .
lib/my_gem.rb:2: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
2 | WHITE_SPACE = [' ']
^^^^^
有一个解析器标志在某处改变了一些东西,我的猜测是修复了错误:
$ srb typecheck --help
...
--stress-incremental-resolver
Force incremental updates to discover resolver & namer bugs
...
$ srb typecheck --stress-incremental-resolver --typed=strict --dir .
No errors! Great job.
为了验证它确实进行了类型检查,我们可以将 .rbi 文件更改为不正确的内容:
# sorbet/rbi/my_gem.rbi
module MyGem
WHITE_SPACE = T.let(T.unsafe(nil), T::Array[Integer])
end
$ srb typecheck --stress-incremental-resolver --typed=strict --dir .
lib/my_gem.rb:42: Expected T::Array[Integer] but found [String(" ")] for field https://srb.help/7013
42 | WHITE_SPACE = [' ']
^^^^^
似乎可以工作并且看起来它自己正确地解析了常量类型 => [String(" ")]
与 [Integer]
.
不匹配
深入挖掘表明 sorbet parses/rewrites/desugars 我们的文件与解析器标志不同:
$ srb typecheck --print=resolve-tree --typed=strict --dir .
begin
class <emptyTree><<C <root>>> < (::<todo sym>)
nil
end
<emptyTree>
end
begin
class <emptyTree><<C <root>>> < (::<todo sym>)
begin
module ::MyGem<<C MyGem>> < ()
#
# NOTE: This `Magic` bit in particular is not present with --stress-incremental-resolver
#
::MyGem::WHITE_SPACE = ::<Magic>.<suggest-type>([" "])
end
::Sorbet::Private::Static.keep_for_ide(::MyGem)
<emptyTree>
end
end
<emptyTree>
end
begin
class <emptyTree><<C <root>>> < (::<todo sym>)
begin
module ::MyGem<<C MyGem>> < ()
::MyGem::WHITE_SPACE = begin
::Sorbet::Private::Static.keep_for_typechecking(::T::Array.[](::String))
T.let(::T.unsafe(nil), AppliedType {
klass = <C <U Array>>
targs = [
<C <U Elem>> = String
]
})
end
end
::Sorbet::Private::Static.keep_for_ide(::MyGem)
<emptyTree>
end
end
<emptyTree>
end
<Magic>.<suggest-type>
映射到这个方法:
https://github.com/sorbet/sorbet/blob/0.5.10010.20220513160354-d2cd1e574/core/types/calls.cc#L2642
该方法适用于纯字符串 ' '
,但对数组 [' ']
会引发错误,即使一切看起来都已解决,如上文 ... found [String(" ")] ...
所示。它是功能还是错误待定。
此外,将 WHITE_SPACE
从常量转换为方法可能是一个解决方案。
这与我在
我正在尝试将冰糕类型信息添加到我的 gem、pdf-reader。我不希望 sorbet 成为 gem 的运行时依赖项,因此所有类型注释都在 rbi/ 目录的外部文件中。我也无法在我的 类 中扩展 T::Sig
,我也无法在我的代码中使用 T.let
。
我想在某些文件中启用 typed: strict
,但这样做会标记常量没有类型注释:
$ be srb tc
./lib/pdf/reader/buffer.rb:41: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
41 | TOKEN_WHITESPACE=[0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Autocorrect: Use `-a` to autocorrect
./lib/pdf/reader/buffer.rb:41: Replace with T.let([0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20], T::Array[Integer])
41 | TOKEN_WHITESPACE=[0x00, 0x09, 0x0A, 0x0C, 0x0D, 0x20]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
./lib/pdf/reader/buffer.rb:42: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
42 | TOKEN_DELIMITER=[0x25, 0x3C, 0x3E, 0x28, 0x5B, 0x7B, 0x29, 0x5D, 0x7D, 0x2F]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Autocorrect: Use `-a` to autocorrect
./lib/pdf/reader/buffer.rb:42: Replace with T.let([0x25, 0x3C, 0x3E, 0x28, 0x5B, 0x7B, 0x29, 0x5D, 0x7D, 0x2F], T::Array[Integer])
42 | TOKEN_DELIMITER=[0x25, 0x3C, 0x3E, 0x28, 0x5B, 0x7B, 0x29, 0x5D, 0x7D, 0x2F]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
./lib/pdf/reader/buffer.rb:55: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
55 | WHITE_SPACE = [LF, CR, ' ']
^^^^^^^^^^^^^
Autocorrect: Use `-a` to autocorrect
./lib/pdf/reader/buffer.rb:55: Replace with T.let([LF, CR, ' '], T::Array[String])
55 | WHITE_SPACE = [LF, CR, ' ']
^^^^^^^^^^^^^
Errors: 3
建议的修复是使用 T.let()
。但是我不能那样做,因为它需要对冰糕的运行时依赖。
我尝试在我的 RBI 文件中使用 T.let()
,类似于我们在链接问题中解决实例变量问题的方式。但是,这似乎对此错误没有影响:
diff --git a/rbi/pdf-reader.rbi b/rbi/pdf-reader.rbi
index 113f183..f392b0a 100644
--- a/rbi/pdf-reader.rbi
+++ b/rbi/pdf-reader.rbi
@@ -81,7 +81,7 @@ module PDF
CR = "\r"
LF = "\n"
CRLF = "\r\n"
- WHITE_SPACE = [LF, CR, ' ']
+ WHITE_SPACE = T.let(T.unsafe(nil), T::Array[String])
TRAILING_BYTECOUNT = 5000
sig { returns(Integer) }
额外研究
有趣的是,如果我将 RBI 文件中的 T.let()
更改为明显错误的内容,例如:
diff --git a/rbi/pdf-reader.rbi b/rbi/pdf-reader.rbi
index 113f183..251d80d 100644
--- a/rbi/pdf-reader.rbi
+++ b/rbi/pdf-reader.rbi
@@ -81,7 +81,7 @@ module PDF
CR = "\r"
LF = "\n"
CRLF = "\r\n"
- WHITE_SPACE = [LF, CR, ' ']
+ WHITE_SPACE = T.let(T.unsafe(nil), T::Array[Integer])
TRAILING_BYTECOUNT = 5000
sig { returns(Integer) }
然后我得到一个类型错误:
$ srb tc
./lib/pdf/reader/buffer.rb:55: Expected T::Array[Integer] but found T::Array[String] for field https://srb.help/7013
55 | WHITE_SPACE = [LF, CR, " "]
^^^^^^^^^^^^^
Expected T::Array[Integer] for field defined here:
./lib/pdf/reader/buffer.rb:55:
55 | WHITE_SPACE = [LF, CR, " "]
^^^^^^^^^^^
Got T::Array[String] originating from:
./lib/pdf/reader/buffer.rb:55:
55 | WHITE_SPACE = [LF, CR, " "]
^^^^^^^^^^^^^
打点文件中常量的T.let()
似乎并没有被忽略,但这还不足以满足对要定义的常量类型的严格要求。
# TLDR
# NOTE: temporary fix, because this looks like a sorbet bug/feature;
# if you're getting inconsistent results use --max-threads=1
$ srb typecheck --stress-incremental-resolver
这是重现问题的最小设置:
# Gemfile
source "https://rubygems.org"
gem 'sorbet'
# lib/my_gem.rb
module MyGem
WHITE_SPACE = [' ']
end
# sorbet/rbi/my_gem.rbi
module MyGem
# NOTE: Based on sorbet docs, this should tell sorbet the type of this constant
# and should be equivalent to doing this in `lib/my_gem.rb`:
# WHITE_SPACE = T.let([' '], T::Array[String])
WHITE_SPACE = T.let(T.unsafe(nil), T::Array[String])
end
# srb --version
# Sorbet typechecker 0.5.10010 git d2cd1e574d70b4485d961fdf1f457948e4d3988d debug_symbols=true clean=0
# ruby --version
# ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
运行 严格类型检查失败:
$ srb typecheck --typed=strict --dir .
lib/my_gem.rb:2: Constants must have type annotations with T.let when specifying # typed: strict https://srb.help/7027
2 | WHITE_SPACE = [' ']
^^^^^
有一个解析器标志在某处改变了一些东西,我的猜测是修复了错误:
$ srb typecheck --help
...
--stress-incremental-resolver
Force incremental updates to discover resolver & namer bugs
...
$ srb typecheck --stress-incremental-resolver --typed=strict --dir .
No errors! Great job.
为了验证它确实进行了类型检查,我们可以将 .rbi 文件更改为不正确的内容:
# sorbet/rbi/my_gem.rbi
module MyGem
WHITE_SPACE = T.let(T.unsafe(nil), T::Array[Integer])
end
$ srb typecheck --stress-incremental-resolver --typed=strict --dir .
lib/my_gem.rb:42: Expected T::Array[Integer] but found [String(" ")] for field https://srb.help/7013
42 | WHITE_SPACE = [' ']
^^^^^
似乎可以工作并且看起来它自己正确地解析了常量类型 => [String(" ")]
与 [Integer]
.
深入挖掘表明 sorbet parses/rewrites/desugars 我们的文件与解析器标志不同:
$ srb typecheck --print=resolve-tree --typed=strict --dir .
begin
class <emptyTree><<C <root>>> < (::<todo sym>)
nil
end
<emptyTree>
end
begin
class <emptyTree><<C <root>>> < (::<todo sym>)
begin
module ::MyGem<<C MyGem>> < ()
#
# NOTE: This `Magic` bit in particular is not present with --stress-incremental-resolver
#
::MyGem::WHITE_SPACE = ::<Magic>.<suggest-type>([" "])
end
::Sorbet::Private::Static.keep_for_ide(::MyGem)
<emptyTree>
end
end
<emptyTree>
end
begin
class <emptyTree><<C <root>>> < (::<todo sym>)
begin
module ::MyGem<<C MyGem>> < ()
::MyGem::WHITE_SPACE = begin
::Sorbet::Private::Static.keep_for_typechecking(::T::Array.[](::String))
T.let(::T.unsafe(nil), AppliedType {
klass = <C <U Array>>
targs = [
<C <U Elem>> = String
]
})
end
end
::Sorbet::Private::Static.keep_for_ide(::MyGem)
<emptyTree>
end
end
<emptyTree>
end
<Magic>.<suggest-type>
映射到这个方法:
https://github.com/sorbet/sorbet/blob/0.5.10010.20220513160354-d2cd1e574/core/types/calls.cc#L2642
该方法适用于纯字符串 ' '
,但对数组 [' ']
会引发错误,即使一切看起来都已解决,如上文 ... found [String(" ")] ...
所示。它是功能还是错误待定。
此外,将 WHITE_SPACE
从常量转换为方法可能是一个解决方案。