Compare commits
3 Commits
claude/fix
...
micha/make
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
308373124d | ||
|
|
990d0a8999 | ||
|
|
99beabdde8 |
@@ -456,6 +456,40 @@ reveal_type(int_container) # revealed: Container[int]
|
||||
reveal_type(int_container.set_value(1)) # revealed: Container[int]
|
||||
```
|
||||
|
||||
## Generic class with bounded type variable
|
||||
|
||||
This is a regression test for <https://github.com/astral-sh/ty/issues/2467>.
|
||||
|
||||
Calling a method on a generic class instance should work when the type parameter is specialized with
|
||||
a type that satisfies a bound.
|
||||
|
||||
```py
|
||||
from typing import NewType
|
||||
|
||||
class Base: ...
|
||||
|
||||
class C[T: Base]:
|
||||
x: T
|
||||
|
||||
def g(self) -> None:
|
||||
pass
|
||||
|
||||
# Calling a method on a specialized instance should not produce an error
|
||||
C[Base]().g()
|
||||
|
||||
# Test with a NewType bound
|
||||
K = NewType("K", int)
|
||||
|
||||
class D[T: K]:
|
||||
x: T
|
||||
|
||||
def h(self) -> None:
|
||||
pass
|
||||
|
||||
# Calling a method on a specialized instance should not produce an error
|
||||
D[K]().h()
|
||||
```
|
||||
|
||||
## Protocols
|
||||
|
||||
TODO: <https://typing.python.org/en/latest/spec/generics.html#use-in-protocols>
|
||||
|
||||
@@ -6942,7 +6942,7 @@ impl<'db> TypeMapping<'_, 'db> {
|
||||
/// put in a [`KnownInstance::ConstraintSet`]. We don't actually manipulate these as part of using
|
||||
/// constraint sets to check things like assignability; they're only used as a debugging aid in
|
||||
/// mdtests. That means there's no need for this to be interned; being tracked is sufficient.
|
||||
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct TrackedConstraintSet<'db> {
|
||||
constraints: ConstraintSet<'db>,
|
||||
|
||||
@@ -715,17 +715,6 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
})
|
||||
}
|
||||
// All other `NewType` assignments fall back to the concrete base type.
|
||||
(Type::NewTypeInstance(self_newtype), _) => {
|
||||
self_newtype.concrete_base_type(db).has_relation_to_impl(
|
||||
db,
|
||||
target,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
}
|
||||
|
||||
(Type::Union(union), _) => union.elements(db).iter().when_all(db, |&elem_ty| {
|
||||
elem_ty.has_relation_to_impl(
|
||||
@@ -869,6 +858,21 @@ impl<'db> Type<'db> {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
// All other `NewType` assignments fall back to the concrete base type.
|
||||
// This case must come after the TypeVar cases above, so that when checking
|
||||
// `NewType <: TypeVar`, we use the TypeVar handling rather than falling back
|
||||
// to the NewType's concrete base type.
|
||||
(Type::NewTypeInstance(self_newtype), _) => {
|
||||
self_newtype.concrete_base_type(db).has_relation_to_impl(
|
||||
db,
|
||||
target,
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
)
|
||||
}
|
||||
|
||||
// Note that the definition of `Type::AlwaysFalsy` depends on the return value of `__bool__`.
|
||||
// If `__bool__` always returns True or False, it can be treated as a subtype of `AlwaysTruthy` or `AlwaysFalsy`, respectively.
|
||||
(left, Type::AlwaysFalsy) => ConstraintSet::from(left.bool(db).is_always_false()),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use self::schedule::spawn_main_loop;
|
||||
use crate::PositionEncoding;
|
||||
use crate::capabilities::{ResolvedClientCapabilities, server_capabilities};
|
||||
use crate::session::{InitializationOptions, Session, warn_about_unknown_options};
|
||||
use crate::session::{ClientName, InitializationOptions, Session, warn_about_unknown_options};
|
||||
use anyhow::Context;
|
||||
use lsp_server::Connection;
|
||||
use lsp_types::{ClientCapabilities, InitializeParams, MessageType, Url};
|
||||
@@ -47,6 +47,7 @@ impl Server {
|
||||
initialization_options,
|
||||
capabilities: client_capabilities,
|
||||
workspace_folders,
|
||||
client_info,
|
||||
..
|
||||
} = serde_json::from_value(init_value)
|
||||
.context("Failed to deserialize initialization parameters")?;
|
||||
@@ -65,6 +66,7 @@ impl Server {
|
||||
tracing::error!("Failed to deserialize initialization options: {error}");
|
||||
}
|
||||
|
||||
tracing::debug!("Client info: {client_info:#?}");
|
||||
tracing::debug!("Initialization options: {initialization_options:#?}");
|
||||
|
||||
let resolved_client_capabilities = ResolvedClientCapabilities::new(&client_capabilities);
|
||||
@@ -155,6 +157,7 @@ impl Server {
|
||||
workspace_urls,
|
||||
initialization_options,
|
||||
native_system,
|
||||
ClientName::from(client_info),
|
||||
in_test,
|
||||
)?,
|
||||
})
|
||||
|
||||
@@ -119,9 +119,12 @@ pub(super) fn request(req: server::Request) -> Task {
|
||||
.unwrap_or_else(|err| {
|
||||
tracing::error!("Encountered error when routing request with ID {id}: {err}");
|
||||
|
||||
Task::sync(move |_session, client| {
|
||||
Task::sync(move |session, client| {
|
||||
if matches!(err.code, ErrorCode::InternalError) {
|
||||
client.show_error_message("ty failed to handle a request from the editor. Check the logs for more details.");
|
||||
client.show_error_message(format!(
|
||||
"ty failed to handle a request from the editor. {}",
|
||||
session.client_name().log_guidance()
|
||||
));
|
||||
}
|
||||
|
||||
respond_silent_error(
|
||||
@@ -175,11 +178,12 @@ pub(super) fn notification(notif: server::Notification) -> Task {
|
||||
}
|
||||
.unwrap_or_else(|err| {
|
||||
tracing::error!("Encountered error when routing notification: {err}");
|
||||
Task::sync(move |_session, client| {
|
||||
Task::sync(move |session, client| {
|
||||
if matches!(err.code, ErrorCode::InternalError) {
|
||||
client.show_error_message(
|
||||
"ty failed to handle a notification from the editor. Check the logs for more details."
|
||||
);
|
||||
client.show_error_message(format!(
|
||||
"ty failed to handle a notification from the editor. {}",
|
||||
session.client_name().log_guidance()
|
||||
));
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -193,7 +197,7 @@ where
|
||||
Ok(Task::sync(move |session, client: &Client| {
|
||||
let _span = tracing::debug_span!("request", %id, method = R::METHOD).entered();
|
||||
let result = R::run(session, client, params);
|
||||
respond::<R>(&id, result, client);
|
||||
respond::<R>(&id, result, client, session.client_name().log_guidance());
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -217,6 +221,7 @@ where
|
||||
// SAFETY: The `snapshot` is safe to move across the unwind boundary because it is not used
|
||||
// after unwinding.
|
||||
let snapshot = AssertUnwindSafe(session.snapshot_session());
|
||||
let log_guidance = snapshot.0.client_name().log_guidance();
|
||||
|
||||
Box::new(move |client| {
|
||||
let _span = tracing::debug_span!("request", %id, method = R::METHOD).entered();
|
||||
@@ -238,7 +243,7 @@ where
|
||||
let snapshot = snapshot;
|
||||
R::handle_request(&id, snapshot.0, client, params);
|
||||
}) {
|
||||
panic_response::<R>(&id, client, &error, retry);
|
||||
panic_response::<R>(&id, client, &error, retry, log_guidance);
|
||||
}
|
||||
})
|
||||
}))
|
||||
@@ -284,6 +289,7 @@ where
|
||||
|
||||
let path = document.notebook_or_file_path();
|
||||
let db = session.project_db(path).clone();
|
||||
let log_guidance = document.client_name().log_guidance();
|
||||
|
||||
Box::new(move |client| {
|
||||
let _span = tracing::debug_span!("request", %id, method = R::METHOD).entered();
|
||||
@@ -306,7 +312,7 @@ where
|
||||
R::handle_request(&id, &db, document, client, params);
|
||||
});
|
||||
}) {
|
||||
panic_response::<R>(&id, client, &error, retry);
|
||||
panic_response::<R>(&id, client, &error, retry, log_guidance);
|
||||
}
|
||||
})
|
||||
}))
|
||||
@@ -317,6 +323,7 @@ fn panic_response<R>(
|
||||
client: &Client,
|
||||
error: &PanicError,
|
||||
request: Option<lsp_server::Request>,
|
||||
log_guidance: &str,
|
||||
) where
|
||||
R: traits::RetriableRequestHandler,
|
||||
{
|
||||
@@ -346,6 +353,7 @@ fn panic_response<R>(
|
||||
error: anyhow!("request handler {error}"),
|
||||
}),
|
||||
client,
|
||||
log_guidance,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -358,7 +366,10 @@ fn sync_notification_task<N: traits::SyncNotificationHandler>(
|
||||
let _span = tracing::debug_span!("notification", method = N::METHOD).entered();
|
||||
if let Err(err) = N::run(session, client, params) {
|
||||
tracing::error!("An error occurred while running {id}: {err}");
|
||||
client.show_error_message("ty encountered a problem. Check the logs for more details.");
|
||||
client.show_error_message(format!(
|
||||
"ty encountered a problem. {}",
|
||||
session.client_name().log_guidance()
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -390,6 +401,8 @@ where
|
||||
return Box::new(|_| {});
|
||||
};
|
||||
|
||||
let log_guidance = snapshot.client_name().log_guidance();
|
||||
|
||||
Box::new(move |client| {
|
||||
let _span = tracing::debug_span!("notification", method = N::METHOD).entered();
|
||||
|
||||
@@ -399,18 +412,14 @@ where
|
||||
Ok(result) => result,
|
||||
Err(panic) => {
|
||||
tracing::error!("An error occurred while running {id}: {panic}");
|
||||
client.show_error_message(
|
||||
"ty encountered a panic. Check the logs for more details.",
|
||||
);
|
||||
client.show_error_message(format!("ty encountered a panic. {log_guidance}"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = result {
|
||||
tracing::error!("An error occurred while running {id}: {err}");
|
||||
client.show_error_message(
|
||||
"ty encountered a problem. Check the logs for more details.",
|
||||
);
|
||||
client.show_error_message(format!("ty encountered a problem. {log_guidance}"));
|
||||
}
|
||||
})
|
||||
}))
|
||||
@@ -449,12 +458,13 @@ fn respond<Req>(
|
||||
id: &RequestId,
|
||||
result: Result<<<Req as RequestHandler>::RequestType as Request>::Result>,
|
||||
client: &Client,
|
||||
log_guidance: &str,
|
||||
) where
|
||||
Req: RequestHandler,
|
||||
{
|
||||
if let Err(err) = &result {
|
||||
tracing::error!("An error occurred with request ID {id}: {err}");
|
||||
client.show_error_message("ty encountered a problem. Check the logs for more details.");
|
||||
client.show_error_message(format!("ty encountered a problem. {log_guidance}"));
|
||||
}
|
||||
client.respond(id, result);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,10 @@ pub(super) trait BackgroundDocumentRequestHandler: RetriableRequestHandler {
|
||||
|
||||
if let Err(err) = &result {
|
||||
tracing::error!("An error occurred with request ID {id}: {err}");
|
||||
client.show_error_message("ty encountered a problem. Check the logs for more details.");
|
||||
client.show_error_message(format!(
|
||||
"ty encountered a problem. {}",
|
||||
snapshot.client_name().log_guidance()
|
||||
));
|
||||
}
|
||||
|
||||
client.respond(id, result);
|
||||
@@ -153,7 +156,10 @@ pub(super) trait BackgroundRequestHandler: RetriableRequestHandler {
|
||||
|
||||
if let Err(err) = &result {
|
||||
tracing::error!("An error occurred with request ID {id}: {err}");
|
||||
client.show_error_message("ty encountered a problem. Check the logs for more details.");
|
||||
client.show_error_message(format!(
|
||||
"ty encountered a problem. {}",
|
||||
snapshot.client_name().log_guidance()
|
||||
));
|
||||
}
|
||||
|
||||
client.respond(id, result);
|
||||
|
||||
@@ -13,7 +13,7 @@ use lsp_types::request::{
|
||||
WorkspaceDiagnosticRequest,
|
||||
};
|
||||
use lsp_types::{
|
||||
DiagnosticRegistrationOptions, DiagnosticServerCapabilities,
|
||||
ClientInfo, DiagnosticRegistrationOptions, DiagnosticServerCapabilities,
|
||||
DidChangeWatchedFilesRegistrationOptions, FileSystemWatcher, Registration, RegistrationParams,
|
||||
TextDocumentContentChangeEvent, Unregistration, UnregistrationParams, Url,
|
||||
};
|
||||
@@ -106,6 +106,9 @@ pub(crate) struct Session {
|
||||
/// Registrations is a set of LSP methods that have been dynamically registered with the
|
||||
/// client.
|
||||
registrations: HashSet<String>,
|
||||
|
||||
/// The name of the client (editor) that connected to this server.
|
||||
client_name: ClientName,
|
||||
}
|
||||
|
||||
/// LSP State for a Project
|
||||
@@ -141,6 +144,7 @@ impl Session {
|
||||
workspace_urls: Vec<Url>,
|
||||
initialization_options: InitializationOptions,
|
||||
native_system: Arc<dyn System + 'static + Send + Sync + RefUnwindSafe>,
|
||||
client_name: ClientName,
|
||||
in_test: bool,
|
||||
) -> crate::Result<Self> {
|
||||
let index = Arc::new(Index::new());
|
||||
@@ -168,6 +172,7 @@ impl Session {
|
||||
suspended_workspace_diagnostics_request: None,
|
||||
revision: 0,
|
||||
registrations: HashSet::new(),
|
||||
client_name,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -532,8 +537,8 @@ impl Session {
|
||||
);
|
||||
|
||||
client.show_error_message(format!(
|
||||
"Failed to load project for workspace {url}. \
|
||||
Please refer to the logs for more details.",
|
||||
"Failed to load project for workspace {url}. {}",
|
||||
self.client_name.log_guidance(),
|
||||
));
|
||||
|
||||
let db_with_default_settings = ProjectMetadata::from_options(
|
||||
@@ -819,6 +824,7 @@ impl Session {
|
||||
.unwrap_or_else(|| Arc::new(WorkspaceSettings::default())),
|
||||
position_encoding: self.position_encoding,
|
||||
document: document_handle,
|
||||
client_name: self.client_name,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -837,6 +843,7 @@ impl Session {
|
||||
in_test: self.in_test,
|
||||
resolved_client_capabilities: self.resolved_client_capabilities,
|
||||
revision: self.revision,
|
||||
client_name: self.client_name,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -976,6 +983,10 @@ impl Session {
|
||||
pub(crate) fn position_encoding(&self) -> PositionEncoding {
|
||||
self.position_encoding
|
||||
}
|
||||
|
||||
pub(crate) fn client_name(&self) -> ClientName {
|
||||
self.client_name
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard that holds the only reference to the index and allows modifying it.
|
||||
@@ -1025,6 +1036,7 @@ pub(crate) struct DocumentSnapshot {
|
||||
workspace_settings: Arc<WorkspaceSettings>,
|
||||
position_encoding: PositionEncoding,
|
||||
document: DocumentHandle,
|
||||
client_name: ClientName,
|
||||
}
|
||||
|
||||
impl DocumentSnapshot {
|
||||
@@ -1071,6 +1083,10 @@ impl DocumentSnapshot {
|
||||
pub(crate) fn notebook_or_file_path(&self) -> &AnySystemPath {
|
||||
self.document.notebook_or_file_path()
|
||||
}
|
||||
|
||||
pub(crate) fn client_name(&self) -> ClientName {
|
||||
self.client_name
|
||||
}
|
||||
}
|
||||
|
||||
/// An immutable snapshot of the current state of [`Session`].
|
||||
@@ -1081,6 +1097,7 @@ pub(crate) struct SessionSnapshot {
|
||||
resolved_client_capabilities: ResolvedClientCapabilities,
|
||||
in_test: bool,
|
||||
revision: u64,
|
||||
client_name: ClientName,
|
||||
|
||||
/// IMPORTANT: It's important that the databases come last, or at least,
|
||||
/// after any `Arc` that we try to extract or mutate in-place using `Arc::into_inner`
|
||||
@@ -1122,6 +1139,42 @@ impl SessionSnapshot {
|
||||
pub(crate) fn revision(&self) -> u64 {
|
||||
self.revision
|
||||
}
|
||||
|
||||
pub(crate) fn client_name(&self) -> ClientName {
|
||||
self.client_name
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the client (editor) that's connected to the language server.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ClientName {
|
||||
Zed,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl From<Option<ClientInfo>> for ClientName {
|
||||
fn from(info: Option<ClientInfo>) -> Self {
|
||||
match info {
|
||||
Some(info) if matches!(info.name.as_str(), "Zed") => ClientName::Zed,
|
||||
_ => ClientName::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientName {
|
||||
/// Returns editor-specific guidance for finding logs.
|
||||
///
|
||||
/// Different editors have different ways to access language server logs, so we provide tailored
|
||||
/// instructions based on the connected client.
|
||||
pub(crate) fn log_guidance(self) -> &'static str {
|
||||
match self {
|
||||
ClientName::Zed => {
|
||||
"Please refer to the logs for more details \
|
||||
(command palette: `dev: open language server logs`)."
|
||||
}
|
||||
ClientName::Other => "Please refer to the logs for more details.",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
||||
Reference in New Issue
Block a user