Skip to content
Open
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
31 changes: 28 additions & 3 deletions pyrefly/lib/commands/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,20 @@ impl ReportArgs {
_ => continue,
};
match binding {
BindingExport::AnnotatedForward(annot_idx, _) => {
BindingExport::AnnotatedForward(annot_idx, key_idx) => {
// IMPLICIT: type aliases are type-level constructs with 0 slots.
if matches!(
bindings.get(*key_idx),
Binding::TypeAlias(_) | Binding::TypeAliasRef(_)
) {
variables.push(Variable {
name: qualified_name,
annotation: None,
slots: SlotCounts::default(),
location,
});
continue;
}
let annotation_text = match bindings.get(*annot_idx) {
BindingAnnotation::AnnotateExpr(_, expr, _) => {
Some(module.code_at(expr.range()).to_owned())
Expand All @@ -531,8 +544,12 @@ impl ReportArgs {
match bindings.get(*idx) {
// Skip injected implicit globals
Binding::Global(_) => {}
// IMPLICIT: special type forms have 0 slots
Binding::TypeVar(_) | Binding::ParamSpec(_) | Binding::TypeVarTuple(_) => {
// IMPLICIT: special type forms and type aliases have 0 slots
Binding::TypeVar(_)
| Binding::ParamSpec(_)
| Binding::TypeVarTuple(_)
| Binding::TypeAlias(_)
| Binding::TypeAliasRef(_) => {
variables.push(Variable {
name: qualified_name,
annotation: None,
Expand Down Expand Up @@ -2248,4 +2265,12 @@ mod tests {
let report = build_module_report_for_test("partial_any.py");
compare_snapshot("partial_any.expected.json", &report);
}

/// Type aliases (explicit `TypeAlias`, bare assignments, PEP 695, TypeAliasType)
/// are type-level constructs and should have 0 typable slots.
#[test]
fn test_report_type_aliases() {
let report = build_module_report_for_test("type_aliases.py");
compare_snapshot("type_aliases.expected.json", &report);
}
}
127 changes: 127 additions & 0 deletions pyrefly/lib/test/report/test_files/type_aliases.expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{
"name": "test",
"names": [
"test.T",
"test.Alias1",
"test.Alias2",
"test.Alias3",
"test.x",
"test.y",
"test.some_func",
"test.SomeClass"
],
"line_count": 30,
"symbol_reports": [
{
"kind": "attr",
"name": "test.T",
"n_typable": 0,
"n_typed": 0,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 8,
"column": 1
}
},
{
"kind": "attr",
"name": "test.Alias1",
"n_typable": 0,
"n_typed": 0,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 11,
"column": 1
}
},
{
"kind": "attr",
"name": "test.Alias2",
"n_typable": 0,
"n_typed": 0,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 14,
"column": 1
}
},
{
"kind": "attr",
"name": "test.Alias3",
"n_typable": 0,
"n_typed": 0,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 17,
"column": 1
}
},
{
"kind": "attr",
"name": "test.x",
"n_typable": 1,
"n_typed": 1,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 20,
"column": 1
}
},
{
"kind": "attr",
"name": "test.y",
"n_typable": 1,
"n_typed": 1,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 21,
"column": 1
}
},
{
"kind": "function",
"name": "test.some_func",
"n_typable": 1,
"n_typed": 1,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 24,
"column": 1
}
},
{
"kind": "class",
"name": "test.SomeClass",
"n_typable": 0,
"n_typed": 0,
"n_any": 0,
"n_untyped": 0,
"location": {
"line": 28,
"column": 1
}
}
],
"type_ignores": [],
"n_typable": 3,
"n_typed": 3,
"n_any": 0,
"n_untyped": 0,
"coverage": 100.0,
"strict_coverage": 100.0,
"n_functions": 1,
"n_methods": 0,
"n_function_params": 0,
"n_method_params": 0,
"n_classes": 1,
"n_attrs": 6,
"n_properties": 0,
"n_type_ignores": 0
}
29 changes: 29 additions & 0 deletions pyrefly/lib/test/report/test_files/type_aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from typing import TypeAlias, TypeAliasType, TypeVar

T = TypeVar("T")

# Implicit type alias (bare assignment with recognizable type RHS)
Alias1 = list[int]

# Explicit TypeAlias annotation (legacy form)
Alias2: TypeAlias = int | str

# TypeAliasType call (runtime equivalent of PEP 695)
Alias3 = TypeAliasType("Alias3", int)

# Regular typed variable for baseline comparison
x: int = 42
y: type[int] = int


def some_func() -> None:
pass


class SomeClass:
my_field = 42
Loading