Shadwing declaration
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
Shadow after incompatible declarations is OK
def _(flag: bool): if flag: x: str else: x: int
x: bytes = b"foo"这份文档验证的是:**在一个函数体内,如果同一个变量 `x` 在 `if/else` 两个分支中分别被声明为 `str` 与 `int`(即产生相互冲突的声明),随后再以 `x: bytes = b"foo"` 进行新的带类型标注的声明,ty 不会报错**。测试标题 "Shadow after incompatible declarations is OK"(在不相容声明之后进行遮蔽是允许的)直白地指出了断言结论:该代码不应产生任何诊断。 值得注意的是,该文档除 H1 标题("Shadwing declaration",原文拼写即如此)与 H2 小节外,不包含任何 `# error:` 或 `# revealed:` 断言注释——在 mdtest 语义中,**没有断言即代表该测试期待零诊断**,类型检查器必须接受这段代码。 ## 三、核心概念拆解:声明 vs 赋值 要理解上述测试为什么「合法」,必须区分 ty 内部的两个概念: 1. **声明(declaration)**:带类型标注的绑定,如 `x: str`、`x: bytes = b"foo"`。声明是显式的类型契约,类型检查器会将其写入 use-def 分析结果,作为该位置后续引用的目标类型依据。 2. **赋值(assignment)**:不带标注的绑定,如 `x = 1`。赋值会更新绑定值,但不会修改已声明的类型契约。 在 [builder.rs](https://link.gitcode.com/i/f31a1de61be3a1dacc356ca5b30f80bf) 的 `add_binding` 方法中可以看到类型检查器处理「新绑定与已有声明」的底层逻辑: - 它从 `use_def_map` 取出该绑定对应的**声明集合**(`declarations_at_binding`), - 调用 `place_from_declarations_with_reachability_cache` 把这些声明合并成当前 place 的目标类型; - 若声明之间存在冲突,会尝试报告 `CONFLICTING_DECLARATIONS` 诊断("Conflicting declared types for `{place}`: ..."),源码注释里还留有 TODO("point out the conflicting declarations in the diagnostic?")。 也就是说,**同一作用域内多个相互冲突的声明本身可能触发 "Conflicting declared types" 提示**——但这与本文主题并不矛盾:mdtest 文档验证的是「在冲突声明之后**新增一个**遮蔽声明是合法的」,即新的 `x: bytes` 声明不会被当作对既有声明的非法覆盖而报错。 ## 四、对照实验:什么时候「遮蔽」会报错? 同一主题下的其他 mdtest 文档给出了遮蔽的完整图景,可用于对照理解: ### 4.1 隐式遮蔽(不合法) 在 [diagnostics/shadowing.md](https://link.gitcode.com/i/4304411bafc540460d921859e10219e5) 中,用 `# snapshot:` 断言展示了隐式遮蔽会触发 `invalid-assignment`: ```py class C: ... C = 1 # snapshot: invalid-assignment对应诊断输出:
error[invalid-assignment]: Object of type `Literal[1]` is not assignable to `<class 'C'>` info: Implicit shadowing of class `C`. Add an annotation to make it explicit if this is intentional函数同理:
def f(): ... f = 1 # snapshot: invalid-assignmenterror[invalid-assignment]: Object of type `Literal[1]` is not assignable to `def f() -> Unknown` info: Implicit shadowing of function `f`. Add an annotation to make it explicit if this is intentional结论:用一个不带标注的赋值去覆盖类或函数的声明属于「隐式遮蔽」,ty 会报invalid-assignment错误,并附带 info 提示(“如需有意为之,请加上类型标注使其显式化”)。
4.2 显式遮蔽(合法)
同样的场景,只要加上类型标注,就变成合法:
- shadowing/class.md:
class C: ... C: int = 1- shadowing/function.md 中的「参数遮蔽」用例:
def f(x: str): x: int = int(x)参数x被声明为str,函数体内又重新声明为int并赋值——不产生任何诊断。这正与本文主题文档的结论互相印证:显式声明(带注解)对既有声明的覆盖,是类型检查器允许的遮蔽行为。
4.3 def 声明的互相遮蔽
shadowing/function.md 还指出:def语句本身是声明,因此一个def可以遮蔽另一个def,也可以遮蔽先前的非def声明,且不会报错:
f = 1 reveal_type(f) # revealed: Literal[1] def f(): ... reveal_type(f) # revealed: def f() -> Unknown f: int = 1 reveal_type(f) # revealed: Literal[1] def f(): ... reveal_type(f) # revealed: def f() -> Unknownreveal_type的输出随着每次新声明实时更新,证明声明确实覆盖了此前的类型。
4.4 属性赋值不适用遮蔽提示
diagnostics/shadowing.md 中还有一个边界用例:属性(attribute)赋值不触发遮蔽提示。对config.optionxform = str这类属性写入,ty 走的是属性赋值检查,报告的是属性类型不匹配,而不是「隐式遮蔽」:
from configparser import ConfigParser config = ConfigParser() config.optionxform = str # snapshot: invalid-assignmenterror[invalid-assignment]: Object of type `<class 'str'>` is not assignable to attribute `optionxform` of type `def optionxform(self, optionstr: str) -> str`五、遮蔽提示的源码实现
遮蔽 info 提示的实现位于 types/diagnostic.rs 的report_invalid_assignment中:当目标节点是ExprName(即普通名字赋值)且目标类型是类字面量或函数字面量时,诊断追加一条 info:
Type::ClassLiteral(class) => { diag.info(format_args!( "Implicit shadowing of class `{}`. \ Add an annotation to make it explicit if this is intentional", class.name(context.db()), )); } Type::FunctionLiteral(function) => { diag.info(format_args!( "Implicit shadowing of function `{}`. \ Add an annotation to make it explicit if this is intentional", function.name(context.db()), )); }这一实现细节印证了 mdtest 快照中的提示文案,也解释了为什么「加上注解」即可消除错误——注解会让该绑定从「赋值」升级为「声明」,从而进入合法的显式遮蔽路径。
六、如何在本地复现与扩展验证
6.1 运行 mdtest
所有 Markdown 测试均由 mdtest.rs 通过datatest_stable::harness!统一驱动(root 为./resources/mdtest,匹配所有.md文件)。执行:
# 运行 ty_python_semantic 下全部 mdtest cargo test -p ty_python_semantic -- mdtest # 按文件名过滤到本文主题文档 cargo test -p ty_python_semantic --test mdtest -- shadowing也可使用带监视模式的 Python 运行器(文件变更即自动重跑对应测试):
uv run crates/ty_python_semantic/mdtest.py6.2 修改/新增用例的建议
- 复制 variable_declaration.md 到同一目录,修改代码块内容即可得到新用例;
- 如需断言诊断,使用行尾注释
# error: [invalid-assignment]或# revealed: Literal[...]; - 如需完整诊断快照,使用
# snapshot:行 + 紧随其后的
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考