Nimblesite / Nimblesite/Basilisk
aliases_implicit: ParamSpec argument check is a shape guess ("probably wrong") that never identifies the ParamSpec position
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 54
- Forks
- 3
- PR merge metrics
- No merged PRs in 30d
Description
Split out of #408. Confirmed live on main @ da74283 and on the bidirectionaltype-inference branch (that branch's only change to this file is routing is_assignable_to_bound through a SubtypingContext; this code is untouched).
The code
crates/basilisk-checker/src/rules/aliases_implicit.rs:693-698
// If all args are simple types (no `[...]` or `...`),
// the ParamSpec arg is probably wrong
let all_simple = args.iter().all(|arg| {
let trimmed = arg.trim();
!trimmed.contains('[') && trimmed != "..."
});
if all_simple && args.len() > 1 {
// → error: "Invalid type argument for `ParamSpec` parameter in `{base}`"
}
This emits a hard error on a shape guess. Its own comment says "probably".
Why it looks correct
The alias positions come from collect_typevar_bounds, which records TypeVar names in order of first textual appearance in the RHS. For the conformance suite's
GoodTypeAlias9 = Callable[Concatenate[int, P], R]
that yields [P, R], so GoodTypeAlias9[int, int] gives arg_count == typevar_count == 2, both args "simple", len > 1 → fires. That is aliases_implicit.py:80, and it is the only case the branch was ever exercised against.
It never identifies the ParamSpec position
The check never asks which argument corresponds to the ParamSpec. It asks whether all arguments look bracket-free. Consequences:
False negative — invalid ParamSpec argument accepted:
def f(x: GoodTypeAlias9[int, [str]]): ...
Position 0 is P (a ParamSpec) and receives int — invalid, it must be a parameter list or .... Position 1 is R (a TypeVar) and receives [str] — also invalid, that is not a type. Because "[str]" contains [, all_simple is false and the entire branch is skipped. Two errors, zero diagnostics.
False negative — nested annotations are never reached:
annotation_base_name splits on the first [ and check_single_annotation is called only from the two top-level loops (:606, :627) — it never recurses into type arguments. So:
def f(x: list[GoodTypeAlias2[int]]): ...
resolves the base name to list, finds no alias, and returns. The inner misuse — the same error the suite checks at top level on line 76 — is invisible at any nesting depth.
Fix
Determine parameter kinds from the alias's declared type parameters, match each argument to its position, and validate a ParamSpec argument as a parameter list, Concatenate[...], ..., or another ParamSpec. Recurse into type arguments so nested annotations are checked. Until that exists, this diagnostic should not be emitted — a check whose comment says "probably" must not produce an error.
Needs off-suite regression tests: both cases above are invisible to the conformance suite.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in crates/basilisk-checker/src/rules/aliases_implicit.rs around lines 606, 627, and 693-698, then trace how collect_typevar_bounds records declared parameters. Add off-suite regression tests for invalid top-level and nested ParamSpec arguments, and verify that parameter kinds are matched by position, nested type arguments are checked, and the speculative diagnostic is not emitted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust
- Domain
- compilers, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100