[ty] Always infer the submodule literal for submodule attribute-access, even when we emit a diagnostic

This commit is contained in:
Alex Waygood 2025-11-24 19:34:31 +00:00 committed by Aria Desires
parent a2f9e5a680
commit a4be2aad04
6 changed files with 44 additions and 36 deletions

View File

@ -2770,9 +2770,9 @@ import foo
import baz import baz
# error: [possibly-missing-attribute] # error: [possibly-missing-attribute]
reveal_type(foo.bar) # revealed: Unknown reveal_type(foo.bar) # revealed: <module 'foo.bar'>
# error: [possibly-missing-attribute] # error: [possibly-missing-attribute]
reveal_type(baz.bar) # revealed: Unknown reveal_type(baz.bar) # revealed: <module 'baz.bar'>
``` ```
## References ## References

View File

@ -61,7 +61,7 @@ import mypackage
reveal_type(mypackage.imported.X) # revealed: int reveal_type(mypackage.imported.X) # revealed: int
# error: [possibly-missing-attribute] "Submodule `fails` may not be available" # error: [possibly-missing-attribute] "Submodule `fails` may not be available"
reveal_type(mypackage.fails.Y) # revealed: Unknown reveal_type(mypackage.fails.Y) # revealed: int
``` ```
### In Non-Stub ### In Non-Stub
@ -91,7 +91,7 @@ import mypackage
reveal_type(mypackage.imported.X) # revealed: int reveal_type(mypackage.imported.X) # revealed: int
# error: [possibly-missing-attribute] "Submodule `fails` may not be available" # error: [possibly-missing-attribute] "Submodule `fails` may not be available"
reveal_type(mypackage.fails.Y) # revealed: Unknown reveal_type(mypackage.fails.Y) # revealed: int
``` ```
## Absolute `from` Import of Direct Submodule in `__init__` ## Absolute `from` Import of Direct Submodule in `__init__`
@ -126,7 +126,7 @@ import mypackage
reveal_type(mypackage.imported.X) # revealed: int reveal_type(mypackage.imported.X) # revealed: int
# error: [possibly-missing-attribute] "Submodule `fails` may not be available" # error: [possibly-missing-attribute] "Submodule `fails` may not be available"
reveal_type(mypackage.fails.Y) # revealed: Unknown reveal_type(mypackage.fails.Y) # revealed: int
``` ```
### In Non-Stub ### In Non-Stub
@ -156,7 +156,7 @@ import mypackage
reveal_type(mypackage.imported.X) # revealed: int reveal_type(mypackage.imported.X) # revealed: int
# error: [possibly-missing-attribute] "Submodule `fails` may not be available" # error: [possibly-missing-attribute] "Submodule `fails` may not be available"
reveal_type(mypackage.fails.Y) # revealed: Unknown reveal_type(mypackage.fails.Y) # revealed: int
``` ```
## Import of Direct Submodule in `__init__` ## Import of Direct Submodule in `__init__`
@ -185,7 +185,7 @@ import mypackage
# TODO: this could work and would be nice to have? # TODO: this could work and would be nice to have?
# error: [possibly-missing-attribute] "Submodule `imported` may not be available" # error: [possibly-missing-attribute] "Submodule `imported` may not be available"
reveal_type(mypackage.imported.X) # revealed: Unknown reveal_type(mypackage.imported.X) # revealed: int
``` ```
### In Non-Stub ### In Non-Stub
@ -209,7 +209,7 @@ import mypackage
# TODO: this could work and would be nice to have # TODO: this could work and would be nice to have
# error: [possibly-missing-attribute] "Submodule `imported` may not be available" # error: [possibly-missing-attribute] "Submodule `imported` may not be available"
reveal_type(mypackage.imported.X) # revealed: Unknown reveal_type(mypackage.imported.X) # revealed: int
``` ```
## Relative `from` Import of Nested Submodule in `__init__` ## Relative `from` Import of Nested Submodule in `__init__`
@ -243,9 +243,9 @@ import mypackage
reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'> reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'>
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: Unknown reveal_type(mypackage.submodule.nested) # revealed: <module 'mypackage.submodule.nested'>
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: Unknown reveal_type(mypackage.submodule.nested.X) # revealed: int
# error: [unresolved-attribute] "has no member `nested`" # error: [unresolved-attribute] "has no member `nested`"
reveal_type(mypackage.nested) # revealed: Unknown reveal_type(mypackage.nested) # revealed: Unknown
# error: [unresolved-attribute] "has no member `nested`" # error: [unresolved-attribute] "has no member `nested`"
@ -281,9 +281,9 @@ import mypackage
reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'> reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'>
# TODO: this would be nice to support # TODO: this would be nice to support
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: Unknown reveal_type(mypackage.submodule.nested) # revealed: <module 'mypackage.submodule.nested'>
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: Unknown reveal_type(mypackage.submodule.nested.X) # revealed: int
reveal_type(mypackage.nested) # revealed: <module 'mypackage.submodule.nested'> reveal_type(mypackage.nested) # revealed: <module 'mypackage.submodule.nested'>
reveal_type(mypackage.nested.X) # revealed: int reveal_type(mypackage.nested.X) # revealed: int
``` ```
@ -319,9 +319,9 @@ import mypackage
reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'> reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'>
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: Unknown reveal_type(mypackage.submodule.nested) # revealed: <module 'mypackage.submodule.nested'>
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: Unknown reveal_type(mypackage.submodule.nested.X) # revealed: int
# error: [unresolved-attribute] "has no member `nested`" # error: [unresolved-attribute] "has no member `nested`"
reveal_type(mypackage.nested) # revealed: Unknown reveal_type(mypackage.nested) # revealed: Unknown
# error: [unresolved-attribute] "has no member `nested`" # error: [unresolved-attribute] "has no member `nested`"
@ -357,9 +357,9 @@ import mypackage
reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'> reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'>
# TODO: this would be nice to support # TODO: this would be nice to support
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: Unknown reveal_type(mypackage.submodule.nested) # revealed: <module 'mypackage.submodule.nested'>
# error: [possibly-missing-attribute] "Submodule `nested` may not be available" # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: Unknown reveal_type(mypackage.submodule.nested.X) # revealed: int
reveal_type(mypackage.nested) # revealed: <module 'mypackage.submodule.nested'> reveal_type(mypackage.nested) # revealed: <module 'mypackage.submodule.nested'>
reveal_type(mypackage.nested.X) # revealed: int reveal_type(mypackage.nested.X) # revealed: int
``` ```
@ -394,11 +394,13 @@ X: int = 42
import mypackage import mypackage
# error: [possibly-missing-attribute] "Submodule `submodule` may not be available" # error: [possibly-missing-attribute] "Submodule `submodule` may not be available"
reveal_type(mypackage.submodule) # revealed: Unknown reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'>
# error: [possibly-missing-attribute] "Submodule `submodule` may not be available" # error: [possibly-missing-attribute] "Submodule `submodule` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: Unknown # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: <module 'mypackage.submodule.nested'>
# error: [possibly-missing-attribute] "Submodule `submodule` may not be available" # error: [possibly-missing-attribute] "Submodule `submodule` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: Unknown # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: int
``` ```
### In Non-Stub ### In Non-Stub
@ -430,11 +432,13 @@ import mypackage
# TODO: this would be nice to support # TODO: this would be nice to support
# error: [possibly-missing-attribute] "Submodule `submodule` may not be available" # error: [possibly-missing-attribute] "Submodule `submodule` may not be available"
reveal_type(mypackage.submodule) # revealed: Unknown reveal_type(mypackage.submodule) # revealed: <module 'mypackage.submodule'>
# error: [possibly-missing-attribute] "Submodule `submodule` may not be available" # error: [possibly-missing-attribute] "Submodule `submodule` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: Unknown # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested) # revealed: <module 'mypackage.submodule.nested'>
# error: [possibly-missing-attribute] "Submodule `submodule` may not be available" # error: [possibly-missing-attribute] "Submodule `submodule` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: Unknown # error: [possibly-missing-attribute] "Submodule `nested` may not be available"
reveal_type(mypackage.submodule.nested.X) # revealed: int
``` ```
## Relative `from` Import of Direct Submodule in `__init__`, Mismatched Alias ## Relative `from` Import of Direct Submodule in `__init__`, Mismatched Alias
@ -461,7 +465,7 @@ X: int = 42
import mypackage import mypackage
# error: [possibly-missing-attribute] "Submodule `imported` may not be available" # error: [possibly-missing-attribute] "Submodule `imported` may not be available"
reveal_type(mypackage.imported.X) # revealed: Unknown reveal_type(mypackage.imported.X) # revealed: int
# error: [unresolved-attribute] "has no member `imported_m`" # error: [unresolved-attribute] "has no member `imported_m`"
reveal_type(mypackage.imported_m.X) # revealed: Unknown reveal_type(mypackage.imported_m.X) # revealed: Unknown
``` ```
@ -487,7 +491,7 @@ import mypackage
# TODO: this would be nice to support, as it works at runtime # TODO: this would be nice to support, as it works at runtime
# error: [possibly-missing-attribute] "Submodule `imported` may not be available" # error: [possibly-missing-attribute] "Submodule `imported` may not be available"
reveal_type(mypackage.imported.X) # revealed: Unknown reveal_type(mypackage.imported.X) # revealed: int
reveal_type(mypackage.imported_m.X) # revealed: int reveal_type(mypackage.imported_m.X) # revealed: int
``` ```
@ -759,7 +763,7 @@ reveal_type(imported.X) # revealed: int
# error: [unresolved-attribute] "has no member `fails`" # error: [unresolved-attribute] "has no member `fails`"
reveal_type(imported.fails.Y) # revealed: Unknown reveal_type(imported.fails.Y) # revealed: Unknown
# error: [possibly-missing-attribute] "Submodule `fails` may not be available" # error: [possibly-missing-attribute] "Submodule `fails` may not be available"
reveal_type(mypackage.fails.Y) # revealed: Unknown reveal_type(mypackage.fails.Y) # revealed: int
``` ```
### In Non-Stub ### In Non-Stub
@ -792,7 +796,7 @@ from mypackage import imported
reveal_type(imported.X) # revealed: int reveal_type(imported.X) # revealed: int
reveal_type(imported.fails.Y) # revealed: int reveal_type(imported.fails.Y) # revealed: int
# error: [possibly-missing-attribute] "Submodule `fails`" # error: [possibly-missing-attribute] "Submodule `fails`"
reveal_type(mypackage.fails.Y) # revealed: Unknown reveal_type(mypackage.fails.Y) # revealed: int
``` ```
## Fractal Re-export Nameclash Problems ## Fractal Re-export Nameclash Problems

View File

@ -248,7 +248,7 @@ from . import foo
import package import package
# error: [possibly-missing-attribute] # error: [possibly-missing-attribute]
reveal_type(package.foo.X) # revealed: Unknown reveal_type(package.foo.X) # revealed: int
``` ```
## Relative imports at the top of a search path ## Relative imports at the top of a search path

View File

@ -31,9 +31,9 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/attributes.md
2 | import baz 2 | import baz
3 | 3 |
4 | # error: [possibly-missing-attribute] 4 | # error: [possibly-missing-attribute]
5 | reveal_type(foo.bar) # revealed: Unknown 5 | reveal_type(foo.bar) # revealed: <module 'foo.bar'>
6 | # error: [possibly-missing-attribute] 6 | # error: [possibly-missing-attribute]
7 | reveal_type(baz.bar) # revealed: Unknown 7 | reveal_type(baz.bar) # revealed: <module 'baz.bar'>
``` ```
# Diagnostics # Diagnostics
@ -43,10 +43,10 @@ warning[possibly-missing-attribute]: Submodule `bar` may not be available as an
--> src/main.py:5:13 --> src/main.py:5:13
| |
4 | # error: [possibly-missing-attribute] 4 | # error: [possibly-missing-attribute]
5 | reveal_type(foo.bar) # revealed: Unknown 5 | reveal_type(foo.bar) # revealed: <module 'foo.bar'>
| ^^^^^^^ | ^^^^^^^
6 | # error: [possibly-missing-attribute] 6 | # error: [possibly-missing-attribute]
7 | reveal_type(baz.bar) # revealed: Unknown 7 | reveal_type(baz.bar) # revealed: <module 'baz.bar'>
| |
help: Consider explicitly importing `foo.bar` help: Consider explicitly importing `foo.bar`
info: rule `possibly-missing-attribute` is enabled by default info: rule `possibly-missing-attribute` is enabled by default
@ -57,9 +57,9 @@ info: rule `possibly-missing-attribute` is enabled by default
warning[possibly-missing-attribute]: Submodule `bar` may not be available as an attribute on module `baz` warning[possibly-missing-attribute]: Submodule `bar` may not be available as an attribute on module `baz`
--> src/main.py:7:13 --> src/main.py:7:13
| |
5 | reveal_type(foo.bar) # revealed: Unknown 5 | reveal_type(foo.bar) # revealed: <module 'foo.bar'>
6 | # error: [possibly-missing-attribute] 6 | # error: [possibly-missing-attribute]
7 | reveal_type(baz.bar) # revealed: Unknown 7 | reveal_type(baz.bar) # revealed: <module 'baz.bar'>
| ^^^^^^^ | ^^^^^^^
| |
help: Consider explicitly importing `baz.bar` help: Consider explicitly importing `baz.bar`

View File

@ -629,7 +629,7 @@ from module2 import imported as other_imported
from ty_extensions import TypeOf, static_assert, is_equivalent_to from ty_extensions import TypeOf, static_assert, is_equivalent_to
# error: [possibly-missing-attribute] # error: [possibly-missing-attribute]
reveal_type(imported.abc) # revealed: Unknown reveal_type(imported.abc) # revealed: <module 'imported.abc'>
reveal_type(other_imported.abc) # revealed: <module 'imported.abc'> reveal_type(other_imported.abc) # revealed: <module 'imported.abc'>

View File

@ -9148,7 +9148,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
{ {
let mut maybe_submodule_name = module_name.clone(); let mut maybe_submodule_name = module_name.clone();
maybe_submodule_name.extend(&relative_submodule); maybe_submodule_name.extend(&relative_submodule);
if resolve_module(db, &maybe_submodule_name).is_some() { if let Some(submodule) = resolve_module(db, &maybe_submodule_name) {
if let Some(builder) = self if let Some(builder) = self
.context .context
.report_lint(&POSSIBLY_MISSING_ATTRIBUTE, attribute) .report_lint(&POSSIBLY_MISSING_ATTRIBUTE, attribute)
@ -9161,7 +9161,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
"Consider explicitly importing `{maybe_submodule_name}`" "Consider explicitly importing `{maybe_submodule_name}`"
)); ));
} }
return fallback(); return TypeAndQualifiers::new(
Type::module_literal(db, self.file(), submodule),
TypeOrigin::Inferred,
TypeQualifiers::empty(),
);
} }
} }
} }