Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# `reveal_type`

`reveal_type` is used to inspect the type of an expression at a given point in the code. It is often
used for debugging and understanding how types are inferred by the type checker.

## Basic usage

```py
from typing_extensions import reveal_type

reveal_type(1) # revealed: Literal[1]
```

This also works with the fully qualified name:

```py
import typing_extensions

typing_extensions.reveal_type(1) # revealed: Literal[1]
```

The return type of `reveal_type` is the type of the argument:

```py
from typing_extensions import assert_type

def _(x: int):
y = reveal_type(x) # revealed: int
assert_type(y, int)
```

## Without importing it

For convenience, we also allow `reveal_type` to be used without importing it, even if that would
fail at runtime:

```py
reveal_type(1) # revealed: Literal[1]
```

## In unreachable code

Make sure that `reveal_type` works even in unreachable code.

### When importing it

```py
from typing_extensions import reveal_type
import typing_extensions

if False:
reveal_type(1) # revealed: Literal[1]
typing_extensions.reveal_type(1) # revealed: Literal[1]

if 1 + 1 != 2:
reveal_type(1) # revealed: Literal[1]
typing_extensions.reveal_type(1) # revealed: Literal[1]
```

### Without importing it

```py
if False:
reveal_type(1) # revealed: Literal[1]

if 1 + 1 != 2:
reveal_type(1) # revealed: Literal[1]
```
32 changes: 21 additions & 11 deletions crates/ty_python_semantic/src/types/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1835,17 +1835,7 @@ impl KnownFunction {
.arguments_for_parameter(call_arguments, 0)
.fold(UnionBuilder::new(db), |builder, (_, ty)| builder.add(ty))
.build();
if let Some(builder) =
context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
{
let mut diag = builder.into_diagnostic("Revealed type");
let span = context.span(&call_expression.arguments.args[0]);
diag.annotate(Annotation::primary(span).message(format_args!(
"`{}`",
revealed_type
.display_with(db, DisplaySettings::default().preserve_long_unions())
)));
}
report_revealed_type(context, revealed_type, &call_expression.arguments.args[0]);
}

KnownFunction::HasMember => {
Expand Down Expand Up @@ -2235,6 +2225,26 @@ impl KnownFunction {
}
}

/// Emit a `revealed-type` diagnostic for a `reveal_type(...)` call.
pub(super) fn report_revealed_type<'db>(
context: &InferContext<'db, '_>,
revealed_type: Type<'db>,
argument_node: &ast::Expr,
) {
if let Some(builder) = context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info) {
let mut diag = builder.into_diagnostic("Revealed type");
diag.annotate(
Annotation::primary(context.span(argument_node)).message(format_args!(
"`{}`",
revealed_type.display_with(
context.db(),
DisplaySettings::default().preserve_long_unions()
)
)),
);
}
}

#[cfg(test)]
pub(crate) mod tests {
use strum::IntoEnumIterator;
Expand Down
17 changes: 16 additions & 1 deletion crates/ty_python_semantic/src/types/infer/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ use crate::types::diagnostic::{
report_unsupported_comparison,
};
use crate::types::enums::{enum_ignored_names, is_enum_class_by_inheritance};
use crate::types::function::{FunctionType, KnownFunction};
use crate::types::function::{FunctionType, KnownFunction, report_revealed_type};
use crate::types::generics::{InferableTypeVars, SpecializationBuilder, bind_typevar};
use crate::types::infer::builder::named_tuple::NamedTupleKind;
use crate::types::infer::builder::paramspec_validation::validate_paramspec_components;
Expand Down Expand Up @@ -7199,6 +7199,21 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
);
}
}
Type::Never => {
// In unreachable sections of code, we infer `Never` for symbols that were
// defined outside the unreachable part. We still want to emit revealed-type
// diagnostics in these sections, so check on the name of the callable here
// and assume that it's actually `typing.reveal_type`.
let is_reveal_type = match func.as_ref() {
ast::Expr::Name(name) => name.id == "reveal_type",
ast::Expr::Attribute(attr) => attr.attr.id == "reveal_type",
_ => false,
};
if is_reveal_type && let Some(first_arg) = arguments.args.first() {
let revealed_ty = self.expression_type(first_arg);
report_revealed_type(&self.context, revealed_ty, first_arg);
}
}
_ => {}
}
}
Expand Down
Loading