diff options
-rw-r--r-- | crates/hir/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/hir/src/semantics.rs | 5 | ||||
-rw-r--r-- | crates/hir_ty/src/lib.rs | 94 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/dot.rs | 30 | ||||
-rw-r--r-- | editors/code/src/config.ts | 8 | ||||
-rw-r--r-- | editors/code/src/main.ts | 35 | ||||
-rw-r--r-- | editors/code/src/persistent_state.ts | 8 |
7 files changed, 150 insertions, 34 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 52d72c3c5..800101c91 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -2053,7 +2053,7 @@ impl Type { | |||
2053 | name: Option<&Name>, | 2053 | name: Option<&Name>, |
2054 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | 2054 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, |
2055 | ) -> Option<T> { | 2055 | ) -> Option<T> { |
2056 | let canonical = hir_ty::replace_errors_with_variables(self.ty.clone()); | 2056 | let canonical = hir_ty::replace_errors_with_variables(&self.ty); |
2057 | 2057 | ||
2058 | let env = self.env.clone(); | 2058 | let env = self.env.clone(); |
2059 | let krate = krate.id; | 2059 | let krate = krate.id; |
@@ -2222,7 +2222,7 @@ impl Type { | |||
2222 | } | 2222 | } |
2223 | 2223 | ||
2224 | pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool { | 2224 | pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool { |
2225 | let tys = hir_ty::replace_errors_with_variables((self.ty.clone(), other.ty.clone())); | 2225 | let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone())); |
2226 | could_unify(db, self.env.clone(), &tys) | 2226 | could_unify(db, self.env.clone(), &tys) |
2227 | } | 2227 | } |
2228 | } | 2228 | } |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 38bd376bc..1b5064b5a 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -505,9 +505,10 @@ impl<'db> SemanticsImpl<'db> { | |||
505 | } | 505 | } |
506 | 506 | ||
507 | fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { | 507 | fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { |
508 | // FIXME: this erases Substs | 508 | // FIXME: this erases Substs, we should instead record the correct |
509 | // substitution during inference and use that | ||
509 | let func = self.resolve_method_call(call)?; | 510 | let func = self.resolve_method_call(call)?; |
510 | let (ty, _) = self.db.value_ty(func.into()).into_value_and_skipped_binders(); | 511 | let ty = hir_ty::TyBuilder::value_ty(self.db, func.into()).fill_with_unknown().build(); |
511 | let resolver = self.analyze(call.syntax()).resolver; | 512 | let resolver = self.analyze(call.syntax()).resolver; |
512 | let ty = Type::new_with_resolver(self.db, &resolver, ty)?; | 513 | let ty = Type::new_with_resolver(self.db, &resolver, ty)?; |
513 | let mut res = ty.as_callable(self.db)?; | 514 | let mut res = ty.as_callable(self.db)?; |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 72093d75a..ef021978a 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -43,7 +43,6 @@ use hir_def::{ | |||
43 | type_ref::{ConstScalar, Rawness}, | 43 | type_ref::{ConstScalar, Rawness}, |
44 | TypeParamId, | 44 | TypeParamId, |
45 | }; | 45 | }; |
46 | use stdx::always; | ||
47 | 46 | ||
48 | use crate::{db::HirDatabase, utils::generics}; | 47 | use crate::{db::HirDatabase, utils::generics}; |
49 | 48 | ||
@@ -329,14 +328,17 @@ pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>( | |||
329 | t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly") | 328 | t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly") |
330 | } | 329 | } |
331 | 330 | ||
332 | pub fn replace_errors_with_variables<T>(t: T) -> Canonical<T::Result> | 331 | /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also |
332 | /// ensures there are no unbound variables or inference variables anywhere in | ||
333 | /// the `t`. | ||
334 | pub fn replace_errors_with_variables<T>(t: &T) -> Canonical<T::Result> | ||
333 | where | 335 | where |
334 | T: HasInterner<Interner = Interner> + Fold<Interner>, | 336 | T: HasInterner<Interner = Interner> + Fold<Interner> + Clone, |
335 | T::Result: HasInterner<Interner = Interner>, | 337 | T::Result: HasInterner<Interner = Interner>, |
336 | { | 338 | { |
337 | use chalk_ir::{ | 339 | use chalk_ir::{ |
338 | fold::{Folder, SuperFold}, | 340 | fold::{Folder, SuperFold}, |
339 | Fallible, | 341 | Fallible, NoSolution, |
340 | }; | 342 | }; |
341 | struct ErrorReplacer { | 343 | struct ErrorReplacer { |
342 | vars: usize, | 344 | vars: usize, |
@@ -363,18 +365,88 @@ where | |||
363 | 365 | ||
364 | fn fold_inference_ty( | 366 | fn fold_inference_ty( |
365 | &mut self, | 367 | &mut self, |
366 | var: InferenceVar, | 368 | _var: InferenceVar, |
367 | kind: TyVariableKind, | 369 | _kind: TyVariableKind, |
370 | _outer_binder: DebruijnIndex, | ||
371 | ) -> Fallible<Ty> { | ||
372 | if cfg!(debug_assertions) { | ||
373 | // we don't want to just panic here, because then the error message | ||
374 | // won't contain the whole thing, which would not be very helpful | ||
375 | Err(NoSolution) | ||
376 | } else { | ||
377 | Ok(TyKind::Error.intern(&Interner)) | ||
378 | } | ||
379 | } | ||
380 | |||
381 | fn fold_free_var_ty( | ||
382 | &mut self, | ||
383 | _bound_var: BoundVar, | ||
368 | _outer_binder: DebruijnIndex, | 384 | _outer_binder: DebruijnIndex, |
369 | ) -> Fallible<Ty> { | 385 | ) -> Fallible<Ty> { |
370 | always!(false); | 386 | if cfg!(debug_assertions) { |
371 | Ok(TyKind::InferenceVar(var, kind).intern(&Interner)) | 387 | // we don't want to just panic here, because then the error message |
388 | // won't contain the whole thing, which would not be very helpful | ||
389 | Err(NoSolution) | ||
390 | } else { | ||
391 | Ok(TyKind::Error.intern(&Interner)) | ||
392 | } | ||
393 | } | ||
394 | |||
395 | fn fold_inference_const( | ||
396 | &mut self, | ||
397 | _ty: Ty, | ||
398 | _var: InferenceVar, | ||
399 | _outer_binder: DebruijnIndex, | ||
400 | ) -> Fallible<Const> { | ||
401 | if cfg!(debug_assertions) { | ||
402 | Err(NoSolution) | ||
403 | } else { | ||
404 | Ok(dummy_usize_const()) | ||
405 | } | ||
406 | } | ||
407 | |||
408 | fn fold_free_var_const( | ||
409 | &mut self, | ||
410 | _ty: Ty, | ||
411 | _bound_var: BoundVar, | ||
412 | _outer_binder: DebruijnIndex, | ||
413 | ) -> Fallible<Const> { | ||
414 | if cfg!(debug_assertions) { | ||
415 | Err(NoSolution) | ||
416 | } else { | ||
417 | Ok(dummy_usize_const()) | ||
418 | } | ||
419 | } | ||
420 | |||
421 | fn fold_inference_lifetime( | ||
422 | &mut self, | ||
423 | _var: InferenceVar, | ||
424 | _outer_binder: DebruijnIndex, | ||
425 | ) -> Fallible<Lifetime> { | ||
426 | if cfg!(debug_assertions) { | ||
427 | Err(NoSolution) | ||
428 | } else { | ||
429 | Ok(static_lifetime()) | ||
430 | } | ||
431 | } | ||
432 | |||
433 | fn fold_free_var_lifetime( | ||
434 | &mut self, | ||
435 | _bound_var: BoundVar, | ||
436 | _outer_binder: DebruijnIndex, | ||
437 | ) -> Fallible<Lifetime> { | ||
438 | if cfg!(debug_assertions) { | ||
439 | Err(NoSolution) | ||
440 | } else { | ||
441 | Ok(static_lifetime()) | ||
442 | } | ||
372 | } | 443 | } |
373 | } | 444 | } |
374 | let mut error_replacer = ErrorReplacer { vars: 0 }; | 445 | let mut error_replacer = ErrorReplacer { vars: 0 }; |
375 | let value = t | 446 | let value = match t.clone().fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) { |
376 | .fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) | 447 | Ok(t) => t, |
377 | .expect("fold failed unexpectedly"); | 448 | Err(_) => panic!("Encountered unbound or inference vars in {:?}", t), |
449 | }; | ||
378 | let kinds = (0..error_replacer.vars).map(|_| { | 450 | let kinds = (0..error_replacer.vars).map(|_| { |
379 | chalk_ir::CanonicalVarKind::new( | 451 | chalk_ir::CanonicalVarKind::new( |
380 | chalk_ir::VariableKind::Ty(TyVariableKind::General), | 452 | chalk_ir::VariableKind::Ty(TyVariableKind::General), |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 7e4efe589..fd9738743 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -454,4 +454,34 @@ mod foo { | |||
454 | "#]], | 454 | "#]], |
455 | ); | 455 | ); |
456 | } | 456 | } |
457 | |||
458 | #[test] | ||
459 | fn issue_8931() { | ||
460 | check( | ||
461 | r#" | ||
462 | #[lang = "fn_once"] | ||
463 | trait FnOnce<Args> { | ||
464 | type Output; | ||
465 | } | ||
466 | struct S; | ||
467 | |||
468 | struct Foo; | ||
469 | impl Foo { | ||
470 | fn foo(&self) -> &[u8] { loop {} } | ||
471 | } | ||
472 | |||
473 | impl S { | ||
474 | fn indented(&mut self, f: impl FnOnce(&mut Self)) { | ||
475 | } | ||
476 | |||
477 | fn f(&mut self, v: Foo) { | ||
478 | self.indented(|this| v.$0) | ||
479 | } | ||
480 | } | ||
481 | "#, | ||
482 | expect![[r#" | ||
483 | me foo() fn(&self) -> &[u8] | ||
484 | "#]], | ||
485 | ); | ||
486 | } | ||
457 | } | 487 | } |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index e858f80bc..fbb7a556a 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -4,7 +4,7 @@ import { log } from "./util"; | |||
4 | 4 | ||
5 | export type UpdatesChannel = "stable" | "nightly"; | 5 | export type UpdatesChannel = "stable" | "nightly"; |
6 | 6 | ||
7 | export const NIGHTLY_TAG = "nightly"; | 7 | const NIGHTLY_TAG = "nightly"; |
8 | 8 | ||
9 | export type RunnableEnvCfg = undefined | Record<string, string> | { mask?: string; env: Record<string, string> }[]; | 9 | export type RunnableEnvCfg = undefined | Record<string, string> | { mask?: string; env: Record<string, string> }[]; |
10 | 10 | ||
@@ -34,7 +34,7 @@ export class Config { | |||
34 | readonly globalStoragePath: string; | 34 | readonly globalStoragePath: string; |
35 | 35 | ||
36 | constructor(ctx: vscode.ExtensionContext) { | 36 | constructor(ctx: vscode.ExtensionContext) { |
37 | this.globalStoragePath = ctx.globalStoragePath; | 37 | this.globalStoragePath = ctx.globalStorageUri.path; |
38 | vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions); | 38 | vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions); |
39 | this.refreshLogging(); | 39 | this.refreshLogging(); |
40 | } | 40 | } |
@@ -170,4 +170,8 @@ export class Config { | |||
170 | gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"), | 170 | gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"), |
171 | }; | 171 | }; |
172 | } | 172 | } |
173 | |||
174 | get currentExtensionIsNightly() { | ||
175 | return this.package.releaseTag === NIGHTLY_TAG; | ||
176 | } | ||
173 | } | 177 | } |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 92c797d47..aaedc2431 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -6,7 +6,7 @@ import { promises as fs, PathLike } from "fs"; | |||
6 | import * as commands from './commands'; | 6 | import * as commands from './commands'; |
7 | import { activateInlayHints } from './inlay_hints'; | 7 | import { activateInlayHints } from './inlay_hints'; |
8 | import { Ctx } from './ctx'; | 8 | import { Ctx } from './ctx'; |
9 | import { Config, NIGHTLY_TAG } from './config'; | 9 | import { Config } from './config'; |
10 | import { log, assert, isValidExecutable } from './util'; | 10 | import { log, assert, isValidExecutable } from './util'; |
11 | import { PersistentState } from './persistent_state'; | 11 | import { PersistentState } from './persistent_state'; |
12 | import { fetchRelease, download } from './net'; | 12 | import { fetchRelease, download } from './net'; |
@@ -156,16 +156,18 @@ export async function deactivate() { | |||
156 | async function bootstrap(config: Config, state: PersistentState): Promise<string> { | 156 | async function bootstrap(config: Config, state: PersistentState): Promise<string> { |
157 | await fs.mkdir(config.globalStoragePath, { recursive: true }); | 157 | await fs.mkdir(config.globalStoragePath, { recursive: true }); |
158 | 158 | ||
159 | if (!config.currentExtensionIsNightly) { | ||
160 | await state.updateNightlyReleaseId(undefined); | ||
161 | } | ||
159 | await bootstrapExtension(config, state); | 162 | await bootstrapExtension(config, state); |
160 | const path = await bootstrapServer(config, state); | 163 | const path = await bootstrapServer(config, state); |
161 | |||
162 | return path; | 164 | return path; |
163 | } | 165 | } |
164 | 166 | ||
165 | async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> { | 167 | async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> { |
166 | if (config.package.releaseTag === null) return; | 168 | if (config.package.releaseTag === null) return; |
167 | if (config.channel === "stable") { | 169 | if (config.channel === "stable") { |
168 | if (config.package.releaseTag === NIGHTLY_TAG) { | 170 | if (config.currentExtensionIsNightly) { |
169 | void vscode.window.showWarningMessage( | 171 | void vscode.window.showWarningMessage( |
170 | `You are running a nightly version of rust-analyzer extension. ` + | 172 | `You are running a nightly version of rust-analyzer extension. ` + |
171 | `To switch to stable, uninstall the extension and re-install it from the marketplace` | 173 | `To switch to stable, uninstall the extension and re-install it from the marketplace` |
@@ -176,27 +178,34 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
176 | if (serverPath(config)) return; | 178 | if (serverPath(config)) return; |
177 | 179 | ||
178 | const now = Date.now(); | 180 | const now = Date.now(); |
179 | if (config.package.releaseTag === NIGHTLY_TAG) { | 181 | const isInitialNightlyDownload = state.nightlyReleaseId === undefined; |
182 | if (config.currentExtensionIsNightly) { | ||
180 | // Check if we should poll github api for the new nightly version | 183 | // Check if we should poll github api for the new nightly version |
181 | // if we haven't done it during the past hour | 184 | // if we haven't done it during the past hour |
182 | const lastCheck = state.lastCheck; | 185 | const lastCheck = state.lastCheck; |
183 | 186 | ||
184 | const anHour = 60 * 60 * 1000; | 187 | const anHour = 60 * 60 * 1000; |
185 | const shouldCheckForNewNightly = state.releaseId === undefined || (now - (lastCheck ?? 0)) > anHour; | 188 | const shouldCheckForNewNightly = isInitialNightlyDownload || (now - (lastCheck ?? 0)) > anHour; |
186 | 189 | ||
187 | if (!shouldCheckForNewNightly) return; | 190 | if (!shouldCheckForNewNightly) return; |
188 | } | 191 | } |
189 | 192 | ||
190 | const release = await downloadWithRetryDialog(state, async () => { | 193 | const latestNightlyRelease = await downloadWithRetryDialog(state, async () => { |
191 | return await fetchRelease("nightly", state.githubToken, config.httpProxy); | 194 | return await fetchRelease("nightly", state.githubToken, config.httpProxy); |
192 | }).catch(async (e) => { | 195 | }).catch(async (e) => { |
193 | log.error(e); | 196 | log.error(e); |
194 | if (state.releaseId === undefined) { // Show error only for the initial download | 197 | if (isInitialNightlyDownload) { |
195 | await vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); | 198 | await vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly: ${e}`); |
196 | } | 199 | } |
197 | return undefined; | 200 | return; |
198 | }); | 201 | }); |
199 | if (release === undefined || release.id === state.releaseId) return; | 202 | if (latestNightlyRelease === undefined) { |
203 | if (isInitialNightlyDownload) { | ||
204 | await vscode.window.showErrorMessage("Failed to download rust-analyzer nightly: empty release contents returned"); | ||
205 | } | ||
206 | return; | ||
207 | } | ||
208 | if (config.currentExtensionIsNightly && latestNightlyRelease.id === state.nightlyReleaseId) return; | ||
200 | 209 | ||
201 | const userResponse = await vscode.window.showInformationMessage( | 210 | const userResponse = await vscode.window.showInformationMessage( |
202 | "New version of rust-analyzer (nightly) is available (requires reload).", | 211 | "New version of rust-analyzer (nightly) is available (requires reload).", |
@@ -204,8 +213,8 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
204 | ); | 213 | ); |
205 | if (userResponse !== "Update") return; | 214 | if (userResponse !== "Update") return; |
206 | 215 | ||
207 | const artifact = release.assets.find(artifact => artifact.name === "rust-analyzer.vsix"); | 216 | const artifact = latestNightlyRelease.assets.find(artifact => artifact.name === "rust-analyzer.vsix"); |
208 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 217 | assert(!!artifact, `Bad release: ${JSON.stringify(latestNightlyRelease)}`); |
209 | 218 | ||
210 | const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); | 219 | const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); |
211 | 220 | ||
@@ -221,7 +230,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
221 | await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); | 230 | await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); |
222 | await fs.unlink(dest); | 231 | await fs.unlink(dest); |
223 | 232 | ||
224 | await state.updateReleaseId(release.id); | 233 | await state.updateNightlyReleaseId(latestNightlyRelease.id); |
225 | await state.updateLastCheck(now); | 234 | await state.updateLastCheck(now); |
226 | await vscode.commands.executeCommand("workbench.action.reloadWindow"); | 235 | await vscode.commands.executeCommand("workbench.action.reloadWindow"); |
227 | } | 236 | } |
diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts index afb652589..dd2aeecca 100644 --- a/editors/code/src/persistent_state.ts +++ b/editors/code/src/persistent_state.ts | |||
@@ -3,8 +3,8 @@ import { log } from './util'; | |||
3 | 3 | ||
4 | export class PersistentState { | 4 | export class PersistentState { |
5 | constructor(private readonly globalState: vscode.Memento) { | 5 | constructor(private readonly globalState: vscode.Memento) { |
6 | const { lastCheck, releaseId, serverVersion } = this; | 6 | const { lastCheck, nightlyReleaseId, serverVersion } = this; |
7 | log.info("PersistentState:", { lastCheck, releaseId, serverVersion }); | 7 | log.info("PersistentState:", { lastCheck, nightlyReleaseId, serverVersion }); |
8 | } | 8 | } |
9 | 9 | ||
10 | /** | 10 | /** |
@@ -21,10 +21,10 @@ export class PersistentState { | |||
21 | * Release id of the *nightly* extension. | 21 | * Release id of the *nightly* extension. |
22 | * Used to check if we should update. | 22 | * Used to check if we should update. |
23 | */ | 23 | */ |
24 | get releaseId(): number | undefined { | 24 | get nightlyReleaseId(): number | undefined { |
25 | return this.globalState.get("releaseId"); | 25 | return this.globalState.get("releaseId"); |
26 | } | 26 | } |
27 | async updateReleaseId(value: number) { | 27 | async updateNightlyReleaseId(value: number | undefined) { |
28 | await this.globalState.update("releaseId", value); | 28 | await this.globalState.update("releaseId", value); |
29 | } | 29 | } |
30 | 30 | ||