Skip to content

aliases_implicit: implicit type aliases are detected by an uppercase-first-letter naming heuristic #411

Description

@MelbourneDeveloper

Split out of #408. Confirmed live on main @ da74283 and on the bidirectionaltype-inference branch.

The code

crates/basilisk-checker/src/rules/aliases_implicit.rs:405-408, in build_alias_info_map's implicit-alias pass:

// Heuristic: if the name starts with uppercase and the RHS is a type
// expression (name, subscript, or union), treat it as an implicit alias
let first_char = var.name.chars().next().unwrap_or('a');
if !first_char.is_ascii_uppercase() {
    continue;
}

A module-level assignment is recognised as an implicit type alias only if its name begins with an uppercase ASCII letter. Naming convention is load-bearing: it decides whether the binding enters alias_map at all, and therefore whether any downstream check sees it — parameterization, ParamSpec, bounds, and union instantiation.

Why it looks correct

The conformance file names every implicit alias GoodTypeAlias1GoodTypeAlias13, ListAlias, ListOrSetAlias. Uppercase-first holds for all of them. PEP 613 and PEP 695 impose no such requirement — the typing spec says nothing about the case of an alias's name.

Failing cases

Union instantiation, silently accepted:

list_or_set = list | set
list_or_set()   # should error: cannot instantiate a union alias

ListOrSetAlias() is caught (aliases_implicit.py:133); the identical construct with a lowercase name is not, because check_union_alias_instantiation looks the callee up in alias_map and the binding was never inserted.

Parameterization, silently accepted:

my_alias = list[int]
def f(x: my_alias[str]) -> None: ...   # already specialized — should error

Also non-ASCII: Ålias = list | set fails is_ascii_uppercase and is skipped, so the rule is inconsistent for any identifier outside ASCII, which Python permits.

Secondary — looks_like_type_expression

The companion gate at :529 is a character blacklist over RHS text:

!text.contains(['=', '+', '-', '*', '/', '%', '!', '~', '^', '&', '{', '}'])

It rejects any RHS containing those characters anywhere, including inside a string forward reference or a Literal[...] argument, and accepts anything else. It is a second textual heuristic stacked on the first.

Fix

Decide alias-hood from binding information the resolver already has — the assignment's target, its RHS expression node, and whether the RHS is a type expression — not from the spelling of the name. Both gates should be replaced by structural analysis of the Ruff AST, matching the approach now used in aliases_type_statement.rs.

Needs off-suite regression tests: every implicit alias in the conformance suite is uppercase-first, so no suite case can detect this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    spec-violationCode functionality/logic does not match its spec (see SPEC-CONFORMANCE-AUDIT-PLAN.md)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions