From 9b47124e6e5d32a676961c05661934215e98012c Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 13 Feb 2020 22:47:31 +0200 Subject: [PATCH 01/46] vscode: added more type safety to package.json config --- editors/code/package.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/editors/code/package.json b/editors/code/package.json index f687eb8d45..12d32cef72 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -181,6 +181,9 @@ }, "rust-analyzer.excludeGlobs": { "type": "array", + "items": { + "type": "string" + }, "default": [], "description": "Paths to exclude from analysis" }, @@ -196,6 +199,9 @@ }, "rust-analyzer.cargo-watch.arguments": { "type": "array", + "items": { + "type": "string" + }, "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", "default": [] }, @@ -241,6 +247,7 @@ "rust-analyzer.maxInlayHintLength": { "type": "number", "default": 20, + "exclusiveMinimum": 0, "description": "Maximum length for inlay hints" }, "rust-analyzer.cargoFeatures.noDefaultFeatures": { @@ -255,6 +262,9 @@ }, "rust-analyzer.cargoFeatures.features": { "type": "array", + "items": { + "type": "string" + }, "default": [], "description": "List of features to activate" } From 7ad15c396286376c4a439b2dec4ec452b5f28dda Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 13 Feb 2020 22:48:20 +0200 Subject: [PATCH 02/46] vscode: redesigned config with simplicity and Dart extension config implementation in mind --- editors/code/src/client.ts | 36 ++-- editors/code/src/config.ts | 262 ++++++++--------------------- editors/code/src/highlighting.ts | 6 +- editors/code/src/inlay_hints.ts | 6 +- editors/code/src/status_display.ts | 6 +- 5 files changed, 98 insertions(+), 218 deletions(-) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 2e3d4aba2d..a6fb045369 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -1,6 +1,6 @@ import * as lc from 'vscode-languageclient'; +import * as vscode from 'vscode'; -import { window, workspace } from 'vscode'; import { Config } from './config'; import { ensureLanguageServerBinary } from './installation/language_server'; @@ -8,37 +8,39 @@ export async function createClient(config: Config): Promise `${Config.rootSection}.${opt}`); - highlightingOn = true; - rainbowHighlightingOn = false; - enableEnhancedTyping = true; - lruCapacity: null | number = null; - displayInlayHints = true; - maxInlayHintLength: null | number = null; - excludeGlobs: string[] = []; - useClientWatching = true; - featureFlags: Record = {}; - // for internal use - withSysroot: null | boolean = null; - cargoWatchOptions: CargoWatchOptions = { - enable: true, - arguments: [], - command: '', - allTargets: true, - }; - cargoFeatures: CargoFeatures = { - noDefaultFeatures: false, - allFeatures: true, - features: [], - }; + private cfg!: vscode.WorkspaceConfiguration; - private prevEnhancedTyping: null | boolean = null; - private prevCargoFeatures: null | CargoFeatures = null; - private prevCargoWatchOptions: null | CargoWatchOptions = null; - - constructor(ctx: vscode.ExtensionContext) { - vscode.workspace.onDidChangeConfiguration(_ => this.refresh(ctx), null, ctx.subscriptions); - this.refresh(ctx); + private refreshConfig() { + this.cfg = vscode.workspace.getConfiguration(Config.rootSection); + console.log("Using configuration:", this.cfg); } - private static expandPathResolving(path: string) { - if (path.startsWith('~/')) { - return path.replace('~', os.homedir()); + constructor(private ctx: vscode.ExtensionContext) { + vscode.workspace.onDidChangeConfiguration(this.onConfigChange, this, ctx.subscriptions); + this.refreshConfig(); + } + + async onConfigChange(event: vscode.ConfigurationChangeEvent) { + this.refreshConfig(); + + const requiresReloadOpt = Config.requiresReloadOpts.find( + opt => event.affectsConfiguration(opt) + ); + + if (!requiresReloadOpt) return; + + const userResponse = await vscode.window.showInformationMessage( + `Changing "${requiresReloadOpt}" requires a reload`, + "Reload now" + ); + + if (userResponse === "Reload now") { + vscode.commands.executeCommand("workbench.action.reloadWindow"); + } + } + + private static replaceTildeWithHomeDir(path: string) { + if (path.startsWith("~/")) { + return os.homedir() + path.slice("~".length); } return path; } @@ -97,16 +100,13 @@ export class Config { } } - private static langServerBinarySource( - ctx: vscode.ExtensionContext, - config: vscode.WorkspaceConfiguration - ): null | BinarySource { - const langServerPath = RA_LSP_DEBUG ?? config.get("raLspServerPath"); + langServerBinarySource(): null | BinarySource { + const langServerPath = RA_LSP_DEBUG ?? this.cfg.get("raLspServerPath"); if (langServerPath) { return { type: BinarySource.Type.ExplicitPath, - path: Config.expandPathResolving(langServerPath) + path: Config.replaceTildeWithHomeDir(langServerPath) }; } @@ -118,7 +118,7 @@ export class Config { return { type: BinarySource.Type.GithubRelease, - dir: ctx.globalStoragePath, + dir: this.ctx.globalStoragePath, file: prebuiltBinaryName, repo: { name: "rust-analyzer", @@ -127,158 +127,36 @@ export class Config { }; } + // We don't do runtime config validation here for simplicity. More on stackoverflow: + // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension - // FIXME: revisit the logic for `if (.has(...)) config.get(...)` set default - // values only in one place (i.e. remove default values from non-readonly members declarations) - private refresh(ctx: vscode.ExtensionContext) { - const config = vscode.workspace.getConfiguration('rust-analyzer'); + // FIXME: add codegen for primitive configurations + highlightingOn() { return this.cfg.get("highlightingOn") as boolean; } + rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; } + lruCapacity() { return this.cfg.get("lruCapacity") as null | number; } + displayInlayHints() { return this.cfg.get("displayInlayHints") as boolean; } + maxInlayHintLength() { return this.cfg.get("maxInlayHintLength") as number; } + excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; } + useClientWatching() { return this.cfg.get("useClientWatching") as boolean; } + featureFlags() { return this.cfg.get("featureFlags") as Record; } - let requireReloadMessage = null; - - if (config.has('highlightingOn')) { - this.highlightingOn = config.get('highlightingOn') as boolean; - } - - if (config.has('rainbowHighlightingOn')) { - this.rainbowHighlightingOn = config.get( - 'rainbowHighlightingOn', - ) as boolean; - } - - if (config.has('enableEnhancedTyping')) { - this.enableEnhancedTyping = config.get( - 'enableEnhancedTyping', - ) as boolean; - - if (this.prevEnhancedTyping === null) { - this.prevEnhancedTyping = this.enableEnhancedTyping; - } - } else if (this.prevEnhancedTyping === null) { - this.prevEnhancedTyping = this.enableEnhancedTyping; - } - - if (this.prevEnhancedTyping !== this.enableEnhancedTyping) { - requireReloadMessage = - 'Changing enhanced typing setting requires a reload'; - this.prevEnhancedTyping = this.enableEnhancedTyping; - } - - this.langServerSource = Config.langServerBinarySource(ctx, config); - - if (config.has('cargo-watch.enable')) { - this.cargoWatchOptions.enable = config.get( - 'cargo-watch.enable', - true, - ); - } - - if (config.has('cargo-watch.arguments')) { - this.cargoWatchOptions.arguments = config.get( - 'cargo-watch.arguments', - [], - ); - } - - if (config.has('cargo-watch.command')) { - this.cargoWatchOptions.command = config.get( - 'cargo-watch.command', - '', - ); - } - - if (config.has('cargo-watch.allTargets')) { - this.cargoWatchOptions.allTargets = config.get( - 'cargo-watch.allTargets', - true, - ); - } - - if (config.has('lruCapacity')) { - this.lruCapacity = config.get('lruCapacity') as number; - } - - if (config.has('displayInlayHints')) { - this.displayInlayHints = config.get('displayInlayHints') as boolean; - } - if (config.has('maxInlayHintLength')) { - this.maxInlayHintLength = config.get( - 'maxInlayHintLength', - ) as number; - } - if (config.has('excludeGlobs')) { - this.excludeGlobs = config.get('excludeGlobs') || []; - } - if (config.has('useClientWatching')) { - this.useClientWatching = config.get('useClientWatching') || true; - } - if (config.has('featureFlags')) { - this.featureFlags = config.get('featureFlags') || {}; - } - if (config.has('withSysroot')) { - this.withSysroot = config.get('withSysroot') || false; - } - - if (config.has('cargoFeatures.noDefaultFeatures')) { - this.cargoFeatures.noDefaultFeatures = config.get( - 'cargoFeatures.noDefaultFeatures', - false, - ); - } - if (config.has('cargoFeatures.allFeatures')) { - this.cargoFeatures.allFeatures = config.get( - 'cargoFeatures.allFeatures', - true, - ); - } - if (config.has('cargoFeatures.features')) { - this.cargoFeatures.features = config.get( - 'cargoFeatures.features', - [], - ); - } - - if ( - this.prevCargoFeatures !== null && - (this.cargoFeatures.allFeatures !== - this.prevCargoFeatures.allFeatures || - this.cargoFeatures.noDefaultFeatures !== - this.prevCargoFeatures.noDefaultFeatures || - this.cargoFeatures.features.length !== - this.prevCargoFeatures.features.length || - this.cargoFeatures.features.some( - (v, i) => v !== this.prevCargoFeatures!.features[i], - )) - ) { - requireReloadMessage = 'Changing cargo features requires a reload'; - } - this.prevCargoFeatures = { ...this.cargoFeatures }; - - if (this.prevCargoWatchOptions !== null) { - const changed = - this.cargoWatchOptions.enable !== this.prevCargoWatchOptions.enable || - this.cargoWatchOptions.command !== this.prevCargoWatchOptions.command || - this.cargoWatchOptions.allTargets !== this.prevCargoWatchOptions.allTargets || - this.cargoWatchOptions.arguments.length !== this.prevCargoWatchOptions.arguments.length || - this.cargoWatchOptions.arguments.some( - (v, i) => v !== this.prevCargoWatchOptions!.arguments[i], - ); - if (changed) { - requireReloadMessage = 'Changing cargo-watch options requires a reload'; - } - } - this.prevCargoWatchOptions = { ...this.cargoWatchOptions }; - - if (requireReloadMessage !== null) { - const reloadAction = 'Reload now'; - vscode.window - .showInformationMessage(requireReloadMessage, reloadAction) - .then(selectedAction => { - if (selectedAction === reloadAction) { - vscode.commands.executeCommand( - 'workbench.action.reloadWindow', - ); - } - }); - } + cargoWatchOptions(): CargoWatchOptions { + return { + enable: this.cfg.get("cargo-watch.enable") as boolean, + arguments: this.cfg.get("cargo-watch.arguments") as string[], + allTargets: this.cfg.get("cargo-watch.allTargets") as boolean, + command: this.cfg.get("cargo-watch.command") as string, + }; } + + cargoFeatures(): CargoFeatures { + return { + noDefaultFeatures: this.cfg.get("cargoFeatures.noDefaultFeatures") as boolean, + allFeatures: this.cfg.get("cargoFeatures.allFeatures") as boolean, + features: this.cfg.get("cargoFeatures.features") as string[], + }; + } + + // for internal use + withSysroot() { return this.cfg.get("withSysroot", false); } } diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index 4fbbe3ddc5..e2ae31d290 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts @@ -11,7 +11,7 @@ export function activateHighlighting(ctx: Ctx) { client.onNotification( 'rust-analyzer/publishDecorations', (params: PublishDecorationsParams) => { - if (!ctx.config.highlightingOn) return; + if (!ctx.config.highlightingOn()) return; const targetEditor = vscode.window.visibleTextEditors.find( editor => { @@ -39,7 +39,7 @@ export function activateHighlighting(ctx: Ctx) { vscode.window.onDidChangeActiveTextEditor( async (editor: vscode.TextEditor | undefined) => { if (!editor || editor.document.languageId !== 'rust') return; - if (!ctx.config.highlightingOn) return; + if (!ctx.config.highlightingOn()) return; const client = ctx.client; if (!client) return; @@ -122,7 +122,7 @@ class Highlighter { string, [vscode.Range[], boolean] > = new Map(); - const rainbowTime = this.ctx.config.rainbowHighlightingOn; + const rainbowTime = this.ctx.config.rainbowHighlightingOn(); for (const tag of this.decorations.keys()) { byTag.set(tag, []); diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 1c019a51bc..3ff45a6257 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts @@ -22,12 +22,12 @@ export function activateInlayHints(ctx: Ctx) { ); vscode.workspace.onDidChangeConfiguration( - async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints), + async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints()), null, ctx.subscriptions ); - ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); + ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints())); } interface InlayHintsParams { @@ -59,7 +59,7 @@ class HintsUpdater { constructor(ctx: Ctx) { this.ctx = ctx; - this.enabled = ctx.config.displayInlayHints; + this.enabled = ctx.config.displayInlayHints(); } async setEnabled(enabled: boolean) { diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts index 51dbf388bb..ae9a7b1b55 100644 --- a/editors/code/src/status_display.ts +++ b/editors/code/src/status_display.ts @@ -7,7 +7,7 @@ import { Ctx } from './ctx'; const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; export function activateStatusDisplay(ctx: Ctx) { - const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions.command); + const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions().command); ctx.pushCleanup(statusDisplay); ctx.onDidRestart(client => ctx.pushCleanup(client.onProgress( WorkDoneProgress.type, @@ -66,9 +66,9 @@ class StatusDisplay implements Disposable { refreshLabel() { if (this.packageName) { - this.statusBarItem!.text = `${spinnerFrames[this.i]} cargo ${this.command} [${this.packageName}]`; + this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command} [${this.packageName}]`; } else { - this.statusBarItem!.text = `${spinnerFrames[this.i]} cargo ${this.command}`; + this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command}`; } } From fd37151ade9948398e863c38418fb4f0d0acdfa7 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 13 Feb 2020 23:05:32 +0200 Subject: [PATCH 03/46] vscode: reordered config constructor before methods --- editors/code/src/config.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 6c4742464c..349f80278d 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -26,16 +26,17 @@ export class Config { private cfg!: vscode.WorkspaceConfiguration; + constructor(private readonly ctx: vscode.ExtensionContext) { + vscode.workspace.onDidChangeConfiguration(this.onConfigChange, this, ctx.subscriptions); + this.refreshConfig(); + } + + private refreshConfig() { this.cfg = vscode.workspace.getConfiguration(Config.rootSection); console.log("Using configuration:", this.cfg); } - constructor(private ctx: vscode.ExtensionContext) { - vscode.workspace.onDidChangeConfiguration(this.onConfigChange, this, ctx.subscriptions); - this.refreshConfig(); - } - async onConfigChange(event: vscode.ConfigurationChangeEvent) { this.refreshConfig(); From a324d066cb767876f9f3398f83db9f5a8dda8ce4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 14 Feb 2020 14:44:00 +0100 Subject: [PATCH 04/46] Rename Ty::Param => Ty::Placeholder This aligns more with Chalk. --- crates/ra_hir_ty/src/lib.rs | 10 +++++----- crates/ra_hir_ty/src/lower.rs | 8 ++++---- crates/ra_hir_ty/src/traits/chalk.rs | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index c5fe18c855..69ad139521 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -291,7 +291,7 @@ pub enum Ty { /// {}` when we're type-checking the body of that function. In this /// situation, we know this stands for *some* type, but don't know the exact /// type. - Param(TypeParamId), + Placeholder(TypeParamId), /// A bound type variable. This is used in various places: when representing /// some polymorphic type like the type of function `fn f`, the type @@ -365,7 +365,7 @@ impl Substs { /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). pub(crate) fn type_params_for_generics(generic_params: &Generics) -> Substs { - Substs(generic_params.iter().map(|(id, _)| Ty::Param(id)).collect()) + Substs(generic_params.iter().map(|(id, _)| Ty::Placeholder(id)).collect()) } /// Return Substs that replace each parameter by itself (i.e. `Ty::Param`). @@ -813,7 +813,7 @@ impl TypeWalk for Ty { p.walk(f); } } - Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} + Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self); } @@ -831,7 +831,7 @@ impl TypeWalk for Ty { p.walk_mut_binders(f, binders + 1); } } - Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} + Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self, binders); } @@ -1032,7 +1032,7 @@ impl HirDisplay for Ty { match self { Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, - Ty::Param(id) => { + Ty::Placeholder(id) => { let generics = generics(f.db, id.parent); let param_data = &generics.params.types[id.local_id]; match param_data.provenance { diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index c68c5852be..df24c16a3f 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -152,7 +152,7 @@ impl Ty { data.provenance == TypeParamProvenance::ArgumentImplTrait }) .nth(idx as usize) - .map_or(Ty::Unknown, |(id, _)| Ty::Param(id)); + .map_or(Ty::Unknown, |(id, _)| Ty::Placeholder(id)); param } else { Ty::Unknown @@ -270,7 +270,7 @@ impl Ty { let generics = generics(ctx.db, ctx.resolver.generic_def().expect("generics in scope")); match ctx.type_param_mode { - TypeParamLoweringMode::Placeholder => Ty::Param(param_id), + TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id), TypeParamLoweringMode::Variable => { let idx = generics.param_idx(param_id).expect("matching generics"); Ty::Bound(idx) @@ -339,7 +339,7 @@ impl Ty { None => return Ty::Unknown, // this can't actually happen }; let param_id = match self_ty { - Ty::Param(id) if ctx.type_param_mode == TypeParamLoweringMode::Placeholder => id, + Ty::Placeholder(id) if ctx.type_param_mode == TypeParamLoweringMode::Placeholder => id, Ty::Bound(idx) if ctx.type_param_mode == TypeParamLoweringMode::Variable => { let generics = generics(ctx.db, def); let param_id = if let Some((id, _)) = generics.iter().nth(idx as usize) { @@ -544,7 +544,7 @@ impl GenericPredicate { let generics = generics(ctx.db, generic_def); let param_id = hir_def::TypeParamId { parent: generic_def, local_id: *param_id }; match ctx.type_param_mode { - TypeParamLoweringMode::Placeholder => Ty::Param(param_id), + TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id), TypeParamLoweringMode::Variable => { let idx = generics.param_idx(param_id).expect("matching generics"); Ty::Bound(idx) diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 4974c565be..882160fa8a 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -142,7 +142,7 @@ impl ToChalk for Ty { let substitution = proj_ty.parameters.to_chalk(db); chalk_ir::AliasTy { associated_ty_id, substitution }.cast().intern() } - Ty::Param(id) => { + Ty::Placeholder(id) => { let interned_id = db.intern_type_param_id(id); PlaceholderIndex { ui: UniverseIndex::ROOT, @@ -184,7 +184,7 @@ impl ToChalk for Ty { let interned_id = crate::db::GlobalTypeParamId::from_intern_id( crate::salsa::InternId::from(idx.idx), ); - Ty::Param(db.lookup_intern_type_param_id(interned_id)) + Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) } chalk_ir::TyData::Alias(proj) => { let associated_ty = from_chalk(db, proj.associated_ty_id); From 5028b86cb8ed40d8b43ba2a1b1cd01377e363626 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 14 Feb 2020 15:01:25 +0100 Subject: [PATCH 05/46] Move hir_fmt code to display module --- crates/ra_hir_ty/src/display.rs | 373 ++++++++++++++++++++++++++++++- crates/ra_hir_ty/src/lib.rs | 375 +------------------------------- 2 files changed, 376 insertions(+), 372 deletions(-) diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d1ff85f0f2..14e089cf4f 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -2,7 +2,12 @@ use std::fmt; -use crate::db::HirDatabase; +use crate::{ + db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, + Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, +}; +use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; +use hir_expand::name::Name; pub struct HirFormatter<'a, 'b, DB> { pub db: &'a DB, @@ -97,3 +102,369 @@ where }) } } + +const TYPE_HINT_TRUNCATION: &str = "…"; + +impl HirDisplay for &Ty { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + HirDisplay::hir_fmt(*self, f) + } +} + +impl HirDisplay for ApplicationTy { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + if f.should_truncate() { + return write!(f, "{}", TYPE_HINT_TRUNCATION); + } + + match self.ctor { + TypeCtor::Bool => write!(f, "bool")?, + TypeCtor::Char => write!(f, "char")?, + TypeCtor::Int(t) => write!(f, "{}", t)?, + TypeCtor::Float(t) => write!(f, "{}", t)?, + TypeCtor::Str => write!(f, "str")?, + TypeCtor::Slice => { + let t = self.parameters.as_single(); + write!(f, "[{}]", t.display(f.db))?; + } + TypeCtor::Array => { + let t = self.parameters.as_single(); + write!(f, "[{}; _]", t.display(f.db))?; + } + TypeCtor::RawPtr(m) => { + let t = self.parameters.as_single(); + write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; + } + TypeCtor::Ref(m) => { + let t = self.parameters.as_single(); + let ty_display = if f.omit_verbose_types() { + t.display_truncated(f.db, f.max_size) + } else { + t.display(f.db) + }; + write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; + } + TypeCtor::Never => write!(f, "!")?, + TypeCtor::Tuple { .. } => { + let ts = &self.parameters; + if ts.len() == 1 { + write!(f, "({},)", ts[0].display(f.db))?; + } else { + write!(f, "(")?; + f.write_joined(&*ts.0, ", ")?; + write!(f, ")")?; + } + } + TypeCtor::FnPtr { .. } => { + let sig = FnSig::from_fn_ptr_substs(&self.parameters); + write!(f, "fn(")?; + f.write_joined(sig.params(), ", ")?; + write!(f, ") -> {}", sig.ret().display(f.db))?; + } + TypeCtor::FnDef(def) => { + let sig = f.db.callable_item_signature(def).subst(&self.parameters); + let name = match def { + CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), + CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), + CallableDef::EnumVariantId(e) => { + let enum_data = f.db.enum_data(e.parent); + enum_data.variants[e.local_id].name.clone() + } + }; + match def { + CallableDef::FunctionId(_) => write!(f, "fn {}", name)?, + CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { + write!(f, "{}", name)? + } + } + if self.parameters.len() > 0 { + let generics = generics(f.db, def.into()); + let (parent_params, self_param, type_params, _impl_trait_params) = + generics.provenance_split(); + let total_len = parent_params + self_param + type_params; + // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? + if total_len > 0 { + write!(f, "<")?; + f.write_joined(&self.parameters.0[..total_len], ", ")?; + write!(f, ">")?; + } + } + write!(f, "(")?; + f.write_joined(sig.params(), ", ")?; + write!(f, ") -> {}", sig.ret().display(f.db))?; + } + TypeCtor::Adt(def_id) => { + let name = match def_id { + AdtId::StructId(it) => f.db.struct_data(it).name.clone(), + AdtId::UnionId(it) => f.db.union_data(it).name.clone(), + AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), + }; + write!(f, "{}", name)?; + if self.parameters.len() > 0 { + write!(f, "<")?; + + let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); + let parameters_to_write = if f.omit_verbose_types() { + match self + .ctor + .as_generic_def() + .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) + .filter(|defaults| !defaults.is_empty()) + { + Option::None => self.parameters.0.as_ref(), + Option::Some(default_parameters) => { + for (i, parameter) in self.parameters.iter().enumerate() { + match (parameter, default_parameters.get(i)) { + (&Ty::Unknown, _) | (_, None) => { + non_default_parameters.push(parameter.clone()) + } + (_, Some(default_parameter)) + if parameter != default_parameter => + { + non_default_parameters.push(parameter.clone()) + } + _ => (), + } + } + &non_default_parameters + } + } + } else { + self.parameters.0.as_ref() + }; + + f.write_joined(parameters_to_write, ", ")?; + write!(f, ">")?; + } + } + TypeCtor::AssociatedType(type_alias) => { + let trait_ = match type_alias.lookup(f.db).container { + AssocContainerId::TraitId(it) => it, + _ => panic!("not an associated type"), + }; + let trait_name = f.db.trait_data(trait_).name.clone(); + let name = f.db.type_alias_data(type_alias).name.clone(); + write!(f, "{}::{}", trait_name, name)?; + if self.parameters.len() > 0 { + write!(f, "<")?; + f.write_joined(&*self.parameters.0, ", ")?; + write!(f, ">")?; + } + } + TypeCtor::Closure { .. } => { + let sig = self.parameters[0] + .callable_sig(f.db) + .expect("first closure parameter should contain signature"); + let return_type_hint = sig.ret().display(f.db); + if sig.params().is_empty() { + write!(f, "|| -> {}", return_type_hint)?; + } else if f.omit_verbose_types() { + write!(f, "|{}| -> {}", TYPE_HINT_TRUNCATION, return_type_hint)?; + } else { + write!(f, "|")?; + f.write_joined(sig.params(), ", ")?; + write!(f, "| -> {}", return_type_hint)?; + }; + } + } + Ok(()) + } +} + +impl HirDisplay for ProjectionTy { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + if f.should_truncate() { + return write!(f, "{}", TYPE_HINT_TRUNCATION); + } + + let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); + write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; + if self.parameters.len() > 1 { + write!(f, "<")?; + f.write_joined(&self.parameters[1..], ", ")?; + write!(f, ">")?; + } + write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?; + Ok(()) + } +} + +impl HirDisplay for Ty { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + if f.should_truncate() { + return write!(f, "{}", TYPE_HINT_TRUNCATION); + } + + match self { + Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, + Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, + Ty::Placeholder(id) => { + let generics = generics(f.db, id.parent); + let param_data = &generics.params.types[id.local_id]; + match param_data.provenance { + TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { + write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? + } + TypeParamProvenance::ArgumentImplTrait => { + write!(f, "impl ")?; + let bounds = f.db.generic_predicates_for_param(*id); + let substs = Substs::type_params_for_generics(&generics); + write_bounds_like_dyn_trait( + &bounds.iter().map(|b| b.clone().subst(&substs)).collect::>(), + f, + )?; + } + } + } + Ty::Bound(idx) => write!(f, "?{}", idx)?, + Ty::Dyn(predicates) | Ty::Opaque(predicates) => { + match self { + Ty::Dyn(_) => write!(f, "dyn ")?, + Ty::Opaque(_) => write!(f, "impl ")?, + _ => unreachable!(), + }; + write_bounds_like_dyn_trait(&predicates, f)?; + } + Ty::Unknown => write!(f, "{{unknown}}")?, + Ty::Infer(..) => write!(f, "_")?, + } + Ok(()) + } +} + +fn write_bounds_like_dyn_trait( + predicates: &[GenericPredicate], + f: &mut HirFormatter, +) -> fmt::Result { + // Note: This code is written to produce nice results (i.e. + // corresponding to surface Rust) for types that can occur in + // actual Rust. It will have weird results if the predicates + // aren't as expected (i.e. self types = $0, projection + // predicates for a certain trait come after the Implemented + // predicate for that trait). + let mut first = true; + let mut angle_open = false; + for p in predicates.iter() { + match p { + GenericPredicate::Implemented(trait_ref) => { + if angle_open { + write!(f, ">")?; + } + if !first { + write!(f, " + ")?; + } + // We assume that the self type is $0 (i.e. the + // existential) here, which is the only thing that's + // possible in actual Rust, and hence don't print it + write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; + if trait_ref.substs.len() > 1 { + write!(f, "<")?; + f.write_joined(&trait_ref.substs[1..], ", ")?; + // there might be assoc type bindings, so we leave the angle brackets open + angle_open = true; + } + } + GenericPredicate::Projection(projection_pred) => { + // in types in actual Rust, these will always come + // after the corresponding Implemented predicate + if angle_open { + write!(f, ", ")?; + } else { + write!(f, "<")?; + angle_open = true; + } + let name = + f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); + write!(f, "{} = ", name)?; + projection_pred.ty.hir_fmt(f)?; + } + GenericPredicate::Error => { + if angle_open { + // impl Trait + write!(f, ", ")?; + } else if !first { + // impl Trait + {error} + write!(f, " + ")?; + } + p.hir_fmt(f)?; + } + } + first = false; + } + if angle_open { + write!(f, ">")?; + } + Ok(()) +} + +impl TraitRef { + fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { + if f.should_truncate() { + return write!(f, "{}", TYPE_HINT_TRUNCATION); + } + + self.substs[0].hir_fmt(f)?; + if use_as { + write!(f, " as ")?; + } else { + write!(f, ": ")?; + } + write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?; + if self.substs.len() > 1 { + write!(f, "<")?; + f.write_joined(&self.substs[1..], ", ")?; + write!(f, ">")?; + } + Ok(()) + } +} + +impl HirDisplay for TraitRef { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + self.hir_fmt_ext(f, false) + } +} + +impl HirDisplay for &GenericPredicate { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + HirDisplay::hir_fmt(*self, f) + } +} + +impl HirDisplay for GenericPredicate { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + if f.should_truncate() { + return write!(f, "{}", TYPE_HINT_TRUNCATION); + } + + match self { + GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, + GenericPredicate::Projection(projection_pred) => { + write!(f, "<")?; + projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; + write!( + f, + ">::{} = {}", + f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name, + projection_pred.ty.display(f.db) + )?; + } + GenericPredicate::Error => write!(f, "{{error}}")?, + } + Ok(()) + } +} + +impl HirDisplay for Obligation { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + match self { + Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), + Obligation::Projection(proj) => write!( + f, + "Normalize({} => {})", + proj.projection_ty.display(f.db), + proj.ty.display(f.db) + ), + } + } +} diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 69ad139521..571579cc44 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -41,13 +41,12 @@ mod marks; use std::ops::Deref; use std::sync::Arc; -use std::{fmt, iter, mem}; +use std::{iter, mem}; use hir_def::{ - expr::ExprId, generics::TypeParamProvenance, type_ref::Mutability, AdtId, AssocContainerId, - DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, + expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId, + HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, }; -use hir_expand::name::Name; use ra_db::{impl_intern_key, salsa, CrateId}; use crate::{ @@ -55,7 +54,7 @@ use crate::{ primitive::{FloatTy, IntTy, Uncertain}, utils::{generics, make_mut_slice, Generics}, }; -use display::{HirDisplay, HirFormatter}; +use display::HirDisplay; pub use autoderef::autoderef; pub use infer::{do_infer_query, InferTy, InferenceResult}; @@ -836,369 +835,3 @@ impl TypeWalk for Ty { f(self, binders); } } - -const TYPE_HINT_TRUNCATION: &str = "…"; - -impl HirDisplay for &Ty { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - HirDisplay::hir_fmt(*self, f) - } -} - -impl HirDisplay for ApplicationTy { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); - } - - match self.ctor { - TypeCtor::Bool => write!(f, "bool")?, - TypeCtor::Char => write!(f, "char")?, - TypeCtor::Int(t) => write!(f, "{}", t)?, - TypeCtor::Float(t) => write!(f, "{}", t)?, - TypeCtor::Str => write!(f, "str")?, - TypeCtor::Slice => { - let t = self.parameters.as_single(); - write!(f, "[{}]", t.display(f.db))?; - } - TypeCtor::Array => { - let t = self.parameters.as_single(); - write!(f, "[{}; _]", t.display(f.db))?; - } - TypeCtor::RawPtr(m) => { - let t = self.parameters.as_single(); - write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; - } - TypeCtor::Ref(m) => { - let t = self.parameters.as_single(); - let ty_display = if f.omit_verbose_types() { - t.display_truncated(f.db, f.max_size) - } else { - t.display(f.db) - }; - write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; - } - TypeCtor::Never => write!(f, "!")?, - TypeCtor::Tuple { .. } => { - let ts = &self.parameters; - if ts.len() == 1 { - write!(f, "({},)", ts[0].display(f.db))?; - } else { - write!(f, "(")?; - f.write_joined(&*ts.0, ", ")?; - write!(f, ")")?; - } - } - TypeCtor::FnPtr { .. } => { - let sig = FnSig::from_fn_ptr_substs(&self.parameters); - write!(f, "fn(")?; - f.write_joined(sig.params(), ", ")?; - write!(f, ") -> {}", sig.ret().display(f.db))?; - } - TypeCtor::FnDef(def) => { - let sig = f.db.callable_item_signature(def).subst(&self.parameters); - let name = match def { - CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), - CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), - CallableDef::EnumVariantId(e) => { - let enum_data = f.db.enum_data(e.parent); - enum_data.variants[e.local_id].name.clone() - } - }; - match def { - CallableDef::FunctionId(_) => write!(f, "fn {}", name)?, - CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { - write!(f, "{}", name)? - } - } - if self.parameters.len() > 0 { - let generics = generics(f.db, def.into()); - let (parent_params, self_param, type_params, _impl_trait_params) = - generics.provenance_split(); - let total_len = parent_params + self_param + type_params; - // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? - if total_len > 0 { - write!(f, "<")?; - f.write_joined(&self.parameters.0[..total_len], ", ")?; - write!(f, ">")?; - } - } - write!(f, "(")?; - f.write_joined(sig.params(), ", ")?; - write!(f, ") -> {}", sig.ret().display(f.db))?; - } - TypeCtor::Adt(def_id) => { - let name = match def_id { - AdtId::StructId(it) => f.db.struct_data(it).name.clone(), - AdtId::UnionId(it) => f.db.union_data(it).name.clone(), - AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), - }; - write!(f, "{}", name)?; - if self.parameters.len() > 0 { - write!(f, "<")?; - - let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); - let parameters_to_write = if f.omit_verbose_types() { - match self - .ctor - .as_generic_def() - .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) - .filter(|defaults| !defaults.is_empty()) - { - Option::None => self.parameters.0.as_ref(), - Option::Some(default_parameters) => { - for (i, parameter) in self.parameters.iter().enumerate() { - match (parameter, default_parameters.get(i)) { - (&Ty::Unknown, _) | (_, None) => { - non_default_parameters.push(parameter.clone()) - } - (_, Some(default_parameter)) - if parameter != default_parameter => - { - non_default_parameters.push(parameter.clone()) - } - _ => (), - } - } - &non_default_parameters - } - } - } else { - self.parameters.0.as_ref() - }; - - f.write_joined(parameters_to_write, ", ")?; - write!(f, ">")?; - } - } - TypeCtor::AssociatedType(type_alias) => { - let trait_ = match type_alias.lookup(f.db).container { - AssocContainerId::TraitId(it) => it, - _ => panic!("not an associated type"), - }; - let trait_name = f.db.trait_data(trait_).name.clone(); - let name = f.db.type_alias_data(type_alias).name.clone(); - write!(f, "{}::{}", trait_name, name)?; - if self.parameters.len() > 0 { - write!(f, "<")?; - f.write_joined(&*self.parameters.0, ", ")?; - write!(f, ">")?; - } - } - TypeCtor::Closure { .. } => { - let sig = self.parameters[0] - .callable_sig(f.db) - .expect("first closure parameter should contain signature"); - let return_type_hint = sig.ret().display(f.db); - if sig.params().is_empty() { - write!(f, "|| -> {}", return_type_hint)?; - } else if f.omit_verbose_types() { - write!(f, "|{}| -> {}", TYPE_HINT_TRUNCATION, return_type_hint)?; - } else { - write!(f, "|")?; - f.write_joined(sig.params(), ", ")?; - write!(f, "| -> {}", return_type_hint)?; - }; - } - } - Ok(()) - } -} - -impl HirDisplay for ProjectionTy { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); - } - - let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); - write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; - if self.parameters.len() > 1 { - write!(f, "<")?; - f.write_joined(&self.parameters[1..], ", ")?; - write!(f, ">")?; - } - write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?; - Ok(()) - } -} - -impl HirDisplay for Ty { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); - } - - match self { - Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, - Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, - Ty::Placeholder(id) => { - let generics = generics(f.db, id.parent); - let param_data = &generics.params.types[id.local_id]; - match param_data.provenance { - TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { - write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? - } - TypeParamProvenance::ArgumentImplTrait => { - write!(f, "impl ")?; - let bounds = f.db.generic_predicates_for_param(*id); - let substs = Substs::type_params_for_generics(&generics); - write_bounds_like_dyn_trait( - &bounds.iter().map(|b| b.clone().subst(&substs)).collect::>(), - f, - )?; - } - } - } - Ty::Bound(idx) => write!(f, "?{}", idx)?, - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { - match self { - Ty::Dyn(_) => write!(f, "dyn ")?, - Ty::Opaque(_) => write!(f, "impl ")?, - _ => unreachable!(), - }; - write_bounds_like_dyn_trait(&predicates, f)?; - } - Ty::Unknown => write!(f, "{{unknown}}")?, - Ty::Infer(..) => write!(f, "_")?, - } - Ok(()) - } -} - -fn write_bounds_like_dyn_trait( - predicates: &[GenericPredicate], - f: &mut HirFormatter, -) -> fmt::Result { - // Note: This code is written to produce nice results (i.e. - // corresponding to surface Rust) for types that can occur in - // actual Rust. It will have weird results if the predicates - // aren't as expected (i.e. self types = $0, projection - // predicates for a certain trait come after the Implemented - // predicate for that trait). - let mut first = true; - let mut angle_open = false; - for p in predicates.iter() { - match p { - GenericPredicate::Implemented(trait_ref) => { - if angle_open { - write!(f, ">")?; - } - if !first { - write!(f, " + ")?; - } - // We assume that the self type is $0 (i.e. the - // existential) here, which is the only thing that's - // possible in actual Rust, and hence don't print it - write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; - if trait_ref.substs.len() > 1 { - write!(f, "<")?; - f.write_joined(&trait_ref.substs[1..], ", ")?; - // there might be assoc type bindings, so we leave the angle brackets open - angle_open = true; - } - } - GenericPredicate::Projection(projection_pred) => { - // in types in actual Rust, these will always come - // after the corresponding Implemented predicate - if angle_open { - write!(f, ", ")?; - } else { - write!(f, "<")?; - angle_open = true; - } - let name = - f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); - write!(f, "{} = ", name)?; - projection_pred.ty.hir_fmt(f)?; - } - GenericPredicate::Error => { - if angle_open { - // impl Trait - write!(f, ", ")?; - } else if !first { - // impl Trait + {error} - write!(f, " + ")?; - } - p.hir_fmt(f)?; - } - } - first = false; - } - if angle_open { - write!(f, ">")?; - } - Ok(()) -} - -impl TraitRef { - fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { - if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); - } - - self.substs[0].hir_fmt(f)?; - if use_as { - write!(f, " as ")?; - } else { - write!(f, ": ")?; - } - write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?; - if self.substs.len() > 1 { - write!(f, "<")?; - f.write_joined(&self.substs[1..], ", ")?; - write!(f, ">")?; - } - Ok(()) - } -} - -impl HirDisplay for TraitRef { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - self.hir_fmt_ext(f, false) - } -} - -impl HirDisplay for &GenericPredicate { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - HirDisplay::hir_fmt(*self, f) - } -} - -impl HirDisplay for GenericPredicate { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); - } - - match self { - GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, - GenericPredicate::Projection(projection_pred) => { - write!(f, "<")?; - projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; - write!( - f, - ">::{} = {}", - f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name, - projection_pred.ty.display(f.db) - )?; - } - GenericPredicate::Error => write!(f, "{{error}}")?, - } - Ok(()) - } -} - -impl HirDisplay for Obligation { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - match self { - Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), - Obligation::Projection(proj) => write!( - f, - "Normalize({} => {})", - proj.projection_ty.display(f.db), - proj.ty.display(f.db) - ), - } - } -} From 52dcf3243e979bed92413108864a62bad7b83f15 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 15:06:10 +0100 Subject: [PATCH 06/46] Minor --- xtask/src/install.rs | 50 +++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/xtask/src/install.rs b/xtask/src/install.rs index 99e1eddb16..a279598b97 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs @@ -2,7 +2,7 @@ use std::{env, path::PathBuf, str}; -use anyhow::{Context, Result}; +use anyhow::{bail, format_err, Context, Result}; use crate::cmd::{run, run_with_output, Cmd}; @@ -55,7 +55,7 @@ fn fix_path_for_mac() -> Result<()> { const ROOT_DIR: &str = ""; let home_dir = match env::var("HOME") { Ok(home) => home, - Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e), + Err(e) => bail!("Failed getting HOME from environment with error: {}.", e), }; [ROOT_DIR, &home_dir] @@ -69,7 +69,7 @@ fn fix_path_for_mac() -> Result<()> { if !vscode_path.is_empty() { let vars = match env::var_os("PATH") { Some(path) => path, - None => anyhow::bail!("Could not get PATH variable from env."), + None => bail!("Could not get PATH variable from env."), }; let mut paths = env::split_paths(&vars).collect::>(); @@ -90,7 +90,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { .run(); if npm_version.is_err() { - eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin") + bail!("`npm --version` failed, `npm` is required to build the VS Code plugin") } Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } @@ -102,20 +102,20 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { } .run()?; - let code_binary = ["code", "code-insiders", "codium", "code-oss"].iter().find(|bin| { - Cmd { - unix: &format!("{} --version", bin), - windows: &format!("cmd.exe /c {}.cmd --version", bin), - work_dir: "./editors/code", - } - .run() - .is_ok() - }); - - let code_binary = match code_binary { - Some(it) => it, - None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"), - }; + let code_binary = ["code", "code-insiders", "codium", "code-oss"] + .iter() + .find(|bin| { + Cmd { + unix: &format!("{} --version", bin), + windows: &format!("cmd.exe /c {}.cmd --version", bin), + work_dir: "./editors/code", + } + .run() + .is_ok() + }) + .ok_or_else(|| { + format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?") + })?; Cmd { unix: &format!(r"{} --install-extension ./rust-analyzer-0.1.0.vsix --force", code_binary), @@ -135,24 +135,12 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { .run_with_output()?; if !installed_extensions.contains("rust-analyzer") { - anyhow::bail!( + bail!( "Could not install the Visual Studio Code extension. \ Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again." ); } - if installed_extensions.contains("ra-lsp") { - Cmd { - unix: &format!(r"{} --uninstall-extension matklad.ra-lsp", code_binary), - windows: &format!( - r"cmd.exe /c {}.cmd --uninstall-extension matklad.ra-lsp", - code_binary - ), - work_dir: "./editors/code", - } - .run()?; - } - Ok(()) } From bd3a41cc33a25491c19468aaf24dbba4b467edaf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 15:29:19 +0100 Subject: [PATCH 07/46] Prevent auto-update of dev extension --- editors/code/package-lock.json | 2 +- editors/code/package.json | 3 ++- xtask/src/install.rs | 25 +++++++++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 5c056463e0..a7a1829dd5 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -1,6 +1,6 @@ { "name": "rust-analyzer", - "version": "0.1.0", + "version": "0.2.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/editors/code/package.json b/editors/code/package.json index f687eb8d45..2b8e5aec5e 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -5,7 +5,8 @@ "preview": true, "private": true, "icon": "icon.png", - "version": "0.1.0", + "//": "The real version is in release.yaml, this one just needs to be bigger", + "version": "0.2.0-dev", "publisher": "matklad", "repository": { "url": "https://github.com/rust-analyzer/rust-analyzer.git", diff --git a/xtask/src/install.rs b/xtask/src/install.rs index a279598b97..9bddc8d7f9 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs @@ -1,8 +1,9 @@ //! Installs rust-analyzer language server and/or editor plugin. -use std::{env, path::PathBuf, str}; +use std::{env, fs, path::PathBuf, str}; use anyhow::{bail, format_err, Context, Result}; +use walkdir::WalkDir; use crate::cmd::{run, run_with_output, Cmd}; @@ -95,6 +96,20 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } .run()?; + + let vsixes = || { + WalkDir::new("./editors/code") + .max_depth(1) + .into_iter() + .map(|it| it.unwrap()) + .map(|it| it.path().to_owned()) + .filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix")) + }; + + for path in vsixes() { + fs::remove_file(path)? + } + Cmd { unix: r"npm run package --scripts-prepend-node-path", windows: r"cmd.exe /c npm run package", @@ -102,6 +117,8 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { } .run()?; + let extension = vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string(); + let code_binary = ["code", "code-insiders", "codium", "code-oss"] .iter() .find(|bin| { @@ -118,10 +135,10 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { })?; Cmd { - unix: &format!(r"{} --install-extension ./rust-analyzer-0.1.0.vsix --force", code_binary), + unix: &format!(r"{} --install-extension ./{} --force", code_binary, extension), windows: &format!( - r"cmd.exe /c {}.cmd --install-extension ./rust-analyzer-0.1.0.vsix --force", - code_binary + r"cmd.exe /c {}.cmd --install-extension ./{} --force", + code_binary, extension ), work_dir: "./editors/code", } From ce29497e4324d3e2f2c7c696a212672dbdb46884 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 15:59:19 +0100 Subject: [PATCH 08/46] Replace Cmd with not-bash --- xtask/src/cmd.rs | 56 -------------------- xtask/src/install.rs | 113 ++++++++++++++++------------------------ xtask/src/lib.rs | 58 ++++++++++----------- xtask/src/not_bash.rs | 96 ++++++++++++++++++++++++++++++++++ xtask/src/pre_commit.rs | 6 +-- 5 files changed, 173 insertions(+), 156 deletions(-) delete mode 100644 xtask/src/cmd.rs create mode 100644 xtask/src/not_bash.rs diff --git a/xtask/src/cmd.rs b/xtask/src/cmd.rs deleted file mode 100644 index 37497fb745..0000000000 --- a/xtask/src/cmd.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::process::{Command, Output, Stdio}; - -use anyhow::{Context, Result}; - -use crate::project_root; - -pub struct Cmd<'a> { - pub unix: &'a str, - pub windows: &'a str, - pub work_dir: &'a str, -} - -impl Cmd<'_> { - pub fn run(self) -> Result<()> { - if cfg!(windows) { - run(self.windows, self.work_dir) - } else { - run(self.unix, self.work_dir) - } - } - pub fn run_with_output(self) -> Result { - if cfg!(windows) { - run_with_output(self.windows, self.work_dir) - } else { - run_with_output(self.unix, self.work_dir) - } - } -} - -pub fn run(cmdline: &str, dir: &str) -> Result<()> { - do_run(cmdline, dir, &mut |c| { - c.stdout(Stdio::inherit()); - }) - .map(|_| ()) -} - -pub fn run_with_output(cmdline: &str, dir: &str) -> Result { - let output = do_run(cmdline, dir, &mut |_| {})?; - let stdout = String::from_utf8(output.stdout)?; - let stdout = stdout.trim().to_string(); - Ok(stdout) -} - -fn do_run(cmdline: &str, dir: &str, f: &mut dyn FnMut(&mut Command)) -> Result { - eprintln!("\nwill run: {}", cmdline); - let proj_dir = project_root().join(dir); - let mut args = cmdline.split_whitespace(); - let exec = args.next().unwrap(); - let mut cmd = Command::new(exec); - f(cmd.args(args).current_dir(proj_dir).stderr(Stdio::inherit())); - let output = cmd.output().with_context(|| format!("running `{}`", cmdline))?; - if !output.status.success() { - anyhow::bail!("`{}` exited with {}", cmdline, output.status); - } - Ok(output) -} diff --git a/xtask/src/install.rs b/xtask/src/install.rs index 9bddc8d7f9..f89c939b5b 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs @@ -5,7 +5,7 @@ use std::{env, fs, path::PathBuf, str}; use anyhow::{bail, format_err, Context, Result}; use walkdir::WalkDir; -use crate::cmd::{run, run_with_output, Cmd}; +use crate::not_bash::{pushd, run}; // Latest stable, feel free to send a PR if this lags behind. const REQUIRED_RUST_VERSION: u32 = 41; @@ -83,21 +83,9 @@ fn fix_path_for_mac() -> Result<()> { } fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { - let npm_version = Cmd { - unix: r"npm --version", - windows: r"cmd.exe /c npm --version", - work_dir: "./editors/code", - } - .run(); + let _dir = pushd("./editors/code"); - if npm_version.is_err() { - bail!("`npm --version` failed, `npm` is required to build the VS Code plugin") - } - - Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } - .run()?; - - let vsixes = || { + let list_vsixes = || { WalkDir::new("./editors/code") .max_depth(1) .into_iter() @@ -106,51 +94,46 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { .filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix")) }; - for path in vsixes() { - fs::remove_file(path)? + let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { + ["code", "code-insiders", "codium", "code-oss"] + .iter() + .copied() + .find(|bin| f(bin)) + .ok_or_else(|| { + format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?") + }) + }; + + let installed_extensions; + if cfg!(unix) { + run!("npm --version").context("`npm` is required to build the VS Code plugin")?; + run!("npm install")?; + + let vsix_pkg = { + list_vsixes().try_for_each(fs::remove_file)?; + run!("npm run package --scripts-prepend-node-path")?; + list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string() + }; + + let code = find_code(|bin| run!("{} --version", bin).is_ok())?; + run!("{} --install-extension ./{} --force", code, vsix_pkg)?; + installed_extensions = run!("{} --list-extensions", code; echo = false)?; + } else { + run!("cmd.exe /c npm --version") + .context("`npm` is required to build the VS Code plugin")?; + run!("cmd.exe /c npm install")?; + + let vsix_pkg = { + list_vsixes().try_for_each(fs::remove_file)?; + run!("cmd.exe /c npm run package")?; + list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string() + }; + + let code = find_code(|bin| run!("cmd.exe /c {}.cmd --version", bin).is_ok())?; + run!(r"cmd.exe /c {}.cmd --install-extension ./{} --force", code, vsix_pkg)?; + installed_extensions = run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)?; } - Cmd { - unix: r"npm run package --scripts-prepend-node-path", - windows: r"cmd.exe /c npm run package", - work_dir: "./editors/code", - } - .run()?; - - let extension = vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string(); - - let code_binary = ["code", "code-insiders", "codium", "code-oss"] - .iter() - .find(|bin| { - Cmd { - unix: &format!("{} --version", bin), - windows: &format!("cmd.exe /c {}.cmd --version", bin), - work_dir: "./editors/code", - } - .run() - .is_ok() - }) - .ok_or_else(|| { - format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?") - })?; - - Cmd { - unix: &format!(r"{} --install-extension ./{} --force", code_binary, extension), - windows: &format!( - r"cmd.exe /c {}.cmd --install-extension ./{} --force", - code_binary, extension - ), - work_dir: "./editors/code", - } - .run()?; - - let installed_extensions = Cmd { - unix: &format!(r"{} --list-extensions", code_binary), - windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary), - work_dir: ".", - } - .run_with_output()?; - if !installed_extensions.contains("rust-analyzer") { bail!( "Could not install the Visual Studio Code extension. \ @@ -163,8 +146,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { fn install_server(opts: ServerOpt) -> Result<()> { let mut old_rust = false; - if let Ok(stdout) = run_with_output("cargo --version", ".") { - println!("{}", stdout); + if let Ok(stdout) = run!("cargo --version") { if !check_version(&stdout, REQUIRED_RUST_VERSION) { old_rust = true; } @@ -177,20 +159,17 @@ fn install_server(opts: ServerOpt) -> Result<()> { ) } - let res = if opts.jemalloc { - run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") - } else { - run("cargo install --path crates/ra_lsp_server --locked --force", ".") - }; + let jemalloc = if opts.jemalloc { "--features jemalloc" } else { "" }; + let res = run!("cargo install --path crates/ra_lsp_server --locked --force {}", jemalloc); if res.is_err() && old_rust { eprintln!( "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", REQUIRED_RUST_VERSION, - ) + ); } - res + res.map(drop) } fn check_version(version_output: &str, min_minor_version: u32) -> bool { diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 1bb1882b0f..d2ef2e95b7 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -mod cmd; +pub mod not_bash; pub mod install; pub mod pre_commit; @@ -16,8 +16,8 @@ use std::{ }; use crate::{ - cmd::{run, run_with_output}, codegen::Mode, + not_bash::{pushd, run}, }; pub use anyhow::Result; @@ -38,9 +38,9 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> { ensure_rustfmt()?; if mode == Mode::Verify { - run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?; + run!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN)?; } else { - run(&format!("rustup run {} -- cargo fmt", TOOLCHAIN), ".")?; + run!("rustup run {} -- cargo fmt", TOOLCHAIN)?; } Ok(()) } @@ -70,8 +70,9 @@ fn ensure_rustfmt() -> Result<()> { Ok(status) if status.success() => return Ok(()), _ => (), }; - run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; - run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".") + run!("rustup toolchain install {}", TOOLCHAIN)?; + run!("rustup component add rustfmt --toolchain {}", TOOLCHAIN)?; + Ok(()) } pub fn run_clippy() -> Result<()> { @@ -92,34 +93,31 @@ pub fn run_clippy() -> Result<()> { "clippy::nonminimal_bool", "clippy::redundant_pattern_matching", ]; - run( - &format!( - "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}", - TOOLCHAIN, - allowed_lints.join(" -A ") - ), - ".", + run!( + "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}", + TOOLCHAIN, + allowed_lints.join(" -A ") )?; Ok(()) } fn install_clippy() -> Result<()> { - run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; - run(&format!("rustup component add clippy --toolchain {}", TOOLCHAIN), ".") + run!("rustup toolchain install {}", TOOLCHAIN)?; + run!("rustup component add clippy --toolchain {}", TOOLCHAIN)?; + Ok(()) } pub fn run_fuzzer() -> Result<()> { - match Command::new("cargo") - .args(&["fuzz", "--help"]) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .status() - { - Ok(status) if status.success() => (), - _ => run("cargo install cargo-fuzz", ".")?, + let _d = pushd("./crates/ra_syntax"); + match run!("cargo fuzz --help") { + Ok(_) => (), + _ => { + run!("cargo install cargo-fuzz")?; + } }; - run("rustup run nightly -- cargo fuzz run parser", "./crates/ra_syntax") + run!("rustup run nightly -- cargo fuzz run parser")?; + Ok(()) } /// Cleans the `./target` dir after the build such that only @@ -161,15 +159,15 @@ fn rm_rf(path: &Path) -> Result<()> { } pub fn run_release() -> Result<()> { - run("git switch release", ".")?; - run("git fetch upstream", ".")?; - run("git reset --hard upstream/master", ".")?; - run("git push", ".")?; + run!("git switch release")?; + run!("git fetch upstream")?; + run!("git reset --hard upstream/master")?; + run!("git push")?; let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts"); - let today = run_with_output("date --iso", ".")?; - let commit = run_with_output("git rev-parse HEAD", ".")?; + let today = run!("date --iso")?; + let commit = run!("git rev-parse HEAD")?; let changelog_n = fs::read_dir(changelog_dir.as_path())?.count(); let contents = format!( diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs new file mode 100644 index 0000000000..a2e47c7afd --- /dev/null +++ b/xtask/src/not_bash.rs @@ -0,0 +1,96 @@ +//! A bad shell -- small cross platform module for writing glue code +use std::{ + cell::RefCell, + env, + path::PathBuf, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context, Result}; + +macro_rules! _run { + ($($expr:expr),*) => { + run!($($expr),*; echo = true) + }; + ($($expr:expr),* ; echo = $echo:expr) => { + $crate::not_bash::run_process(format!($($expr),*), $echo) + }; +} +pub(crate) use _run as run; + +pub struct Pushd { + _p: (), +} + +pub fn pushd(path: impl Into) -> Pushd { + Env::with(|env| env.pushd(path.into())); + Pushd { _p: () } +} + +impl Drop for Pushd { + fn drop(&mut self) { + Env::with(|env| env.popd()) + } +} + +#[doc(hidden)] +pub fn run_process(cmd: String, echo: bool) -> Result { + run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd)) +} + +fn run_process_inner(cmd: &str, echo: bool) -> Result { + let cwd = Env::with(|env| env.cwd()); + let mut args = shelx(cmd); + let binary = args.remove(0); + + if echo { + println!("> {}", cmd) + } + + let output = Command::new(binary) + .args(args) + .current_dir(cwd) + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output()?; + let stdout = String::from_utf8(output.stdout)?; + + if echo { + print!("{}", stdout) + } + + if !output.status.success() { + bail!("returned non-zero status: {}", output.status) + } + + Ok(stdout) +} + +// FIXME: some real shell lexing here +fn shelx(cmd: &str) -> Vec { + cmd.split_whitespace().map(|it| it.to_string()).collect() +} + +#[derive(Default)] +struct Env { + pushd_stack: Vec, +} + +impl Env { + fn with T, T>(f: F) -> T { + thread_local! { + static ENV: RefCell = Default::default(); + } + ENV.with(|it| f(&mut *it.borrow_mut())) + } + + fn pushd(&mut self, dir: PathBuf) { + self.pushd_stack.push(dir) + } + fn popd(&mut self) { + self.pushd_stack.pop().unwrap(); + } + fn cwd(&self) -> PathBuf { + self.pushd_stack.last().cloned().unwrap_or_else(|| env::current_dir().unwrap()) + } +} diff --git a/xtask/src/pre_commit.rs b/xtask/src/pre_commit.rs index 1533f64dc0..056f34acfb 100644 --- a/xtask/src/pre_commit.rs +++ b/xtask/src/pre_commit.rs @@ -4,18 +4,18 @@ use std::{fs, path::PathBuf}; use anyhow::{bail, Result}; -use crate::{cmd::run_with_output, project_root, run, run_rustfmt, Mode}; +use crate::{not_bash::run, project_root, run_rustfmt, Mode}; // FIXME: if there are changed `.ts` files, also reformat TypeScript (by // shelling out to `npm fmt`). pub fn run_hook() -> Result<()> { run_rustfmt(Mode::Overwrite)?; - let diff = run_with_output("git diff --diff-filter=MAR --name-only --cached", ".")?; + let diff = run!("git diff --diff-filter=MAR --name-only --cached")?; let root = project_root(); for line in diff.lines() { - run(&format!("git update-index --add {}", root.join(line).to_string_lossy()), ".")?; + run!("git update-index --add {}", root.join(line).display())?; } Ok(()) From 269e2f22a942919d421b89287d5669b2c2607917 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 17:05:56 +0100 Subject: [PATCH 09/46] More declarative fs massaging --- xtask/src/install.rs | 26 ++++++++------------------ xtask/src/not_bash.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/xtask/src/install.rs b/xtask/src/install.rs index f89c939b5b..540a66130b 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs @@ -1,11 +1,10 @@ //! Installs rust-analyzer language server and/or editor plugin. -use std::{env, fs, path::PathBuf, str}; +use std::{env, path::PathBuf, str}; use anyhow::{bail, format_err, Context, Result}; -use walkdir::WalkDir; -use crate::not_bash::{pushd, run}; +use crate::not_bash::{ls, pushd, rm, run}; // Latest stable, feel free to send a PR if this lags behind. const REQUIRED_RUST_VERSION: u32 = 41; @@ -85,15 +84,6 @@ fn fix_path_for_mac() -> Result<()> { fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { let _dir = pushd("./editors/code"); - let list_vsixes = || { - WalkDir::new("./editors/code") - .max_depth(1) - .into_iter() - .map(|it| it.unwrap()) - .map(|it| it.path().to_owned()) - .filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix")) - }; - let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { ["code", "code-insiders", "codium", "code-oss"] .iter() @@ -110,13 +100,13 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { run!("npm install")?; let vsix_pkg = { - list_vsixes().try_for_each(fs::remove_file)?; + rm("*.vsix")?; run!("npm run package --scripts-prepend-node-path")?; - list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string() + ls("*.vsix")?.pop().unwrap() }; let code = find_code(|bin| run!("{} --version", bin).is_ok())?; - run!("{} --install-extension ./{} --force", code, vsix_pkg)?; + run!("{} --install-extension {} --force", code, vsix_pkg.display())?; installed_extensions = run!("{} --list-extensions", code; echo = false)?; } else { run!("cmd.exe /c npm --version") @@ -124,13 +114,13 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { run!("cmd.exe /c npm install")?; let vsix_pkg = { - list_vsixes().try_for_each(fs::remove_file)?; + rm("*.vsix")?; run!("cmd.exe /c npm run package")?; - list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string() + ls("*.vsix")?.pop().unwrap() }; let code = find_code(|bin| run!("cmd.exe /c {}.cmd --version", bin).is_ok())?; - run!(r"cmd.exe /c {}.cmd --install-extension ./{} --force", code, vsix_pkg)?; + run!(r"cmd.exe /c {}.cmd --install-extension {} --force", code, vsix_pkg.display())?; installed_extensions = run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)?; } diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs index a2e47c7afd..4ec1efa739 100644 --- a/xtask/src/not_bash.rs +++ b/xtask/src/not_bash.rs @@ -2,6 +2,8 @@ use std::{ cell::RefCell, env, + ffi::OsStr, + fs, path::PathBuf, process::{Command, Stdio}, }; @@ -33,6 +35,31 @@ impl Drop for Pushd { } } +pub fn rm(glob: &str) -> Result<()> { + let cwd = Env::with(|env| env.cwd()); + ls(glob)?.into_iter().try_for_each(|it| fs::remove_file(cwd.join(it)))?; + Ok(()) +} + +pub fn ls(glob: &str) -> Result> { + let cwd = Env::with(|env| env.cwd()); + let mut res = Vec::new(); + for entry in fs::read_dir(&cwd)? { + let entry = entry?; + if matches(&entry.file_name(), glob) { + let path = entry.path(); + let path = path.strip_prefix(&cwd).unwrap(); + res.push(path.to_path_buf()) + } + } + return Ok(res); + + fn matches(file_name: &OsStr, glob: &str) -> bool { + assert!(glob.starts_with('*')); + file_name.to_string_lossy().ends_with(&glob[1..]) + } +} + #[doc(hidden)] pub fn run_process(cmd: String, echo: bool) -> Result { run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd)) From f2e8dfc8203bfd7cc69f58149577e207f6636fd3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 17:11:19 +0100 Subject: [PATCH 10/46] Cleanup --- xtask/src/not_bash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs index 4ec1efa739..56d6c6c2d8 100644 --- a/xtask/src/not_bash.rs +++ b/xtask/src/not_bash.rs @@ -87,7 +87,7 @@ fn run_process_inner(cmd: &str, echo: bool) -> Result { } if !output.status.success() { - bail!("returned non-zero status: {}", output.status) + bail!("{}", output.status) } Ok(stdout) From 7a832cdf6b9efef2e6c45f56a4385adddd493ea6 Mon Sep 17 00:00:00 2001 From: kjeremy Date: Fri, 14 Feb 2020 11:48:27 -0500 Subject: [PATCH 11/46] Do not register all proposed features Instead only opt-in to CallHierarchy since it has a vscode API but LSP support is still proposed. Discovered while working on SemanticTokens which does not have a vscode API and is still in the proposed state. Somehow enabling it would crash the language server. See https://github.com/microsoft/vscode-languageserver-node/issues/572 --- editors/code/src/client.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 2e3d4aba2d..d2759969be 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -3,6 +3,7 @@ import * as lc from 'vscode-languageclient'; import { window, workspace } from 'vscode'; import { Config } from './config'; import { ensureLanguageServerBinary } from './installation/language_server'; +import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; export async function createClient(config: Config): Promise { // '.' Is the fallback if no folder is open @@ -78,6 +79,10 @@ export async function createClient(config: Config): Promise Date: Fri, 14 Feb 2020 12:02:19 -0500 Subject: [PATCH 12/46] Expect vscode 1.42 --- editors/code/package-lock.json | 18 +++++++++--------- editors/code/package.json | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index a7a1829dd5..c740787359 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -107,9 +107,9 @@ "dev": true }, "@types/vscode": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.41.0.tgz", - "integrity": "sha512-7SfeY5u9jgiELwxyLB3z7l6l/GbN9CqpCQGkcRlB7tKRFBxzbz2PoBfGrLxI1vRfUCIq5+hg5vtDHExwq5j3+A==", + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.42.0.tgz", + "integrity": "sha512-ds6TceMsh77Fs0Mq0Vap6Y72JbGWB8Bay4DrnJlf5d9ui2RSe1wis13oQm+XhguOeH1HUfLGzaDAoupTUtgabw==", "dev": true }, "acorn": { @@ -662,9 +662,9 @@ } }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -860,9 +860,9 @@ "dev": true }, "vsce": { - "version": "1.71.0", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.71.0.tgz", - "integrity": "sha512-7k+LPC4oJYPyyxs0a5nh4A8CleQ6+2EMPiAiX/bDyN+PmwJFm2FFPqLRxdIsIWfFnkW4ZMQBf10+W62dCRd9kQ==", + "version": "1.73.0", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.73.0.tgz", + "integrity": "sha512-6W37Ebbkj3uF3WhT+SCfRtsneRQEFcGvf/XYz+b6OAgDCj4gPurWyDVrqw/HLsbP1WflGIyUfVZ8t5M7kQp6Uw==", "dev": true, "requires": { "azure-devops-node-api": "^7.2.0", diff --git a/editors/code/package.json b/editors/code/package.json index 2b8e5aec5e..db1fe51893 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -16,7 +16,7 @@ "Other" ], "engines": { - "vscode": "^1.41.0" + "vscode": "^1.42.0" }, "scripts": { "vscode:prepublish": "tsc && rollup -c", @@ -36,13 +36,13 @@ "@types/node": "^12.12.25", "@types/node-fetch": "^2.5.4", "@types/throttle-debounce": "^2.1.0", - "@types/vscode": "^1.41.0", + "@types/vscode": "^1.42.0", "rollup": "^1.31.0", "tslib": "^1.10.0", "tslint": "^5.20.1", "typescript": "^3.7.5", "typescript-formatter": "^7.2.2", - "vsce": "^1.71.0" + "vsce": "^1.73.0" }, "activationEvents": [ "onLanguage:rust", From 082dd87b328e8cdf1b99999f9332e056a8bc916b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 18:30:52 +0100 Subject: [PATCH 13/46] Start manual --- docs/user/README.md | 280 ------------------------------------------ docs/user/readme.adoc | 152 +++++++++++++++++++++++ 2 files changed, 152 insertions(+), 280 deletions(-) delete mode 100644 docs/user/README.md create mode 100644 docs/user/readme.adoc diff --git a/docs/user/README.md b/docs/user/README.md deleted file mode 100644 index 14ca6fd642..0000000000 --- a/docs/user/README.md +++ /dev/null @@ -1,280 +0,0 @@ -[github-releases]: https://github.com/rust-analyzer/rust-analyzer/releases - -The main interface to rust-analyzer is the -[LSP](https://microsoft.github.io/language-server-protocol/) implementation. To -install lsp server, you have three options: - -* **Preferred and default:** install the plugin/extension for your IDE and it will ask your permission to automatically download the latest lsp server for you from [GitHub releases][github-releases]. (See docs to find out whether this is implemented for your editor below). -* Manually download prebuilt binaries from [GitHub releases][github-releases] - * `ra_lsp_server-linux` for Linux - * `ra_lsp_server-mac` for Mac - * `ra_lsp_server-windows.exe` for Windows -* Clone the repository and build from sources -```bash -$ git clone git@github.com:rust-analyzer/rust-analyzer && cd rust-analyzer -$ cargo xtask install --server # or cargo install --path ./crates/ra_lsp_server -``` - -This way you will get a binary named `ra_lsp_server` (with os suffix for prebuilt binaries) -which you should be able to use with any LSP-compatible editor. - -We make use of custom extensions to LSP, so special client-side support is required to take full -advantage of rust-analyzer. This repository contains support code for VS Code. - -Rust Analyzer needs sources of rust standard library to work, so -you might also need to execute - -``` -$ rustup component add rust-src -``` - -See [./features.md](./features.md) document for a list of features that are available. - -## VS Code - -### Prerequisites - -You will need the most recent version of VS Code: we don't try to -maintain compatibility with older versions yet. - -### Installation from prebuilt binaries - -We ship prebuilt binaries for Linux, Mac and Windows via -[GitHub releases][github-releases]. -In order to use them you need to install the client VSCode extension. - -Publishing to VS Code marketplace is currently WIP. Thus, you need to manually download -`rust-analyzer-0.1.0.vsix` file from latest [GitHub release][github-releases]. - -After you downloaded the `.vsix` file you can install it from the terminal - -``` -$ code --install-extension rust-analyzer-0.1.0.vsix -``` - -Or open VS Code, press Ctrl+Shift+P, and search for the following command: - -Install from VSIX command - -Press Enter and go to `rust-analyzer-0.1.0.vsix` file through the file explorer. - -Then open some Rust project and you should -see an info message pop-up. - -Download now message - - -Click `Download now`, wait until the progress is 100% and you are ready to go. - -For updates you need to remove installed binary -``` -rm -rf ${HOME}/.config/Code/User/globalStorage/matklad.rust-analyzer -``` - -`"Download latest language server"` command for VSCode and automatic updates detection is currently WIP. - - -### Installation from sources - -In order to build the VS Code plugin from sources, you need to have node.js and npm with -a minimum version of 12 installed. Please refer to -[node.js and npm documentation](https://nodejs.org) for installation instructions. - -The experimental VS Code plugin can be built and installed by executing the -following commands: - -``` -$ git clone https://github.com/rust-analyzer/rust-analyzer.git --depth 1 -$ cd rust-analyzer -$ cargo xtask install -``` - -After that you need to amend your `settings.json` file to explicitly specify the -path to `ra_lsp_server` that you've just built. -```json -{ - "rust-analyzer.raLspServerPath": "ra_lsp_server" -} -``` -This should work on all platforms, otherwise if installed `ra_lsp_server` is not available through your `$PATH` then see how to configure it [here](#setting-up-the-PATH-variable). - - -The automatic installation is expected to *just work* for common cases, if it -doesn't, report bugs! - -**Note** [#1831](https://github.com/rust-analyzer/rust-analyzer/issues/1831): If you are using the popular -[Vim emulation plugin](https://github.com/VSCodeVim/Vim), you will likely -need to turn off the `rust-analyzer.enableEnhancedTyping` setting. -(// TODO: This configuration is no longer available, enhanced typing shoud be disabled via removing Enter key binding, [see this issue](https://github.com/rust-analyzer/rust-analyzer/issues/3051)) - -If you have an unusual setup (for example, `code` is not in the `PATH`), you -should adapt these manual installation instructions: - -``` -$ git clone https://github.com/rust-analyzer/rust-analyzer.git --depth 1 -$ cd rust-analyzer -$ cargo install --path ./crates/ra_lsp_server/ --force --locked -$ cd ./editors/code -$ npm install -$ npm run package -$ code --install-extension ./rust-analyzer-0.1.0.vsix -``` - -It's better to remove existing Rust plugins to avoid interference. - -Beyond basic LSP features, there are some extension commands which you can -invoke via Ctrl+Shift+P or bind to a shortcut. See [./features.md](./features.md) -for details. - -For updates, pull the latest changes from the master branch, run `cargo xtask install` again, and **restart** VS Code instance. -See [microsoft/vscode#72308](https://github.com/microsoft/vscode/issues/72308) for why a full restart is needed. - -### VS Code Remote - -You can also use `rust-analyzer` with the Visual Studio Code Remote extensions -(Remote SSH, Remote WSL, Remote Containers). In this case, however, you have to -manually install the `.vsix` package: - -1. Build the extension on the remote host using the instructions above (ignore the - error if `code` cannot be found in your PATH: VSCode doesn't need to be installed - on the remote host). -2. In Visual Studio Code open a connection to the remote host. -3. Open the Extensions View (`View > Extensions`, keyboard shortcut: `Ctrl+Shift+X`). -4. From the top-right kebab menu (`···`) select `Install from VSIX...` -5. Inside the `rust-analyzer` directory find the `editors/code` subdirectory and choose - the `rust-analyzer-0.1.0.vsix` file. -6. Restart Visual Studio Code and re-establish the connection to the remote host. - -In case of errors please make sure that `~/.cargo/bin` is in your `PATH` on the remote -host. - -### Settings - -* `rust-analyzer.highlightingOn`: enables experimental syntax highlighting. - Colors can be configured via `editor.tokenColorCustomizations`. - As an example, [Pale Fire](https://github.com/matklad/pale-fire/) color scheme tweaks rust colors. -* `rust-analyzer.enableEnhancedTyping`: by default, rust-analyzer intercepts the - `Enter` key to make it easier to continue comments. Note that it may conflict with VIM emulation plugin. -* `rust-analyzer.raLspServerPath`: path to `ra_lsp_server` executable, when absent or `null` defaults to prebuilt binary path -* `rust-analyzer.enableCargoWatchOnStartup`: prompt to install & enable `cargo - watch` for live error highlighting (note, this **does not** use rust-analyzer) -* `rust-analyzer.excludeGlobs`: a list of glob-patterns for exclusion (see globset [docs](https://docs.rs/globset) for syntax). - Note: glob patterns are applied to all Cargo packages and a rooted at a package root. - This is not very intuitive and a limitation of a current implementation. -* `rust-analyzer.useClientWatching`: use client provided file watching instead - of notify watching. -* `rust-analyzer.cargo-watch.command`: `cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` ) -* `rust-analyzer.cargo-watch.arguments`: cargo-watch check arguments. - (e.g: `--features="shumway,pdf"` will run as `cargo watch -x "check --features="shumway,pdf""` ) -* `rust-analyzer.cargo-watch.ignore`: list of patterns for cargo-watch to ignore (will be passed as `--ignore`) -* `rust-analyzer.trace.server`: enables internal logging -* `rust-analyzer.trace.cargo-watch`: enables cargo-watch logging -* `RUST_SRC_PATH`: environment variable that overwrites the sysroot -* `rust-analyzer.featureFlags` -- a JSON object to tweak fine-grained behavior: - ```jsonc - { - // Show diagnostics produced by rust-analyzer itself. - "lsp.diagnostics": true, - // Automatically insert `()` and `<>` when completing functions and types. - "completion.insertion.add-call-parenthesis": true, - // Enable completions like `.if`, `.match`, etc. - "completion.enable-postfix": true, - // Show notification when workspace is fully loaded - "notifications.workspace-loaded": true, - // Show error when no Cargo.toml was found - "notifications.cargo-toml-not-found": true, - } - ``` - - -## Emacs - -* install recent version of `emacs-lsp` package by following the instructions [here][emacs-lsp] -* set `lsp-rust-server` to `'rust-analyzer` -* run `lsp` in a Rust buffer -* (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys - -[emacs-lsp]: https://github.com/emacs-lsp/lsp-mode - - -## Vim and NeoVim (coc-rust-analyzer) - -* Install coc.nvim by following the instructions at [coc.nvim][] (nodejs required) -* Run `:CocInstall coc-rust-analyzer` to install [coc-rust-analyzer], this extension implements _most_ of the features supported in the VSCode extension: - - same configurations as VSCode extension, `rust-analyzer.raLspServerPath`, `rust-analyzer.enableCargoWatchOnStartup` etc. - - same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.startCargoWatch` etc. - - highlighting and inlay_hints are not implemented yet - -[coc.nvim]: https://github.com/neoclide/coc.nvim -[coc-rust-analyzer]: https://github.com/fannheyward/coc-rust-analyzer - -## Vim and NeoVim (LanguageClient-neovim) - -* Install LanguageClient-neovim by following the instructions [here][lang-client-neovim] - - The github project wiki has extra tips on configuration - -* Configure by adding this to your vim/neovim config file (replacing the existing rust specific line if it exists): - -```vim -let g:LanguageClient_serverCommands = { -\ 'rust': ['ra_lsp_server'], -\ } -``` - -[lang-client-neovim]: https://github.com/autozimu/LanguageClient-neovim - -## NeoVim (nvim-lsp) - -NeoVim 0.5 (not yet released) has built in language server support. For a quick start configuration -of rust-analyzer, use [neovim/nvim-lsp](https://github.com/neovim/nvim-lsp#rust_analyzer). -Once `neovim/nvim-lsp` is installed, use `lua require'nvim_lsp'.rust_analyzer.setup({})` in your `init.vim`. - - -## Sublime Text 3 - -Prequisites: - -`LSP` package. - -Installation: - -* Invoke the command palette with Ctrl+Shift+P -* Type `LSP Settings` to open the LSP preferences editor -* Add the following LSP client definition to your settings: - -```json -"rust-analyzer": { - "command": ["ra_lsp_server"], - "languageId": "rust", - "scopes": ["source.rust"], - "syntaxes": [ - "Packages/Rust/Rust.sublime-syntax", - "Packages/Rust Enhanced/RustEnhanced.sublime-syntax" - ], - "initializationOptions": { - "featureFlags": { - } - }, -} -``` - -* You can now invoke the command palette and type LSP enable to locally/globally enable the rust-analyzer LSP (type LSP enable, then choose either locally or globally, then select rust-analyzer) - - - -### Setting up the `PATH` variable - -On Unix systems, `rustup` adds `~/.cargo/bin` to `PATH` by modifying the shell's -startup file. Depending on your configuration, your Desktop Environment might not -actually load it. If you find that `rust-analyzer` only runs when starting the -editor from the terminal, you will have to set up your `PATH` variable manually. - -There are a couple of ways to do that: - -- for Code, set `rust-analyzer.raLspServerPath` to `~/.cargo/bin` (the `~` is - automatically resolved by the extension) -- copy the binary to a location that is already in `PATH`, e.g. `/usr/local/bin` -- on Linux, use PAM to configure the `PATH` variable, by e.g. putting - `PATH DEFAULT=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:@{HOME}/.cargo/bin:@{HOME}/.local/bin` - in your `~/.pam_environment` file; note that this might interfere with other - defaults set by the system administrator via `/etc/environment`. diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc new file mode 100644 index 0000000000..b9ecc70555 --- /dev/null +++ b/docs/user/readme.adoc @@ -0,0 +1,152 @@ += User Manual +:toc: preamble +:sectanchors: +:page-layout: post + + +// Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository + +At it's core, rust-analyzer is a *library* for semantic analysis of the Rust code as it changes over time. +This manual focuses on a specific usage of the library -- the implementation of +https://microsoft.github.io/language-server-protocol/[Language Server Protocol]. +LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic feature like completion or goto definition by talking to an external language server process. + +== Installation + +In theory, one should be able to just install the server binary and have it automatically work with any editor. +We are not there yet, so some editor specific setup is required. + +=== VS Code + +This the best supported editor at the moment. +rust-analyzer plugin for VS Code is maintained +https://github.com/rust-analyzer/rust-analyzer/tree/master/editors/code[in tree]. + +You can install the latest release of the plugin from +https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace]. +By default, the plugin will download the latest version of the server as well. + +image::https://user-images.githubusercontent.com/36276403/74103174-a40df100-4b52-11ea-81f4-372c70797924.png[] + +The server binary is stored in `~/.config/Code/User/globalStorage/matklad.rust-analyzer`. + +Note that we only support the latest version of VS Code. + +==== Updates + +The extension will be updated automatically as new versions become available. +The server update functionality is in progress. +For the time being, the workaround is to remove the binary from `globalStorage` and to restart the extension. + +==== Building From Source + +Alternatively, both the server and the plugin can be installed from source: + +[source] +---- +$ git clone https://github.com/rust-analyzer/rust-analyzer.git && cs rust-analyzer +$ cargo xtask install +---- + +You'll need Cargo, nodejs and npm for this. +To make VS Code use the freshly build server, add this to the settings: + +[source,json] +---- +{ "rust-analyzer.raLspServerPath": "ra_lsp_server" } +---- + +Note that installing via `xtask install` does not work for VS Code Remote, instead you'll need to install the `.vsix` manually. + +=== Language Server Binary + +Other editors generally require `ra_lsp_server` binary to be in `$PATH`. +You can download pre-build binary from +https://github.com/rust-analyzer/rust-analyzer/releases[relases] +page, or you can install it from source using the following command: + +[source,bash] +---- +$ cargo xtask install --server +---- + +=== Emacs + +Emacs support is maintained https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[upstream]. + +1. Install recent version of `emacs-lsp` package by following the instructions https://github.com/emacs-lsp/lsp-mode[here]. +2. Set `lsp-rust-server` to `'rust-analyzer`. +3. Run `lsp` in a Rust buffer. +4. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys. + +=== Vim + +The are several LSP client implementations for vim: + +==== coc-rust-analyzer + +1. Install coc.nvim by following the instructions at + https://github.com/neoclide/coc.nvim[coc.nvim] + (nodejs required) +2. Run `:CocInstall coc-rust-analyzer` to install + https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer], + this extension implements _most_ of the features supported in the VSCode extension: + * same configurations as VSCode extension, `rust-analyzer.raLspServerPath`, `rust-analyzer.enableCargoWatchOnStartup` etc. + * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.startCargoWatch` etc. + * highlighting and inlay_hints are not implemented yet + +==== LanguageClient-neovim + +1. Install LanguageClient-neovim by following the instructions + https://github.com/autozimu/LanguageClient-neovim[here] + * The github project wiki has extra tips on configuration + +2. Configure by adding this to your vim/neovim config file (replacing the existing rust specific line if it exists): ++ +[source,vim] +---- +let g:LanguageClient_serverCommands = { +\ 'rust': ['ra_lsp_server'], +\ } +---- + +==== nvim-lsp + +NeoVim 0.5 (not yet released) has built in language server support. +For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lsp#rust_analyzer[neovim/nvim-lsp]. +Once `neovim/nvim-lsp` is installed, use `lua require'nvim_lsp'.rust_analyzer.setup({})` in your `init.vim`. + +=== Sublime Text 3 + +Prerequisites: + +`LSP` package. + +Installation: + +1. Invoke the command palette with Ctrl+Shift+P +2. Type `LSP Settings` to open the LSP preferences editor +3. Add the following LSP client definition to your settings: ++ +[source,json] +---- +"rust-analyzer": { + "command": ["ra_lsp_server"], + "languageId": "rust", + "scopes": ["source.rust"], + "syntaxes": [ + "Packages/Rust/Rust.sublime-syntax", + "Packages/Rust Enhanced/RustEnhanced.sublime-syntax" + ], + "initializationOptions": { + "featureFlags": { + } + }, +} +---- + +4. You can now invoke the command palette and type LSP enable to locally/globally enable the rust-analyzer LSP (type LSP enable, then choose either locally or globally, then select rust-analyzer) + +== Usage + +See https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/features.md[features.md]. From 9fc2748d476066675dddaf54dfc6c6a8b5e5f450 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 18:33:30 +0100 Subject: [PATCH 14/46] Add dry run mode to xtask release --- xtask/src/lib.rs | 12 +++++++----- xtask/src/main.rs | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index d2ef2e95b7..301a88324e 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -158,11 +158,13 @@ fn rm_rf(path: &Path) -> Result<()> { .with_context(|| format!("failed to remove {:?}", path)) } -pub fn run_release() -> Result<()> { - run!("git switch release")?; - run!("git fetch upstream")?; - run!("git reset --hard upstream/master")?; - run!("git push")?; +pub fn run_release(dry_run: bool) -> Result<()> { + if !dry_run { + run!("git switch release")?; + run!("git fetch upstream")?; + run!("git reset --hard upstream/master")?; + run!("git push")?; + } let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts"); diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 7ca727bde9..a7dffe2cc8 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -93,8 +93,9 @@ FLAGS: run_pre_cache() } "release" => { + let dry_run = args.contains("--dry-run"); args.finish()?; - run_release() + run_release(dry_run) } _ => { eprintln!( From 705f8820c93277768ddc38a743b1ce02a97a9750 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 18:36:42 +0100 Subject: [PATCH 15/46] Update the manual on release --- xtask/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 301a88324e..4050f9d019 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -166,7 +166,8 @@ pub fn run_release(dry_run: bool) -> Result<()> { run!("git push")?; } - let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts"); + let website_root = project_root().join("../rust-analyzer.github.io"); + let changelog_dir = website_root.join("/thisweek/_posts"); let today = run!("date --iso")?; let commit = run!("git rev-parse HEAD")?; @@ -195,5 +196,7 @@ Release: release:{}[] let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); fs::write(&path, &contents)?; + fs::copy(project_root().join("./docs/user/readme.adoc"), website_root.join("manual.adoc"))?; + Ok(()) } From 3f675179e5566514fde2730c0a8b47195a688d6d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 18:56:07 +0100 Subject: [PATCH 16/46] Add fs2 module for better error messages --- xtask/src/lib.rs | 10 +++++----- xtask/src/not_bash.rs | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 4050f9d019..cebb14abcf 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -17,7 +17,7 @@ use std::{ use crate::{ codegen::Mode, - not_bash::{pushd, run}, + not_bash::{fs2, pushd, run}, }; pub use anyhow::Result; @@ -167,11 +167,11 @@ pub fn run_release(dry_run: bool) -> Result<()> { } let website_root = project_root().join("../rust-analyzer.github.io"); - let changelog_dir = website_root.join("/thisweek/_posts"); + let changelog_dir = website_root.join("./thisweek/_posts"); let today = run!("date --iso")?; let commit = run!("git rev-parse HEAD")?; - let changelog_n = fs::read_dir(changelog_dir.as_path())?.count(); + let changelog_n = fs2::read_dir(changelog_dir.as_path())?.count(); let contents = format!( "\ @@ -194,9 +194,9 @@ Release: release:{}[] ); let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); - fs::write(&path, &contents)?; + fs2::write(&path, &contents)?; - fs::copy(project_root().join("./docs/user/readme.adoc"), website_root.join("manual.adoc"))?; + fs2::copy(project_root().join("./docs/user/readme.adoc"), website_root.join("manual.adoc"))?; Ok(()) } diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs index 56d6c6c2d8..027571b62c 100644 --- a/xtask/src/not_bash.rs +++ b/xtask/src/not_bash.rs @@ -10,6 +10,29 @@ use std::{ use anyhow::{bail, Context, Result}; +pub mod fs2 { + use std::{fs, path::Path}; + + use anyhow::{Context, Result}; + + pub fn read_dir>(path: P) -> Result { + let path = path.as_ref(); + fs::read_dir(path).with_context(|| format!("Failed to read {}", path.display())) + } + + pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> { + let path = path.as_ref(); + fs::write(path, contents).with_context(|| format!("Failed to write {}", path.display())) + } + + pub fn copy, Q: AsRef>(from: P, to: Q) -> Result { + let from = from.as_ref(); + let to = to.as_ref(); + fs::copy(from, to) + .with_context(|| format!("Failed to copy {} to {}", from.display(), to.display())) + } +} + macro_rules! _run { ($($expr:expr),*) => { run!($($expr),*; echo = true) From cd956a191f8cb32573be7a78139ad322051e949e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 18:59:26 +0100 Subject: [PATCH 17/46] Trim output --- xtask/src/not_bash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs index 027571b62c..1a7cb7114e 100644 --- a/xtask/src/not_bash.rs +++ b/xtask/src/not_bash.rs @@ -113,7 +113,7 @@ fn run_process_inner(cmd: &str, echo: bool) -> Result { bail!("{}", output.status) } - Ok(stdout) + Ok(stdout.trim().to_string()) } // FIXME: some real shell lexing here From 5acb467894053cb0342eb6ded4d162b4a6912483 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 19:03:45 +0100 Subject: [PATCH 18/46] Move rm_rf to not-bash --- xtask/src/lib.rs | 11 +++-------- xtask/src/not_bash.rs | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index cebb14abcf..25b64301c7 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -9,7 +9,7 @@ mod ast_src; use anyhow::Context; use std::{ - env, fs, + env, io::Write, path::{Path, PathBuf}, process::{Command, Stdio}, @@ -17,7 +17,7 @@ use std::{ use crate::{ codegen::Mode, - not_bash::{fs2, pushd, run}, + not_bash::{fs2, pushd, rm_rf, run}, }; pub use anyhow::Result; @@ -139,7 +139,7 @@ pub fn run_pre_cache() -> Result<()> { } } - fs::remove_file("./target/.rustc_info.json")?; + fs2::remove_file("./target/.rustc_info.json")?; let to_delete = ["ra_", "heavy_test"]; for &dir in ["./target/debug/deps", "target/debug/.fingerprint"].iter() { for entry in Path::new(dir).read_dir()? { @@ -153,11 +153,6 @@ pub fn run_pre_cache() -> Result<()> { Ok(()) } -fn rm_rf(path: &Path) -> Result<()> { - if path.is_file() { fs::remove_file(path) } else { fs::remove_dir_all(path) } - .with_context(|| format!("failed to remove {:?}", path)) -} - pub fn run_release(dry_run: bool) -> Result<()> { if !dry_run { run!("git switch release")?; diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs index 1a7cb7114e..3e30e7279f 100644 --- a/xtask/src/not_bash.rs +++ b/xtask/src/not_bash.rs @@ -4,7 +4,7 @@ use std::{ env, ffi::OsStr, fs, - path::PathBuf, + path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -31,6 +31,16 @@ pub mod fs2 { fs::copy(from, to) .with_context(|| format!("Failed to copy {} to {}", from.display(), to.display())) } + + pub fn remove_file>(path: P) -> Result<()> { + let path = path.as_ref(); + fs::remove_file(path).with_context(|| format!("Failed to remove file {}", path.display())) + } + + pub fn remove_dir_all>(path: P) -> Result<()> { + let path = path.as_ref(); + fs::remove_dir_all(path).with_context(|| format!("Failed to remove dir {}", path.display())) + } } macro_rules! _run { @@ -64,6 +74,15 @@ pub fn rm(glob: &str) -> Result<()> { Ok(()) } +pub fn rm_rf(path: impl AsRef) -> Result<()> { + let path = path.as_ref(); + if path.is_file() { + fs2::remove_file(path) + } else { + fs2::remove_dir_all(path) + } +} + pub fn ls(glob: &str) -> Result> { let cwd = Env::with(|env| env.cwd()); let mut res = Vec::new(); From 2ae71a9ed0df471b8982b847873ab5440463d78d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 19:13:26 +0100 Subject: [PATCH 19/46] Simplify --- xtask/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 25b64301c7..2bcd76d60b 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -109,11 +109,8 @@ fn install_clippy() -> Result<()> { pub fn run_fuzzer() -> Result<()> { let _d = pushd("./crates/ra_syntax"); - match run!("cargo fuzz --help") { - Ok(_) => (), - _ => { - run!("cargo install cargo-fuzz")?; - } + if run!("cargo fuzz --help").is_err() { + run!("cargo install cargo-fuzz")?; }; run!("rustup run nightly -- cargo fuzz run parser")?; From 81904421ac6543db4255698f143bdea0af57207e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 19:15:32 +0100 Subject: [PATCH 20/46] Ensure that the manual is self-improving --- docs/user/readme.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index b9ecc70555..1badba94fa 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc @@ -11,6 +11,9 @@ This manual focuses on a specific usage of the library -- the implementation of https://microsoft.github.io/language-server-protocol/[Language Server Protocol]. LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic feature like completion or goto definition by talking to an external language server process. +To improve this document, send a pull request against +https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc[this file]. + == Installation In theory, one should be able to just install the server binary and have it automatically work with any editor. From 0bfebb8b2042e787c16bbfa26b1cbcd9425de2ab Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 19:33:39 +0100 Subject: [PATCH 21/46] Make AtomicX type resolve again --- crates/ra_project_model/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index fef405b7f5..250255813e 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -418,8 +418,10 @@ pub fn get_rustc_cfg_options() -> CfgOptions { // Some nightly-only cfgs, which are required for stdlib { cfg_options.insert_atom("target_thread_local".into()); - for &target_has_atomic in ["16", "32", "64", "8", "cas", "ptr"].iter() { - cfg_options.insert_key_value("target_has_atomic".into(), target_has_atomic.into()) + for &target_has_atomic in ["8", "16", "32", "64", "cas", "ptr"].iter() { + cfg_options.insert_key_value("target_has_atomic".into(), target_has_atomic.into()); + cfg_options + .insert_key_value("target_has_atomic_load_store".into(), target_has_atomic.into()); } } From 001dd6a2000ce4adada0ab6e4ed8fd67cb8eb569 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 14 Feb 2020 19:16:42 +0100 Subject: [PATCH 22/46] Make Self implement the trait inside trait default methods --- crates/ra_hir_def/src/resolver.rs | 6 +--- crates/ra_hir_ty/src/lower.rs | 32 ++++++++++++++++--- crates/ra_hir_ty/src/marks.rs | 1 + crates/ra_hir_ty/src/tests/traits.rs | 48 ++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index 05cf4646a8..e2b228e80d 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs @@ -542,11 +542,7 @@ impl Resolver { fn push_generic_params_scope(self, db: &impl DefDatabase, def: GenericDefId) -> Resolver { let params = db.generic_params(def); - if params.types.is_empty() { - self - } else { - self.push_scope(Scope::GenericParams { def, params }) - } + self.push_scope(Scope::GenericParams { def, params }) } fn push_impl_block_scope(self, impl_block: ImplId) -> Resolver { diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index df24c16a3f..6a2aded021 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -14,9 +14,9 @@ use hir_def::{ path::{GenericArg, Path, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, type_ref::{TypeBound, TypeRef}, - AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, - LocalStructFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, - VariantId, + AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, + ImplId, LocalStructFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, + UnionId, VariantId, }; use ra_arena::map::ArenaMap; use ra_db::CrateId; @@ -672,11 +672,35 @@ impl TraitEnvironment { pub fn lower(db: &impl HirDatabase, resolver: &Resolver) -> Arc { let ctx = TyLoweringContext::new(db, &resolver) .with_type_param_mode(TypeParamLoweringMode::Placeholder); - let predicates = resolver + let mut predicates = resolver .where_predicates_in_scope() .flat_map(|pred| GenericPredicate::from_where_predicate(&ctx, pred)) .collect::>(); + if let Some(def) = resolver.generic_def() { + let container: Option = match def { + // FIXME: is there a function for this? + GenericDefId::FunctionId(f) => Some(f.lookup(db).container), + GenericDefId::AdtId(_) => None, + GenericDefId::TraitId(_) => None, + GenericDefId::TypeAliasId(t) => Some(t.lookup(db).container), + GenericDefId::ImplId(_) => None, + GenericDefId::EnumVariantId(_) => None, + GenericDefId::ConstId(c) => Some(c.lookup(db).container), + }; + if let Some(AssocContainerId::TraitId(trait_id)) = container { + // add `Self: Trait` to the environment in trait + // function default implementations (and hypothetical code + // inside consts or type aliases) + test_utils::tested_by!(trait_self_implements_self); + let substs = Substs::type_params(db, trait_id); + let trait_ref = TraitRef { trait_: trait_id, substs }; + let pred = GenericPredicate::Implemented(trait_ref); + + predicates.push(pred); + } + } + Arc::new(TraitEnvironment { predicates }) } } diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index 0f754eb9c7..ae47855e95 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs @@ -6,4 +6,5 @@ test_utils::marks!( type_var_resolves_to_int_var match_ergonomics_ref coerce_merge_fail_fallback + trait_self_implements_self ); diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 17611ddbfa..aa2018944c 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -299,6 +299,54 @@ fn test() { ); } +#[test] +fn trait_default_method_self_bound_implements_trait() { + test_utils::covers!(trait_self_implements_self); + assert_snapshot!( + infer(r#" +trait Trait { + fn foo(&self) -> i64; + fn bar(&self) -> { + let x = self.foo(); + } +} +"#), + @r###" + [27; 31) 'self': &Self + [53; 57) 'self': &Self + [62; 97) '{ ... }': () + [76; 77) 'x': i64 + [80; 84) 'self': &Self + [80; 90) 'self.foo()': i64 + "### + ); +} + +#[test] +fn trait_default_method_self_bound_implements_super_trait() { + test_utils::covers!(trait_self_implements_self); + assert_snapshot!( + infer(r#" +trait SuperTrait { + fn foo(&self) -> i64; +} +trait Trait: SuperTrait { + fn bar(&self) -> { + let x = self.foo(); + } +} +"#), + @r###" + [32; 36) 'self': &Self + [86; 90) 'self': &Self + [95; 130) '{ ... }': () + [109; 110) 'x': i64 + [113; 117) 'self': &Self + [113; 123) 'self.foo()': i64 + "### + ); +} + #[test] fn infer_project_associated_type() { // y, z, a don't yet work because of https://github.com/rust-lang/chalk/issues/234 From 4aa538f9a83a514a1bf54300ba541a94fade43be Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 14 Feb 2020 22:02:08 +0200 Subject: [PATCH 23/46] Typo typo --- docs/user/readme.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index 1badba94fa..867aae9750 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc @@ -47,7 +47,7 @@ Alternatively, both the server and the plugin can be installed from source: [source] ---- -$ git clone https://github.com/rust-analyzer/rust-analyzer.git && cs rust-analyzer +$ git clone https://github.com/rust-analyzer/rust-analyzer.git && cd rust-analyzer $ cargo xtask install ---- From f47dc4de8d7aadea17dba023ece70e5b170274b1 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 14 Feb 2020 21:08:25 +0100 Subject: [PATCH 24/46] Check that impl self type matches up with expected self type in path mode Fixes #3144. --- crates/ra_hir_ty/src/marks.rs | 1 + crates/ra_hir_ty/src/method_resolution.rs | 9 ++++++ .../ra_hir_ty/src/tests/method_resolution.rs | 32 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index 0f754eb9c7..4e08284250 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs @@ -4,6 +4,7 @@ test_utils::marks!( type_var_cycles_resolve_completely type_var_cycles_resolve_as_possible type_var_resolves_to_int_var + impl_self_type_match_without_receiver match_ergonomics_ref coerce_merge_fail_fallback ); diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 5283bff28f..4f8c524336 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -425,6 +425,15 @@ fn iterate_inherent_methods( if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { continue; } + // we have to check whether the self type unifies with the type + // that the impl is for. If we have a receiver type, this + // already happens in `is_valid_candidate` above; if not, we + // check it here + if receiver_ty.is_none() && inherent_impl_substs(db, impl_block, self_ty).is_none() + { + test_utils::tested_by!(impl_self_type_match_without_receiver); + continue; + } if let Some(result) = callback(&self_ty.value, item) { return Some(result); } diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 1722563aa1..1f767d324f 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -963,6 +963,38 @@ fn test() { S2.into()<|>; } assert_eq!(t, "{unknown}"); } +#[test] +fn method_resolution_overloaded_method() { + test_utils::covers!(impl_self_type_match_without_receiver); + let t = type_at( + r#" +//- main.rs +struct Wrapper(T); +struct Foo(T); +struct Bar(T); + +impl Wrapper> { + pub fn new(foo_: T) -> Self { + Wrapper(Foo(foo_)) + } +} + +impl Wrapper> { + pub fn new(bar_: T) -> Self { + Wrapper(Bar(bar_)) + } +} + +fn main() { + let a = Wrapper::>::new(1.0); + let b = Wrapper::>::new(1.0); + (a, b)<|>; +} +"#, + ); + assert_eq!(t, "(Wrapper>, Wrapper>)") +} + #[test] fn method_resolution_encountering_fn_type() { type_at( From 4fb427743c27c63a4aa3ccd54c488b22d4308bc2 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 14 Feb 2020 23:04:50 +0200 Subject: [PATCH 25/46] vscode: moved to getters as per matklad --- editors/code/src/client.ts | 18 +++++++------- editors/code/src/config.ts | 38 +++++++++++++----------------- editors/code/src/highlighting.ts | 6 ++--- editors/code/src/inlay_hints.ts | 6 ++--- editors/code/src/status_display.ts | 2 +- 5 files changed, 32 insertions(+), 38 deletions(-) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index a6fb045369..4484b21674 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -10,7 +10,7 @@ export async function createClient(config: Config): Promise("raLspServerPath"); if (langServerPath) { @@ -111,9 +108,7 @@ export class Config { }; } - const prebuiltBinaryName = Config.prebuiltLangServerFileName( - process.platform, process.arch - ); + const prebuiltBinaryName = this.prebuiltLangServerFileName; if (!prebuiltBinaryName) return null; @@ -131,17 +126,16 @@ export class Config { // We don't do runtime config validation here for simplicity. More on stackoverflow: // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension - // FIXME: add codegen for primitive configurations - highlightingOn() { return this.cfg.get("highlightingOn") as boolean; } - rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; } - lruCapacity() { return this.cfg.get("lruCapacity") as null | number; } - displayInlayHints() { return this.cfg.get("displayInlayHints") as boolean; } - maxInlayHintLength() { return this.cfg.get("maxInlayHintLength") as number; } - excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; } - useClientWatching() { return this.cfg.get("useClientWatching") as boolean; } - featureFlags() { return this.cfg.get("featureFlags") as Record; } + get highlightingOn() { return this.cfg.get("highlightingOn") as boolean; } + get rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; } + get lruCapacity() { return this.cfg.get("lruCapacity") as null | number; } + get displayInlayHints() { return this.cfg.get("displayInlayHints") as boolean; } + get maxInlayHintLength() { return this.cfg.get("maxInlayHintLength") as number; } + get excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; } + get useClientWatching() { return this.cfg.get("useClientWatching") as boolean; } + get featureFlags() { return this.cfg.get("featureFlags") as Record; } - cargoWatchOptions(): CargoWatchOptions { + get cargoWatchOptions(): CargoWatchOptions { return { enable: this.cfg.get("cargo-watch.enable") as boolean, arguments: this.cfg.get("cargo-watch.arguments") as string[], @@ -150,7 +144,7 @@ export class Config { }; } - cargoFeatures(): CargoFeatures { + get cargoFeatures(): CargoFeatures { return { noDefaultFeatures: this.cfg.get("cargoFeatures.noDefaultFeatures") as boolean, allFeatures: this.cfg.get("cargoFeatures.allFeatures") as boolean, @@ -159,5 +153,5 @@ export class Config { } // for internal use - withSysroot() { return this.cfg.get("withSysroot", false); } + get withSysroot() { return this.cfg.get("withSysroot", false); } } diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index e2ae31d290..4fbbe3ddc5 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts @@ -11,7 +11,7 @@ export function activateHighlighting(ctx: Ctx) { client.onNotification( 'rust-analyzer/publishDecorations', (params: PublishDecorationsParams) => { - if (!ctx.config.highlightingOn()) return; + if (!ctx.config.highlightingOn) return; const targetEditor = vscode.window.visibleTextEditors.find( editor => { @@ -39,7 +39,7 @@ export function activateHighlighting(ctx: Ctx) { vscode.window.onDidChangeActiveTextEditor( async (editor: vscode.TextEditor | undefined) => { if (!editor || editor.document.languageId !== 'rust') return; - if (!ctx.config.highlightingOn()) return; + if (!ctx.config.highlightingOn) return; const client = ctx.client; if (!client) return; @@ -122,7 +122,7 @@ class Highlighter { string, [vscode.Range[], boolean] > = new Map(); - const rainbowTime = this.ctx.config.rainbowHighlightingOn(); + const rainbowTime = this.ctx.config.rainbowHighlightingOn; for (const tag of this.decorations.keys()) { byTag.set(tag, []); diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 3ff45a6257..1c019a51bc 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts @@ -22,12 +22,12 @@ export function activateInlayHints(ctx: Ctx) { ); vscode.workspace.onDidChangeConfiguration( - async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints()), + async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints), null, ctx.subscriptions ); - ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints())); + ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); } interface InlayHintsParams { @@ -59,7 +59,7 @@ class HintsUpdater { constructor(ctx: Ctx) { this.ctx = ctx; - this.enabled = ctx.config.displayInlayHints(); + this.enabled = ctx.config.displayInlayHints; } async setEnabled(enabled: boolean) { diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts index ae9a7b1b55..993e79d703 100644 --- a/editors/code/src/status_display.ts +++ b/editors/code/src/status_display.ts @@ -7,7 +7,7 @@ import { Ctx } from './ctx'; const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; export function activateStatusDisplay(ctx: Ctx) { - const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions().command); + const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions.command); ctx.pushCleanup(statusDisplay); ctx.onDidRestart(client => ctx.pushCleanup(client.onProgress( WorkDoneProgress.type, From 20fabaf1eeec32e1115627a5552eddf0c074ce37 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 14 Feb 2020 23:06:11 +0200 Subject: [PATCH 26/46] make onConfigChange handler private --- editors/code/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 3ce669330b..8cd89e1196 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -37,7 +37,7 @@ export class Config { console.log("Using configuration:", this.cfg); } - async onConfigChange(event: vscode.ConfigurationChangeEvent) { + private async onConfigChange(event: vscode.ConfigurationChangeEvent) { this.refreshConfig(); const requiresReloadOpt = Config.requiresReloadOpts.find( From 843f03a3d2d242b765dc2b972d7d29ea85ec881e Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Fri, 14 Feb 2020 16:15:27 -0500 Subject: [PATCH 27/46] Bump crate resolution limit for large projects Change-Id: Ie0221e5bcfd1779cd5e241f96b4489e5bd3854c1 --- crates/ra_hir_def/src/nameres/collector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 6352c71ef1..b1f3f525dd 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -146,7 +146,7 @@ where ReachedFixedPoint::Yes => break, ReachedFixedPoint::No => i += 1, } - if i == 1000 { + if i == 10000 { log::error!("name resolution is stuck"); break; } From c9d83bcacc741b81e5ab68f8f47f49c4135b65f5 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Fri, 14 Feb 2020 23:20:49 +0200 Subject: [PATCH 28/46] xtask: bump nodejs requirement while we can! --- xtask/src/install.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/install.rs b/xtask/src/install.rs index 540a66130b..00bbabce43 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs @@ -127,7 +127,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { if !installed_extensions.contains("rust-analyzer") { bail!( "Could not install the Visual Studio Code extension. \ - Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again." + Please make sure you have at least NodeJS 12.x together with the latest version of VS Code installed and try again." ); } From f61134e1980580050f34701db3441081a5519e4c Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sat, 15 Feb 2020 00:15:06 +0200 Subject: [PATCH 29/46] vscode: renmed ArtifactMetadata -> ArtifactReleaseInfo, languageServer -> langServer --- editors/code/src/client.ts | 4 ++-- ...tadata.ts => fetch_latest_artifact_release_info.ts} | 6 +++--- editors/code/src/installation/interfaces.ts | 2 +- .../{language_server.ts => lang_server.ts} | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) rename editors/code/src/installation/{fetch_latest_artifact_metadata.ts => fetch_latest_artifact_release_info.ts} (89%) rename editors/code/src/installation/{language_server.ts => lang_server.ts} (95%) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index dcf9d0c067..33d9b66dfb 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -2,7 +2,7 @@ import * as lc from 'vscode-languageclient'; import * as vscode from 'vscode'; import { Config } from './config'; -import { ensureLanguageServerBinary } from './installation/language_server'; +import { ensureLangServerBinary } from './installation/lang_server'; import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; export async function createClient(config: Config): Promise { @@ -11,7 +11,7 @@ export async function createClient(config: Config): Promise { +): Promise { const repoOwner = encodeURIComponent(repo.owner); const repoName = encodeURIComponent(repo.name); diff --git a/editors/code/src/installation/interfaces.ts b/editors/code/src/installation/interfaces.ts index 8039d0b90f..93ea577d4a 100644 --- a/editors/code/src/installation/interfaces.ts +++ b/editors/code/src/installation/interfaces.ts @@ -6,7 +6,7 @@ export interface GithubRepo { /** * Metadata about particular artifact retrieved from GitHub releases. */ -export interface ArtifactMetadata { +export interface ArtifactReleaseInfo { releaseName: string; downloadUrl: string; } diff --git a/editors/code/src/installation/language_server.ts b/editors/code/src/installation/lang_server.ts similarity index 95% rename from editors/code/src/installation/language_server.ts rename to editors/code/src/installation/lang_server.ts index 4797c3f01a..ccb936bf5f 100644 --- a/editors/code/src/installation/language_server.ts +++ b/editors/code/src/installation/lang_server.ts @@ -7,13 +7,13 @@ import { spawnSync } from "child_process"; import { throttle } from "throttle-debounce"; import { BinarySource } from "./interfaces"; -import { fetchLatestArtifactMetadata } from "./fetch_latest_artifact_metadata"; +import { fetchLatestArtifactReleaseInfo } from "./fetch_latest_artifact_release_info"; import { downloadFile } from "./download_file"; -export async function downloadLatestLanguageServer( +export async function downloadLatestLangServer( {file: artifactFileName, dir: installationDir, repo}: BinarySource.GithubRelease ) { - const { releaseName, downloadUrl } = (await fetchLatestArtifactMetadata( + const { releaseName, downloadUrl } = (await fetchLatestArtifactReleaseInfo( repo, artifactFileName ))!; @@ -53,7 +53,7 @@ export async function downloadLatestLanguageServer( ); console.timeEnd("Downloading ra_lsp_server"); } -export async function ensureLanguageServerBinary( +export async function ensureLangServerBinary( langServerSource: null | BinarySource ): Promise { @@ -97,7 +97,7 @@ export async function ensureLanguageServerBinary( if (userResponse !== "Download now") return null; try { - await downloadLatestLanguageServer(langServerSource); + await downloadLatestLangServer(langServerSource); } catch (err) { vscode.window.showErrorMessage( `Failed to download language server from ${langServerSource.repo.name} ` + From 80d5ba68da2785280cf154d5d812915b99fc0e87 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sat, 15 Feb 2020 00:42:32 +0200 Subject: [PATCH 30/46] vscode: renamed langServer to server --- editors/code/src/client.ts | 8 +++---- editors/code/src/config.ts | 12 +++++----- .../{lang_server.ts => server.ts} | 24 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) rename editors/code/src/installation/{lang_server.ts => server.ts} (87%) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 33d9b66dfb..12c97be2fe 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -2,7 +2,7 @@ import * as lc from 'vscode-languageclient'; import * as vscode from 'vscode'; import { Config } from './config'; -import { ensureLangServerBinary } from './installation/lang_server'; +import { ensureServerBinary } from './installation/server'; import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; export async function createClient(config: Config): Promise { @@ -11,11 +11,11 @@ export async function createClient(config: Config): Promise("raLspServerPath"); + get serverBinarySource(): null | BinarySource { + const serverPath = RA_LSP_DEBUG ?? this.cfg.get("raLspServerPath"); - if (langServerPath) { + if (serverPath) { return { type: BinarySource.Type.ExplicitPath, - path: Config.replaceTildeWithHomeDir(langServerPath) + path: Config.replaceTildeWithHomeDir(serverPath) }; } - const prebuiltBinaryName = this.prebuiltLangServerFileName; + const prebuiltBinaryName = this.prebuiltServerFileName; if (!prebuiltBinaryName) return null; diff --git a/editors/code/src/installation/lang_server.ts b/editors/code/src/installation/server.ts similarity index 87% rename from editors/code/src/installation/lang_server.ts rename to editors/code/src/installation/server.ts index ccb936bf5f..406e2299c3 100644 --- a/editors/code/src/installation/lang_server.ts +++ b/editors/code/src/installation/server.ts @@ -10,7 +10,7 @@ import { BinarySource } from "./interfaces"; import { fetchLatestArtifactReleaseInfo } from "./fetch_latest_artifact_release_info"; import { downloadFile } from "./download_file"; -export async function downloadLatestLangServer( +export async function downloadLatestServer( {file: artifactFileName, dir: installationDir, repo}: BinarySource.GithubRelease ) { const { releaseName, downloadUrl } = (await fetchLatestArtifactReleaseInfo( @@ -53,11 +53,11 @@ export async function downloadLatestLangServer( ); console.timeEnd("Downloading ra_lsp_server"); } -export async function ensureLangServerBinary( - langServerSource: null | BinarySource +export async function ensureServerBinary( + serverSource: null | BinarySource ): Promise { - if (!langServerSource) { + if (!serverSource) { vscode.window.showErrorMessage( "Unfortunately we don't ship binaries for your platform yet. " + "You need to manually clone rust-analyzer repository and " + @@ -69,21 +69,21 @@ export async function ensureLangServerBinary( return null; } - switch (langServerSource.type) { + switch (serverSource.type) { case BinarySource.Type.ExplicitPath: { - if (isBinaryAvailable(langServerSource.path)) { - return langServerSource.path; + if (isBinaryAvailable(serverSource.path)) { + return serverSource.path; } vscode.window.showErrorMessage( - `Unable to run ${langServerSource.path} binary. ` + + `Unable to run ${serverSource.path} binary. ` + `To use the pre-built language server, set "rust-analyzer.raLspServerPath" ` + "value to `null` or remove it from the settings to use it by default." ); return null; } case BinarySource.Type.GithubRelease: { - const prebuiltBinaryPath = path.join(langServerSource.dir, langServerSource.file); + const prebuiltBinaryPath = path.join(serverSource.dir, serverSource.file); if (isBinaryAvailable(prebuiltBinaryPath)) { return prebuiltBinaryPath; @@ -97,10 +97,10 @@ export async function ensureLangServerBinary( if (userResponse !== "Download now") return null; try { - await downloadLatestLangServer(langServerSource); + await downloadLatestServer(serverSource); } catch (err) { vscode.window.showErrorMessage( - `Failed to download language server from ${langServerSource.repo.name} ` + + `Failed to download language server from ${serverSource.repo.name} ` + `GitHub repository: ${err.message}` ); @@ -122,7 +122,7 @@ export async function ensureLangServerBinary( if (!isBinaryAvailable(prebuiltBinaryPath)) assert(false, `Downloaded language server binary is not functional.` + - `Downloaded from: ${JSON.stringify(langServerSource)}` + `Downloaded from: ${JSON.stringify(serverSource)}` ); From 3484d727c3b26e9596ec3bd671e2a76a87cdb5fd Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 15 Feb 2020 18:00:14 +0100 Subject: [PATCH 31/46] Extend analysis-stats a bit This adds some tools helpful when debugging nondeterminism in analysis-stats: - a `--randomize` option that analyses everything in random order - a `-vv` option that prints even more detail Also add a debug log if Chalk fuel is exhausted (which would be a source of nondeterminism, but didn't happen in my tests). I found one source of nondeterminism (rust-lang/chalk#331), but there are still other cases remaining. --- Cargo.lock | 2 + Cargo.toml | 5 ++ crates/ra_cli/Cargo.toml | 2 + crates/ra_cli/src/analysis_stats.rs | 83 +++++++++++++++++++++++++---- crates/ra_cli/src/main.rs | 22 ++++++-- crates/ra_hir_ty/src/traits.rs | 3 ++ 6 files changed, 103 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1651edaad..f9518758aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1015,6 +1015,7 @@ name = "ra_cli" version = "0.1.0" dependencies = [ "env_logger", + "itertools", "pico-args", "ra_batch", "ra_db", @@ -1024,6 +1025,7 @@ dependencies = [ "ra_ide", "ra_prof", "ra_syntax", + "rand 0.7.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e5620b1b7d..c034e24244 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,8 @@ opt-level = 0 [patch.'crates-io'] # rowan = { path = "../rowan" } + +[patch.'https://github.com/rust-lang/chalk.git'] +# chalk-solve = { path = "../chalk/chalk-solve" } +# chalk-rust-ir = { path = "../chalk/chalk-rust-ir" } +# chalk-ir = { path = "../chalk/chalk-ir" } diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index bcd408421a..53d4876f64 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml @@ -6,8 +6,10 @@ authors = ["rust-analyzer developers"] publish = false [dependencies] +itertools = "0.8.0" pico-args = "0.3.0" env_logger = { version = "0.7.1", default-features = false } +rand = { version = "0.7.0", features = ["small_rng"] } ra_syntax = { path = "../ra_syntax" } ra_ide = { path = "../ra_ide" } diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs index 833235bffd..6d2dd34c64 100644 --- a/crates/ra_cli/src/analysis_stats.rs +++ b/crates/ra_cli/src/analysis_stats.rs @@ -2,6 +2,9 @@ use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; +use itertools::Itertools; +use rand::{seq::SliceRandom, thread_rng}; + use hir::{ db::{DefDatabase, HirDatabase}, AssocItem, Crate, HasSource, HirDisplay, ModuleDef, @@ -19,6 +22,7 @@ pub fn run( path: &Path, only: Option<&str>, with_deps: bool, + randomize: bool, ) -> Result<()> { let db_load_time = Instant::now(); let (mut host, roots) = ra_batch::load_cargo(path)?; @@ -41,7 +45,11 @@ pub fn run( }) .collect::>(); - for krate in Crate::all(db) { + let mut krates = Crate::all(db); + if randomize { + krates.shuffle(&mut thread_rng()); + } + for krate in krates { let module = krate.root_module(db).expect("crate without root module"); let file_id = module.definition_source(db).file_id; if members.contains(&db.file_source_root(file_id.original_file(db))) { @@ -50,6 +58,10 @@ pub fn run( } } + if randomize { + visit_queue.shuffle(&mut thread_rng()); + } + println!("Crates in this dir: {}", num_crates); let mut num_decls = 0; let mut funcs = Vec::new(); @@ -79,10 +91,14 @@ pub fn run( println!("Total functions: {}", funcs.len()); println!("Item Collection: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); + if randomize { + funcs.shuffle(&mut thread_rng()); + } + let inference_time = Instant::now(); let mut bar = match verbosity { - Verbosity::Verbose | Verbosity::Normal => ProgressReport::new(funcs.len() as u64), - Verbosity::Quiet => ProgressReport::hidden(), + Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), + _ => ProgressReport::new(funcs.len() as u64), }; bar.tick(); @@ -92,7 +108,20 @@ pub fn run( let mut num_type_mismatches = 0; for f in funcs { let name = f.name(db); - let mut msg = format!("processing: {}", name); + let full_name = f + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(f.name(db))) + .join("::"); + if let Some(only_name) = only { + if name.to_string() != only_name && full_name != only_name { + continue; + } + } + let mut msg = format!("processing: {}", full_name); if verbosity.is_verbose() { let src = f.source(db); let original_file = src.file_id.original_file(db); @@ -100,15 +129,15 @@ pub fn run( let syntax_range = src.value.syntax().text_range(); write!(msg, " ({:?} {})", path, syntax_range).unwrap(); } - bar.set_message(&msg); - if let Some(only_name) = only { - if name.to_string() != only_name { - continue; - } + if verbosity.is_spammy() { + bar.println(format!("{}", msg)); } + bar.set_message(&msg); let f_id = FunctionId::from(f); let body = db.body(f_id.into()); let inference_result = db.infer(f_id.into()); + let (previous_exprs, previous_unknown, previous_partially_unknown) = + (num_exprs, num_exprs_unknown, num_exprs_partially_unknown); for (expr_id, _) in body.exprs.iter() { let ty = &inference_result[expr_id]; num_exprs += 1; @@ -125,6 +154,33 @@ pub fn run( num_exprs_partially_unknown += 1; } } + if only.is_some() && verbosity.is_spammy() { + // in super-verbose mode for just one function, we print every single expression + let (_, sm) = db.body_with_source_map(f_id.into()); + let src = sm.expr_syntax(expr_id); + if let Some(src) = src { + let original_file = src.file_id.original_file(db); + let line_index = host.analysis().file_line_index(original_file).unwrap(); + let text_range = src.value.either( + |it| it.syntax_node_ptr().range(), + |it| it.syntax_node_ptr().range(), + ); + let (start, end) = ( + line_index.line_col(text_range.start()), + line_index.line_col(text_range.end()), + ); + bar.println(format!( + "{}:{}-{}:{}: {}", + start.line + 1, + start.col_utf16, + end.line + 1, + end.col_utf16, + ty.display(db) + )); + } else { + bar.println(format!("unknown location: {}", ty.display(db))); + } + } if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { num_type_mismatches += 1; if verbosity.is_verbose() { @@ -164,6 +220,15 @@ pub fn run( } } } + if verbosity.is_spammy() { + bar.println(format!( + "In {}: {} exprs, {} unknown, {} partial", + full_name, + num_exprs - previous_exprs, + num_exprs_unknown - previous_unknown, + num_exprs_partially_unknown - previous_partially_unknown + )); + } bar.inc(1); } bar.finish_and_clear(); diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 806612c2ce..6a0e447b96 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs @@ -16,6 +16,7 @@ type Result = std::result::Result>; #[derive(Clone, Copy)] pub enum Verbosity { + Spammy, Verbose, Normal, Quiet, @@ -24,7 +25,13 @@ pub enum Verbosity { impl Verbosity { fn is_verbose(self) -> bool { match self { - Verbosity::Verbose => true, + Verbosity::Verbose | Verbosity::Spammy => true, + _ => false, + } + } + fn is_spammy(self) -> bool { + match self { + Verbosity::Spammy => true, _ => false, } } @@ -86,14 +93,18 @@ fn main() -> Result<()> { return Ok(()); } let verbosity = match ( + matches.contains(["-vv", "--spammy"]), matches.contains(["-v", "--verbose"]), matches.contains(["-q", "--quiet"]), ) { - (false, false) => Verbosity::Normal, - (false, true) => Verbosity::Quiet, - (true, false) => Verbosity::Verbose, - (true, true) => Err("Invalid flags: -q conflicts with -v")?, + (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?, + (true, _, false) => Verbosity::Spammy, + (false, false, false) => Verbosity::Normal, + (false, false, true) => Verbosity::Quiet, + (false, true, false) => Verbosity::Verbose, + (false, true, true) => Err("Invalid flags: -q conflicts with -v")?, }; + let randomize = matches.contains("--randomize"); let memory_usage = matches.contains("--memory-usage"); let only: Option = matches.opt_value_from_str(["-o", "--only"])?; let with_deps: bool = matches.contains("--with-deps"); @@ -111,6 +122,7 @@ fn main() -> Result<()> { path.as_ref(), only.as_ref().map(String::as_ref), with_deps, + randomize, )?; } "analysis-bench" => { diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs index 88af61e87a..ff8e75b48e 100644 --- a/crates/ra_hir_ty/src/traits.rs +++ b/crates/ra_hir_ty/src/traits.rs @@ -60,6 +60,9 @@ impl TraitSolver { context.0.db.check_canceled(); let remaining = fuel.get(); fuel.set(remaining - 1); + if remaining == 0 { + log::debug!("fuel exhausted"); + } remaining > 0 }) } From cc43f07e11e2a7715ab6eb14a18f23fc93e5fcdb Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 16 Feb 2020 05:04:51 +0800 Subject: [PATCH 32/46] Disable rollup warning --- editors/code/rollup.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editors/code/rollup.config.js b/editors/code/rollup.config.js index f8d320f466..337385a245 100644 --- a/editors/code/rollup.config.js +++ b/editors/code/rollup.config.js @@ -18,6 +18,7 @@ export default { external: [...nodeBuiltins, 'vscode'], output: { file: './out/main.js', - format: 'cjs' + format: 'cjs', + exports: 'named' } }; From 11dda319413a7c08dc47a2949e20e1ad1f901d5b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 15 Feb 2020 23:23:44 +0100 Subject: [PATCH 33/46] Remove extra dep --- Cargo.lock | 1 - crates/ra_ide_db/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1651edaad..c7d82f77d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1174,7 +1174,6 @@ dependencies = [ "ra_prof", "ra_syntax", "ra_text_edit", - "rand 0.7.3", "rayon", "rustc-hash", "superslice", diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index 716e88bc1b..495fffb5af 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml @@ -22,7 +22,6 @@ fst = { version = "0.3.1", default-features = false } rustc-hash = "1.0" unicase = "2.2.0" superslice = "1.0.0" -rand = { version = "0.7.0", features = ["small_rng"] } once_cell = "1.2.0" ra_syntax = { path = "../ra_syntax" } From 7ff274470eff7060e4d6b370c55c4fe91ab40bb3 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 01:40:07 +0200 Subject: [PATCH 34/46] cicd: simplify vsce call --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ff7a95ee19..eae4fbcb5f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -190,4 +190,4 @@ jobs: - name: Publish Extension working-directory: ./editors/code # token from https://dev.azure.com/rust-analyzer/ - run: ./node_modules/vsce/out/vsce publish 0.1.$(date +%Y%m%d) --pat ${{ secrets.MARKETPLACE_TOKEN }} + run: npx vsce publish 0.1.$(date +%Y%m%d) --pat ${{ secrets.MARKETPLACE_TOKEN }} From 8533fc437b7619e1c289fa7913fdafda533903b8 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 03:08:36 +0200 Subject: [PATCH 35/46] vscode: add version and storage parameters to github binary source --- editors/code/package.json | 2 +- editors/code/src/client.ts | 2 +- editors/code/src/config.ts | 17 ++++++++++++++++- editors/code/src/ctx.ts | 4 ++++ editors/code/src/installation/interfaces.ts | 13 +++++++++++++ 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index a607c21480..96b8e9eb06 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -6,7 +6,7 @@ "private": true, "icon": "icon.png", "//": "The real version is in release.yaml, this one just needs to be bigger", - "version": "0.2.0-dev", + "version": "0.2.20200211-dev", "publisher": "matklad", "repository": { "url": "https://github.com/rust-analyzer/rust-analyzer.git", diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 12c97be2fe..efef820abf 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -11,7 +11,7 @@ export async function createClient(config: Config): Promise `${Config.rootSection}.${opt}`); + private static readonly extensionVersion: string = (() => { + const packageJsonVersion = vscode + .extensions + .getExtension("matklad.rust-analyzer")! + .packageJSON + .version as string; // n.n.YYYYMMDD + + const realVersionRegexp = /^\d+\.\d+\.(\d{4})(\d{2})(\d{2})/; + const [, yyyy, mm, dd] = packageJsonVersion.match(realVersionRegexp)!; + + return `${yyyy}-${mm}-${dd}`; + })(); + private cfg!: vscode.WorkspaceConfiguration; constructor(private readonly ctx: vscode.ExtensionContext) { @@ -98,7 +111,7 @@ export class Config { } } - get serverBinarySource(): null | BinarySource { + get serverSource(): null | BinarySource { const serverPath = RA_LSP_DEBUG ?? this.cfg.get("raLspServerPath"); if (serverPath) { @@ -116,6 +129,8 @@ export class Config { type: BinarySource.Type.GithubRelease, dir: this.ctx.globalStoragePath, file: prebuiltBinaryName, + storage: this.ctx.globalState, + version: Config.extensionVersion, repo: { name: "rust-analyzer", owner: "rust-analyzer", diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 70042a479e..9fcf2ec382 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -60,6 +60,10 @@ export class Ctx { this.pushCleanup(d); } + get globalState(): vscode.Memento { + return this.extCtx.globalState; + } + get subscriptions(): Disposable[] { return this.extCtx.subscriptions; } diff --git a/editors/code/src/installation/interfaces.ts b/editors/code/src/installation/interfaces.ts index 93ea577d4a..e40839e4b2 100644 --- a/editors/code/src/installation/interfaces.ts +++ b/editors/code/src/installation/interfaces.ts @@ -1,3 +1,5 @@ +import * as vscode from "vscode"; + export interface GithubRepo { name: string; owner: string; @@ -50,6 +52,17 @@ export namespace BinarySource { * and in local `.dir`. */ file: string; + + /** + * Tag of github release that denotes a version required by this extension. + */ + version: string; + + /** + * Object that provides `get()/update()` operations to store metadata + * about the actual binary, e.g. its actual version. + */ + storage: vscode.Memento; } } From 0f7abeb03599964e58d979820134c9e7a61a690e Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 03:12:25 +0200 Subject: [PATCH 36/46] vscode: add release tag option to fetchArtifactReleaseInfo() --- ...se_info.ts => fetch_artifact_release_info.ts} | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) rename editors/code/src/installation/{fetch_latest_artifact_release_info.ts => fetch_artifact_release_info.ts} (68%) diff --git a/editors/code/src/installation/fetch_latest_artifact_release_info.ts b/editors/code/src/installation/fetch_artifact_release_info.ts similarity index 68% rename from editors/code/src/installation/fetch_latest_artifact_release_info.ts rename to editors/code/src/installation/fetch_artifact_release_info.ts index 29ee029a70..7d497057aa 100644 --- a/editors/code/src/installation/fetch_latest_artifact_release_info.ts +++ b/editors/code/src/installation/fetch_artifact_release_info.ts @@ -3,24 +3,30 @@ import { GithubRepo, ArtifactReleaseInfo } from "./interfaces"; const GITHUB_API_ENDPOINT_URL = "https://api.github.com"; + /** - * Fetches the latest release from GitHub `repo` and returns metadata about - * `artifactFileName` shipped with this release or `null` if no such artifact was published. + * Fetches the release with `releaseTag` (or just latest release when not specified) + * from GitHub `repo` and returns metadata about `artifactFileName` shipped with + * this release or `null` if no such artifact was published. */ -export async function fetchLatestArtifactReleaseInfo( - repo: GithubRepo, artifactFileName: string +export async function fetchArtifactReleaseInfo( + repo: GithubRepo, artifactFileName: string, releaseTag?: string ): Promise { const repoOwner = encodeURIComponent(repo.owner); const repoName = encodeURIComponent(repo.name); - const apiEndpointPath = `/repos/${repoOwner}/${repoName}/releases/latest`; + const apiEndpointPath = releaseTag + ? `/repos/${repoOwner}/${repoName}/releases/tags/${releaseTag}` + : `/repos/${repoOwner}/${repoName}/releases/latest`; + const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath; // We skip runtime type checks for simplicity (here we cast from `any` to `GithubRelease`) console.log("Issuing request for released artifacts metadata to", requestUrl); + // FIXME: handle non-ok response const response: GithubRelease = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } }) From b9188226fabb00de3c5f3706186e96b01c223566 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 03:13:06 +0200 Subject: [PATCH 37/46] vscode: extract downloadArtifact() function --- .../src/installation/download_artifact.ts | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 editors/code/src/installation/download_artifact.ts diff --git a/editors/code/src/installation/download_artifact.ts b/editors/code/src/installation/download_artifact.ts new file mode 100644 index 0000000000..de655f8f4b --- /dev/null +++ b/editors/code/src/installation/download_artifact.ts @@ -0,0 +1,58 @@ +import * as vscode from "vscode"; +import * as path from "path"; +import { promises as fs } from "fs"; +import { strict as assert } from "assert"; + +import { ArtifactReleaseInfo } from "./interfaces"; +import { downloadFile } from "./download_file"; +import { throttle } from "throttle-debounce"; + +/** + * Downloads artifact from given `downloadUrl`. + * Creates `installationDir` if it is not yet created and put the artifact under + * `artifactFileName`. + * Displays info about the download progress in an info message printing the name + * of the artifact as `displayName`. + */ +export async function downloadArtifact( + {downloadUrl, releaseName}: ArtifactReleaseInfo, + artifactFileName: string, + installationDir: string, + displayName: string, +) { + await fs.mkdir(installationDir).catch(err => assert.strictEqual( + err?.code, + "EEXIST", + `Couldn't create directory "${installationDir}" to download `+ + `${artifactFileName} artifact: ${err.message}` + )); + + const installationPath = path.join(installationDir, artifactFileName); + + console.time(`Downloading ${artifactFileName}`); + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + cancellable: false, // FIXME: add support for canceling download? + title: `Downloading ${displayName} (${releaseName})` + }, + async (progress, _cancellationToken) => { + let lastPrecentage = 0; + const filePermissions = 0o755; // (rwx, r_x, r_x) + await downloadFile(downloadUrl, installationPath, filePermissions, throttle( + 200, + /* noTrailing: */ true, + (readBytes, totalBytes) => { + const newPercentage = (readBytes / totalBytes) * 100; + progress.report({ + message: newPercentage.toFixed(0) + "%", + increment: newPercentage - lastPrecentage + }); + + lastPrecentage = newPercentage; + }) + ); + } + ); + console.timeEnd(`Downloading ${artifactFileName}`); +} From 467b925b53edfd77981344346c3ae2200acdaabe Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 03:14:20 +0200 Subject: [PATCH 38/46] vscode: save binary version when downloading and download only version that matches TypeScript extension version --- editors/code/src/installation/server.ts | 184 +++++++++++------------- 1 file changed, 80 insertions(+), 104 deletions(-) diff --git a/editors/code/src/installation/server.ts b/editors/code/src/installation/server.ts index 406e2299c3..80cb719e36 100644 --- a/editors/code/src/installation/server.ts +++ b/editors/code/src/installation/server.ts @@ -1,63 +1,15 @@ import * as vscode from "vscode"; import * as path from "path"; import { strict as assert } from "assert"; -import { promises as fs } from "fs"; import { promises as dns } from "dns"; import { spawnSync } from "child_process"; -import { throttle } from "throttle-debounce"; import { BinarySource } from "./interfaces"; -import { fetchLatestArtifactReleaseInfo } from "./fetch_latest_artifact_release_info"; -import { downloadFile } from "./download_file"; +import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info"; +import { downloadArtifact } from "./download_artifact"; -export async function downloadLatestServer( - {file: artifactFileName, dir: installationDir, repo}: BinarySource.GithubRelease -) { - const { releaseName, downloadUrl } = (await fetchLatestArtifactReleaseInfo( - repo, artifactFileName - ))!; - - await fs.mkdir(installationDir).catch(err => assert.strictEqual( - err?.code, - "EEXIST", - `Couldn't create directory "${installationDir}" to download `+ - `language server binary: ${err.message}` - )); - - const installationPath = path.join(installationDir, artifactFileName); - - console.time("Downloading ra_lsp_server"); - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - cancellable: false, // FIXME: add support for canceling download? - title: `Downloading language server (${releaseName})` - }, - async (progress, _cancellationToken) => { - let lastPrecentage = 0; - const filePermissions = 0o755; // (rwx, r_x, r_x) - await downloadFile(downloadUrl, installationPath, filePermissions, throttle( - 200, - /* noTrailing: */ true, - (readBytes, totalBytes) => { - const newPercentage = (readBytes / totalBytes) * 100; - progress.report({ - message: newPercentage.toFixed(0) + "%", - increment: newPercentage - lastPrecentage - }); - - lastPrecentage = newPercentage; - }) - ); - } - ); - console.timeEnd("Downloading ra_lsp_server"); -} -export async function ensureServerBinary( - serverSource: null | BinarySource -): Promise { - - if (!serverSource) { +export async function ensureServerBinary(source: null | BinarySource): Promise { + if (!source) { vscode.window.showErrorMessage( "Unfortunately we don't ship binaries for your platform yet. " + "You need to manually clone rust-analyzer repository and " + @@ -69,80 +21,104 @@ export async function ensureServerBinary( return null; } - switch (serverSource.type) { + switch (source.type) { case BinarySource.Type.ExplicitPath: { - if (isBinaryAvailable(serverSource.path)) { - return serverSource.path; + if (isBinaryAvailable(source.path)) { + return source.path; } vscode.window.showErrorMessage( - `Unable to run ${serverSource.path} binary. ` + + `Unable to run ${source.path} binary. ` + `To use the pre-built language server, set "rust-analyzer.raLspServerPath" ` + "value to `null` or remove it from the settings to use it by default." ); return null; } case BinarySource.Type.GithubRelease: { - const prebuiltBinaryPath = path.join(serverSource.dir, serverSource.file); + const prebuiltBinaryPath = path.join(source.dir, source.file); - if (isBinaryAvailable(prebuiltBinaryPath)) { + const installedVersion: null | string = getServerVersion(source.storage); + const requiredVersion: string = source.version; + + console.log("Installed version:", installedVersion, "required:", requiredVersion); + + if (isBinaryAvailable(prebuiltBinaryPath) && installedVersion == requiredVersion) { + // FIXME: check for new releases and notify the user to update if possible return prebuiltBinaryPath; } const userResponse = await vscode.window.showInformationMessage( - "Language server binary for rust-analyzer was not found. " + + `Language server version ${source.version} for rust-analyzer is not installed. ` + "Do you want to download it now?", "Download now", "Cancel" ); if (userResponse !== "Download now") return null; - try { - await downloadLatestServer(serverSource); - } catch (err) { - vscode.window.showErrorMessage( - `Failed to download language server from ${serverSource.repo.name} ` + - `GitHub repository: ${err.message}` - ); - - console.error(err); - - dns.resolve('example.com').then( - addrs => console.log("DNS resolution for example.com was successful", addrs), - err => { - console.error( - "DNS resolution for example.com failed, " + - "there might be an issue with Internet availability" - ); - console.error(err); - } - ); - - return null; - } - - if (!isBinaryAvailable(prebuiltBinaryPath)) assert(false, - `Downloaded language server binary is not functional.` + - `Downloaded from: ${JSON.stringify(serverSource)}` - ); - - - vscode.window.showInformationMessage( - "Rust analyzer language server was successfully installed 🦀" - ); + if (!await downloadServer(source)) return null; return prebuiltBinaryPath; } } - - function isBinaryAvailable(binaryPath: string) { - const res = spawnSync(binaryPath, ["--version"]); - - // ACHTUNG! `res` type declaration is inherently wrong, see - // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42221 - - console.log("Checked binary availablity via --version", res); - console.log(binaryPath, "--version output:", res.output?.map(String)); - - return res.status === 0; - } +} + +async function downloadServer(source: BinarySource.GithubRelease): Promise { + try { + const releaseInfo = (await fetchArtifactReleaseInfo(source.repo, source.file, source.version))!; + + await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); + await setServerVersion(source.storage, releaseInfo.releaseName); + } catch (err) { + vscode.window.showErrorMessage( + `Failed to download language server from ${source.repo.name} ` + + `GitHub repository: ${err.message}` + ); + + console.error(err); + + dns.resolve('example.com').then( + addrs => console.log("DNS resolution for example.com was successful", addrs), + err => { + console.error( + "DNS resolution for example.com failed, " + + "there might be an issue with Internet availability" + ); + console.error(err); + } + ); + return false; + } + + if (!isBinaryAvailable(path.join(source.dir, source.file))) assert(false, + `Downloaded language server binary is not functional.` + + `Downloaded from: ${JSON.stringify(source, null, 4)}` + ); + + vscode.window.showInformationMessage( + "Rust analyzer language server was successfully installed 🦀" + ); + + return true; +} + +function isBinaryAvailable(binaryPath: string): boolean { + const res = spawnSync(binaryPath, ["--version"]); + + // ACHTUNG! `res` type declaration is inherently wrong, see + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42221 + + console.log("Checked binary availablity via --version", res); + console.log(binaryPath, "--version output:", res.output?.map(String)); + + return res.status === 0; +} + +function getServerVersion(storage: vscode.Memento): null | string { + const version = storage.get("server-version", null); + console.log("Get server-version:", version); + return version; +} + +async function setServerVersion(storage: vscode.Memento, version: string): Promise { + console.log("Set server-version:", version); + await storage.update("server-version", version.toString()); } From 325eba58a286c147f19dada5f205aa9e2ec6f391 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 03:56:54 +0200 Subject: [PATCH 39/46] vscode: updated user docs vscode updates section --- docs/user/readme.adoc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index 867aae9750..553687e789 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc @@ -27,8 +27,9 @@ https://github.com/rust-analyzer/rust-analyzer/tree/master/editors/code[in tree] You can install the latest release of the plugin from https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace]. -By default, the plugin will download the latest version of the server as well. +By default, the plugin will download the matching version of the server as well. +// FIXME: update the image (its text has changed) image::https://user-images.githubusercontent.com/36276403/74103174-a40df100-4b52-11ea-81f4-372c70797924.png[] The server binary is stored in `~/.config/Code/User/globalStorage/matklad.rust-analyzer`. @@ -37,9 +38,7 @@ Note that we only support the latest version of VS Code. ==== Updates -The extension will be updated automatically as new versions become available. -The server update functionality is in progress. -For the time being, the workaround is to remove the binary from `globalStorage` and to restart the extension. +The extension will be updated automatically as new versions become available. It will ask your permission to download the matching language server version binary if needed. ==== Building From Source From 3068aab82d06f4fa2082bcaa34d57452247a6cbb Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 11:15:19 +0200 Subject: [PATCH 40/46] vscode: fix the default value for withSysroot --- editors/code/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 7866ed7e1b..8c033052be 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -153,5 +153,5 @@ export class Config { } // for internal use - get withSysroot() { return this.cfg.get("withSysroot", false); } + get withSysroot() { return this.cfg.get("withSysroot", true) as boolean; } } From e14e7ffa34bf9a458681fe7490ac2d51b02ff908 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 16 Feb 2020 12:57:19 +0100 Subject: [PATCH 41/46] Fix coercion of &T to itself The autoderef coercion logic did not handle matching placeholders. This led to some type mismatches. --- crates/ra_hir_ty/src/infer/unify.rs | 2 ++ crates/ra_hir_ty/src/tests/coercion.rs | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index fe05642ae3..1dc842f40e 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -249,6 +249,8 @@ impl InferenceTable { match (ty1, ty2) { (Ty::Unknown, _) | (_, Ty::Unknown) => true, + (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true, + (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index fc5ef36a59..42330b269f 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -526,3 +526,25 @@ fn test() { "### ); } + +#[test] +fn coerce_placeholder_ref() { + // placeholders should unify, even behind references + assert_snapshot!( + infer_with_mismatches(r#" +struct S { t: T } +impl S { + fn get(&self) -> &TT { + &self.t + } +} +"#, true), + @r###" + [51; 55) 'self': &S + [64; 87) '{ ... }': &TT + [74; 81) '&self.t': &TT + [75; 79) 'self': &S + [75; 81) 'self.t': TT + "### + ); +} From 53b5f4ba98cfd5af8603c24720bdadd11bb4bfea Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 16:48:17 +0200 Subject: [PATCH 42/46] vscode: update exclusiveMinimum validation according to JSONSchemaV4 specs --- editors/code/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editors/code/package.json b/editors/code/package.json index 96b8e9eb06..614d36cf71 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -248,7 +248,8 @@ "rust-analyzer.maxInlayHintLength": { "type": "number", "default": 20, - "exclusiveMinimum": 0, + "minimum": 0, + "exclusiveMinimum": true, "description": "Maximum length for inlay hints" }, "rust-analyzer.cargoFeatures.noDefaultFeatures": { From 0565657ed1b51e90f7876158d7b484db1a6c46b2 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 17:55:15 +0200 Subject: [PATCH 43/46] vscode: fix all integer -> number and add nullablitiy to maxInlayHintLength --- editors/code/package.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 614d36cf71..97f2e63cd7 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -233,10 +233,7 @@ "description": "Trace requests to the ra_lsp_server" }, "rust-analyzer.lruCapacity": { - "type": [ - "number", - "null" - ], + "type": [ "null", "integer" ], "default": null, "description": "Number of syntax trees rust-analyzer keeps in memory" }, @@ -246,7 +243,7 @@ "description": "Display additional type and parameter information in the editor" }, "rust-analyzer.maxInlayHintLength": { - "type": "number", + "type": [ "null", "integer" ], "default": 20, "minimum": 0, "exclusiveMinimum": true, From 56b64cac9cbac47e3cf3b04c8cf8e418564e12bd Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 16 Feb 2020 18:00:49 +0200 Subject: [PATCH 44/46] vscode: added minimum bound for lruCapacity option --- editors/code/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editors/code/package.json b/editors/code/package.json index 97f2e63cd7..ed1cae2ab2 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -235,6 +235,8 @@ "rust-analyzer.lruCapacity": { "type": [ "null", "integer" ], "default": null, + "minimum": 0, + "exclusiveMinimum": true, "description": "Number of syntax trees rust-analyzer keeps in memory" }, "rust-analyzer.displayInlayHints": { From 28fa5edbcead160d01932d49573bc60d8d80602c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 16 Feb 2020 17:06:01 +0100 Subject: [PATCH 45/46] Add module colors to css --- crates/ra_ide/src/snapshots/highlighting.html | 1 + crates/ra_ide/src/snapshots/rainbow_highlighting.html | 1 + crates/ra_ide/src/syntax_highlighting.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 1cc55e78b5..a02dbaf2fe 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -16,6 +16,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .literal { color: #BFEBBF; } .literal\.numeric { color: #6A8759; } .macro { color: #94BFF3; } +.module { color: #AFD8AF; } .variable { color: #DCDCCC; } .variable\.mut { color: #DCDCCC; text-decoration: underline; } diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 918fd4b978..95f038f000 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html @@ -16,6 +16,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .literal { color: #BFEBBF; } .literal\.numeric { color: #6A8759; } .macro { color: #94BFF3; } +.module { color: #AFD8AF; } .variable { color: #DCDCCC; } .variable\.mut { color: #DCDCCC; text-decoration: underline; } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 174e135951..20c414ca1b 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -365,6 +365,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .literal { color: #BFEBBF; } .literal\\.numeric { color: #6A8759; } .macro { color: #94BFF3; } +.module { color: #AFD8AF; } .variable { color: #DCDCCC; } .variable\\.mut { color: #DCDCCC; text-decoration: underline; } From 98cc51580d0b8a6662f0155d8a45a8cfff469d72 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 16 Feb 2020 18:04:08 +0100 Subject: [PATCH 46/46] Enable profiling for bench --- crates/ra_cli/src/analysis_bench.rs | 2 ++ crates/ra_lsp_server/src/main.rs | 7 +------ crates/ra_prof/src/lib.rs | 7 +++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs index 5485a38ff2..764df6b9ef 100644 --- a/crates/ra_cli/src/analysis_bench.rs +++ b/crates/ra_cli/src/analysis_bench.rs @@ -20,6 +20,8 @@ pub(crate) enum Op { } pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { + ra_prof::init(); + let start = Instant::now(); eprint!("loading: "); let (mut host, roots) = ra_batch::load_cargo(path)?; diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index c8a017c5c8..ed2eaabd4c 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs @@ -15,13 +15,8 @@ fn main() -> Result<()> { fn setup_logging() -> Result<()> { std::env::set_var("RUST_BACKTRACE", "short"); - env_logger::try_init()?; - - ra_prof::set_filter(match std::env::var("RA_PROFILE") { - Ok(spec) => ra_prof::Filter::from_spec(&spec), - Err(_) => ra_prof::Filter::disabled(), - }); + ra_prof::init(); Ok(()) } diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs index d38ff397e3..c0bfbc2ee1 100644 --- a/crates/ra_prof/src/lib.rs +++ b/crates/ra_prof/src/lib.rs @@ -26,6 +26,13 @@ pub use crate::memory_usage::{Bytes, MemoryUsage}; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; +pub fn init() { + set_filter(match std::env::var("RA_PROFILE") { + Ok(spec) => Filter::from_spec(&spec), + Err(_) => Filter::disabled(), + }); +} + /// Set profiling filter. It specifies descriptions allowed to profile. /// This is helpful when call stack has too many nested profiling scopes. /// Additionally filter can specify maximum depth of profiling scopes nesting.