mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
add support of feature flag for runnables #4464
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
commit
a7c8aa7c60
130 changed files with 2984 additions and 1980 deletions
44
Cargo.lock
generated
44
Cargo.lock
generated
|
@ -2,9 +2,9 @@
|
|||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456d75cbb82da1ad150c8a9d97285ffcd21c9931dcb11e995903e7d75141b38b"
|
||||
checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
@ -20,9 +20,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.29"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc98824304f5513bb8f862f9e5985219003de4d730689e59d8f28818283a6fe4"
|
||||
checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
|
||||
|
||||
[[package]]
|
||||
name = "anymap"
|
||||
|
@ -55,9 +55,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.47"
|
||||
version = "0.3.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5393cb2f40a6fae0014c9af00018e95846f3b241b331a6b7733c326d3e58108"
|
||||
checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
|
@ -101,9 +101,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.52"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d"
|
||||
checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -360,9 +360,9 @@ checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
|||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
|
@ -463,9 +463,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
|
||||
checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -809,9 +809,9 @@ checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2"
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
|
||||
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
|
||||
|
||||
[[package]]
|
||||
name = "ordermap"
|
||||
|
@ -895,9 +895,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
|||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
|
@ -907,18 +907,18 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319"
|
||||
checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e"
|
||||
checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1610,9 +1610,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060"
|
||||
checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
27
crates/ra_assists/src/assist_config.rs
Normal file
27
crates/ra_assists/src/assist_config.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
//! Settings for tweaking assists.
|
||||
//!
|
||||
//! The fun thing here is `SnippetCap` -- this type can only be created in this
|
||||
//! module, and we use to statically check that we only produce snippet
|
||||
//! assists if we are allowed to.
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AssistConfig {
|
||||
pub snippet_cap: Option<SnippetCap>,
|
||||
}
|
||||
|
||||
impl AssistConfig {
|
||||
pub fn allow_snippets(&mut self, yes: bool) {
|
||||
self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct SnippetCap {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl Default for AssistConfig {
|
||||
fn default() -> Self {
|
||||
AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) }
|
||||
}
|
||||
}
|
|
@ -15,7 +15,10 @@ use ra_syntax::{
|
|||
};
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
|
||||
use crate::{Assist, AssistId, GroupLabel, ResolvedAssist};
|
||||
use crate::{
|
||||
assist_config::{AssistConfig, SnippetCap},
|
||||
Assist, AssistId, GroupLabel, ResolvedAssist,
|
||||
};
|
||||
|
||||
/// `AssistContext` allows to apply an assist or check if it could be applied.
|
||||
///
|
||||
|
@ -48,6 +51,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist};
|
|||
/// moment, because the LSP API is pretty awkward in this place, and it's much
|
||||
/// easier to just compute the edit eagerly :-)
|
||||
pub(crate) struct AssistContext<'a> {
|
||||
pub(crate) config: &'a AssistConfig,
|
||||
pub(crate) sema: Semantics<'a, RootDatabase>,
|
||||
pub(crate) db: &'a RootDatabase,
|
||||
pub(crate) frange: FileRange,
|
||||
|
@ -55,10 +59,14 @@ pub(crate) struct AssistContext<'a> {
|
|||
}
|
||||
|
||||
impl<'a> AssistContext<'a> {
|
||||
pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> {
|
||||
pub(crate) fn new(
|
||||
sema: Semantics<'a, RootDatabase>,
|
||||
config: &'a AssistConfig,
|
||||
frange: FileRange,
|
||||
) -> AssistContext<'a> {
|
||||
let source_file = sema.parse(frange.file_id);
|
||||
let db = sema.db;
|
||||
AssistContext { sema, db, frange, source_file }
|
||||
AssistContext { config, sema, db, frange, source_file }
|
||||
}
|
||||
|
||||
// NB, this ignores active selection.
|
||||
|
@ -163,13 +171,13 @@ impl Assists {
|
|||
|
||||
pub(crate) struct AssistBuilder {
|
||||
edit: TextEditBuilder,
|
||||
cursor_position: Option<TextSize>,
|
||||
file: FileId,
|
||||
is_snippet: bool,
|
||||
}
|
||||
|
||||
impl AssistBuilder {
|
||||
pub(crate) fn new(file: FileId) -> AssistBuilder {
|
||||
AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file }
|
||||
AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false }
|
||||
}
|
||||
|
||||
/// Remove specified `range` of text.
|
||||
|
@ -180,10 +188,30 @@ impl AssistBuilder {
|
|||
pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
|
||||
self.edit.insert(offset, text.into())
|
||||
}
|
||||
/// Append specified `snippet` at the given `offset`
|
||||
pub(crate) fn insert_snippet(
|
||||
&mut self,
|
||||
_cap: SnippetCap,
|
||||
offset: TextSize,
|
||||
snippet: impl Into<String>,
|
||||
) {
|
||||
self.is_snippet = true;
|
||||
self.insert(offset, snippet);
|
||||
}
|
||||
/// Replaces specified `range` of text with a given string.
|
||||
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
|
||||
self.edit.replace(range, replace_with.into())
|
||||
}
|
||||
/// Replaces specified `range` of text with a given `snippet`.
|
||||
pub(crate) fn replace_snippet(
|
||||
&mut self,
|
||||
_cap: SnippetCap,
|
||||
range: TextRange,
|
||||
snippet: impl Into<String>,
|
||||
) {
|
||||
self.is_snippet = true;
|
||||
self.replace(range, snippet);
|
||||
}
|
||||
pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
|
||||
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
|
||||
}
|
||||
|
@ -207,10 +235,6 @@ impl AssistBuilder {
|
|||
algo::diff(&node, &new).into_text_edit(&mut self.edit)
|
||||
}
|
||||
|
||||
/// Specify desired position of the cursor after the assist is applied.
|
||||
pub(crate) fn set_cursor(&mut self, offset: TextSize) {
|
||||
self.cursor_position = Some(offset)
|
||||
}
|
||||
// FIXME: better API
|
||||
pub(crate) fn set_file(&mut self, assist_file: FileId) {
|
||||
self.file = assist_file;
|
||||
|
@ -224,10 +248,10 @@ impl AssistBuilder {
|
|||
|
||||
fn finish(self, change_label: String) -> SourceChange {
|
||||
let edit = self.edit.finish();
|
||||
if edit.is_empty() && self.cursor_position.is_none() {
|
||||
panic!("Only call `add_assist` if the assist can be applied")
|
||||
let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file);
|
||||
if self.is_snippet {
|
||||
res.is_snippet = true;
|
||||
}
|
||||
SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position }
|
||||
.into_source_change(self.file)
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use crate::{
|
|||
// struct S;
|
||||
//
|
||||
// impl Debug for S {
|
||||
//
|
||||
// $0
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
|
@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
|||
format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name);
|
||||
|
||||
let target = attr.syntax().text_range();
|
||||
acc.add(AssistId("add_custom_impl"), label, target, |edit| {
|
||||
acc.add(AssistId("add_custom_impl"), label, target, |builder| {
|
||||
let new_attr_input = input
|
||||
.syntax()
|
||||
.descendants_with_tokens()
|
||||
|
@ -63,20 +63,11 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
|||
let has_more_derives = !new_attr_input.is_empty();
|
||||
let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
|
||||
|
||||
let mut buf = String::new();
|
||||
buf.push_str("\n\nimpl ");
|
||||
buf.push_str(trait_token.text().as_str());
|
||||
buf.push_str(" for ");
|
||||
buf.push_str(annotated_name.as_str());
|
||||
buf.push_str(" {\n");
|
||||
|
||||
let cursor_delta = if has_more_derives {
|
||||
let delta = input.syntax().text_range().len() - TextSize::of(&new_attr_input);
|
||||
edit.replace(input.syntax().text_range(), new_attr_input);
|
||||
delta
|
||||
if has_more_derives {
|
||||
builder.replace(input.syntax().text_range(), new_attr_input);
|
||||
} else {
|
||||
let attr_range = attr.syntax().text_range();
|
||||
edit.delete(attr_range);
|
||||
builder.delete(attr_range);
|
||||
|
||||
let line_break_range = attr
|
||||
.syntax()
|
||||
|
@ -84,14 +75,24 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
|||
.filter(|t| t.kind() == WHITESPACE)
|
||||
.map(|t| t.text_range())
|
||||
.unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0)));
|
||||
edit.delete(line_break_range);
|
||||
builder.delete(line_break_range);
|
||||
}
|
||||
|
||||
attr_range.len() + line_break_range.len()
|
||||
};
|
||||
|
||||
edit.set_cursor(start_offset + TextSize::of(&buf) - cursor_delta);
|
||||
buf.push_str("\n}");
|
||||
edit.insert(start_offset, buf);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
builder.insert_snippet(
|
||||
cap,
|
||||
start_offset,
|
||||
format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
builder.insert(
|
||||
start_offset,
|
||||
format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -117,7 +118,7 @@ struct Foo {
|
|||
}
|
||||
|
||||
impl Debug for Foo {
|
||||
<|>
|
||||
$0
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -139,7 +140,7 @@ pub struct Foo {
|
|||
}
|
||||
|
||||
impl Debug for Foo {
|
||||
<|>
|
||||
$0
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -158,7 +159,7 @@ struct Foo {}
|
|||
struct Foo {}
|
||||
|
||||
impl Debug for Foo {
|
||||
<|>
|
||||
$0
|
||||
}
|
||||
",
|
||||
)
|
||||
|
|
|
@ -18,31 +18,37 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// #[derive()]
|
||||
// #[derive($0)]
|
||||
// struct Point {
|
||||
// x: u32,
|
||||
// y: u32,
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let cap = ctx.config.snippet_cap?;
|
||||
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
||||
let node_start = derive_insertion_offset(&nominal)?;
|
||||
let target = nominal.syntax().text_range();
|
||||
acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| {
|
||||
acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| {
|
||||
let derive_attr = nominal
|
||||
.attrs()
|
||||
.filter_map(|x| x.as_simple_call())
|
||||
.filter(|(name, _arg)| name == "derive")
|
||||
.map(|(_name, arg)| arg)
|
||||
.next();
|
||||
let offset = match derive_attr {
|
||||
match derive_attr {
|
||||
None => {
|
||||
edit.insert(node_start, "#[derive()]\n");
|
||||
node_start + TextSize::of("#[derive(")
|
||||
builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
|
||||
}
|
||||
Some(tt) => {
|
||||
// Just move the cursor.
|
||||
builder.insert_snippet(
|
||||
cap,
|
||||
tt.syntax().text_range().end() - TextSize::of(')'),
|
||||
"$0",
|
||||
)
|
||||
}
|
||||
Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'),
|
||||
};
|
||||
edit.set_cursor(offset)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -66,12 +72,12 @@ mod tests {
|
|||
check_assist(
|
||||
add_derive,
|
||||
"struct Foo { a: i32, <|>}",
|
||||
"#[derive(<|>)]\nstruct Foo { a: i32, }",
|
||||
"#[derive($0)]\nstruct Foo { a: i32, }",
|
||||
);
|
||||
check_assist(
|
||||
add_derive,
|
||||
"struct Foo { <|> a: i32, }",
|
||||
"#[derive(<|>)]\nstruct Foo { a: i32, }",
|
||||
"#[derive($0)]\nstruct Foo { a: i32, }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -80,7 +86,7 @@ mod tests {
|
|||
check_assist(
|
||||
add_derive,
|
||||
"#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
|
||||
"#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
|
||||
"#[derive(Clone$0)]\nstruct Foo { a: i32, }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -96,7 +102,7 @@ struct Foo { a: i32<|>, }
|
|||
"
|
||||
/// `Foo` is a pretty important struct.
|
||||
/// It does stuff.
|
||||
#[derive(<|>)]
|
||||
#[derive($0)]
|
||||
struct Foo { a: i32, }
|
||||
",
|
||||
);
|
||||
|
|
|
@ -25,9 +25,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
|
|||
let stmt = ctx.find_node_at_offset::<LetStmt>()?;
|
||||
let module = ctx.sema.scope(stmt.syntax()).module()?;
|
||||
let expr = stmt.initializer()?;
|
||||
let pat = stmt.pat()?;
|
||||
// Must be a binding
|
||||
let pat = match pat {
|
||||
let pat = match stmt.pat()? {
|
||||
ast::Pat::BindPat(bind_pat) => bind_pat,
|
||||
_ => return None,
|
||||
};
|
||||
|
@ -46,7 +45,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
|
|||
// Assist not applicable if the type has already been specified
|
||||
// and it has no placeholders
|
||||
let ascribed_ty = stmt.ascribed_type();
|
||||
if let Some(ref ty) = ascribed_ty {
|
||||
if let Some(ty) = &ascribed_ty {
|
||||
if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
@ -87,11 +86,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn add_explicit_type_works_for_simple_expr() {
|
||||
check_assist(
|
||||
add_explicit_type,
|
||||
"fn f() { let a<|> = 1; }",
|
||||
"fn f() { let a<|>: i32 = 1; }",
|
||||
);
|
||||
check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -99,7 +94,7 @@ mod tests {
|
|||
check_assist(
|
||||
add_explicit_type,
|
||||
"fn f() { let a<|>: _ = 1; }",
|
||||
"fn f() { let a<|>: i32 = 1; }",
|
||||
"fn f() { let a: i32 = 1; }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -123,7 +118,7 @@ mod tests {
|
|||
}
|
||||
|
||||
fn f() {
|
||||
let a<|>: Option<i32> = Option::Some(1);
|
||||
let a: Option<i32> = Option::Some(1);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
@ -133,7 +128,7 @@ mod tests {
|
|||
check_assist(
|
||||
add_explicit_type,
|
||||
r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
|
||||
r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
|
||||
r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -141,8 +136,8 @@ mod tests {
|
|||
fn add_explicit_type_works_for_macro_call_recursive() {
|
||||
check_assist(
|
||||
add_explicit_type,
|
||||
"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
|
||||
"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",
|
||||
r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#,
|
||||
r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -209,7 +204,7 @@ struct Test<K, T = u8> {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let test<|>: Test<i32> = Test { t: 23, k: 33 };
|
||||
let test: Test<i32> = Test { t: 23, k: 33 };
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner};
|
||||
use stdx::format_to;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
|
||||
|
||||
|
@ -35,12 +34,12 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
|
|||
}
|
||||
let field_type = field_list.fields().next()?.type_ref()?;
|
||||
let path = match field_type {
|
||||
ast::TypeRef::PathType(p) => p,
|
||||
ast::TypeRef::PathType(it) => it,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if existing_from_impl(&ctx.sema, &variant).is_some() {
|
||||
tested_by!(test_add_from_impl_already_exists);
|
||||
mark::hit!(test_add_from_impl_already_exists);
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -51,9 +50,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
|
|||
target,
|
||||
|edit| {
|
||||
let start_offset = variant.parent_enum().syntax().text_range().end();
|
||||
let mut buf = String::new();
|
||||
format_to!(
|
||||
buf,
|
||||
let buf = format!(
|
||||
r#"
|
||||
|
||||
impl From<{0}> for {1} {{
|
||||
|
@ -93,7 +90,7 @@ fn existing_from_impl(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
|
@ -104,7 +101,7 @@ mod tests {
|
|||
check_assist(
|
||||
add_from_impl_for_enum,
|
||||
"enum A { <|>One(u32) }",
|
||||
r#"enum A { <|>One(u32) }
|
||||
r#"enum A { One(u32) }
|
||||
|
||||
impl From<u32> for A {
|
||||
fn from(v: u32) -> Self {
|
||||
|
@ -119,7 +116,7 @@ impl From<u32> for A {
|
|||
check_assist(
|
||||
add_from_impl_for_enum,
|
||||
r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
|
||||
r#"enum A { <|>One(foo::bar::baz::Boo) }
|
||||
r#"enum A { One(foo::bar::baz::Boo) }
|
||||
|
||||
impl From<foo::bar::baz::Boo> for A {
|
||||
fn from(v: foo::bar::baz::Boo) -> Self {
|
||||
|
@ -152,7 +149,7 @@ impl From<foo::bar::baz::Boo> for A {
|
|||
|
||||
#[test]
|
||||
fn test_add_from_impl_already_exists() {
|
||||
covers!(test_add_from_impl_already_exists);
|
||||
mark::check!(test_add_from_impl_already_exists);
|
||||
check_not_applicable(
|
||||
r#"
|
||||
enum A { <|>One(u32), }
|
||||
|
@ -181,7 +178,7 @@ impl From<String> for A {
|
|||
pub trait From<T> {
|
||||
fn from(T) -> Self;
|
||||
}"#,
|
||||
r#"enum A { <|>One(u32), Two(String), }
|
||||
r#"enum A { One(u32), Two(String), }
|
||||
|
||||
impl From<u32> for A {
|
||||
fn from(v: u32) -> Self {
|
||||
|
|
|
@ -4,13 +4,17 @@ use ra_syntax::{
|
|||
ast::{
|
||||
self,
|
||||
edit::{AstNodeEdit, IndentLevel},
|
||||
ArgListOwner, AstNode, ModuleItemOwner,
|
||||
make, ArgListOwner, AstNode, ModuleItemOwner,
|
||||
},
|
||||
SyntaxKind, SyntaxNode, TextSize,
|
||||
};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{
|
||||
assist_config::SnippetCap,
|
||||
utils::{render_snippet, Cursor},
|
||||
AssistContext, AssistId, Assists,
|
||||
};
|
||||
|
||||
// Assist: add_function
|
||||
//
|
||||
|
@ -33,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
// }
|
||||
//
|
||||
// fn bar(arg: &str, baz: Baz) {
|
||||
// todo!()
|
||||
// ${0:todo!()}
|
||||
// }
|
||||
//
|
||||
// ```
|
||||
|
@ -58,21 +62,40 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
|
||||
|
||||
let target = call.syntax().text_range();
|
||||
acc.add(AssistId("add_function"), "Add function", target, |edit| {
|
||||
acc.add(AssistId("add_function"), "Add function", target, |builder| {
|
||||
let function_template = function_builder.render();
|
||||
edit.set_file(function_template.file);
|
||||
edit.set_cursor(function_template.cursor_offset);
|
||||
edit.insert(function_template.insert_offset, function_template.fn_def.to_string());
|
||||
builder.set_file(function_template.file);
|
||||
let new_fn = function_template.to_string(ctx.config.snippet_cap);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
|
||||
None => builder.insert(function_template.insert_offset, new_fn),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct FunctionTemplate {
|
||||
insert_offset: TextSize,
|
||||
cursor_offset: TextSize,
|
||||
fn_def: ast::SourceFile,
|
||||
placeholder_expr: ast::MacroCall,
|
||||
leading_ws: String,
|
||||
fn_def: ast::FnDef,
|
||||
trailing_ws: String,
|
||||
file: FileId,
|
||||
}
|
||||
|
||||
impl FunctionTemplate {
|
||||
fn to_string(&self, cap: Option<SnippetCap>) -> String {
|
||||
let f = match cap {
|
||||
Some(cap) => render_snippet(
|
||||
cap,
|
||||
self.fn_def.syntax(),
|
||||
Cursor::Replace(self.placeholder_expr.syntax()),
|
||||
),
|
||||
None => self.fn_def.to_string(),
|
||||
};
|
||||
format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
|
||||
}
|
||||
}
|
||||
|
||||
struct FunctionBuilder {
|
||||
target: GeneratedFunctionTarget,
|
||||
fn_name: ast::Name,
|
||||
|
@ -110,35 +133,41 @@ impl FunctionBuilder {
|
|||
}
|
||||
|
||||
fn render(self) -> FunctionTemplate {
|
||||
let placeholder_expr = ast::make::expr_todo();
|
||||
let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr));
|
||||
let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body);
|
||||
if self.needs_pub {
|
||||
fn_def = ast::make::add_pub_crate_modifier(fn_def);
|
||||
}
|
||||
let placeholder_expr = make::expr_todo();
|
||||
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
|
||||
let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
|
||||
let mut fn_def =
|
||||
make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body);
|
||||
let leading_ws;
|
||||
let trailing_ws;
|
||||
|
||||
let (fn_def, insert_offset) = match self.target {
|
||||
let insert_offset = match self.target {
|
||||
GeneratedFunctionTarget::BehindItem(it) => {
|
||||
let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def);
|
||||
let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it));
|
||||
(indented, it.text_range().end())
|
||||
let indent = IndentLevel::from_node(&it);
|
||||
leading_ws = format!("\n\n{}", indent);
|
||||
fn_def = fn_def.indent(indent);
|
||||
trailing_ws = String::new();
|
||||
it.text_range().end()
|
||||
}
|
||||
GeneratedFunctionTarget::InEmptyItemList(it) => {
|
||||
let indent_once = IndentLevel(1);
|
||||
let indent = IndentLevel::from_node(it.syntax());
|
||||
let fn_def = ast::make::add_leading_newlines(1, fn_def);
|
||||
let fn_def = fn_def.indent(indent_once);
|
||||
let fn_def = ast::make::add_trailing_newlines(1, fn_def);
|
||||
let fn_def = fn_def.indent(indent);
|
||||
(fn_def, it.syntax().text_range().start() + TextSize::of('{'))
|
||||
leading_ws = format!("\n{}", indent + 1);
|
||||
fn_def = fn_def.indent(indent + 1);
|
||||
trailing_ws = format!("\n{}", indent);
|
||||
it.syntax().text_range().start() + TextSize::of('{')
|
||||
}
|
||||
};
|
||||
|
||||
let placeholder_expr =
|
||||
fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||
let cursor_offset_from_fn_start = placeholder_expr.syntax().text_range().start();
|
||||
let cursor_offset = insert_offset + cursor_offset_from_fn_start;
|
||||
FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }
|
||||
FunctionTemplate {
|
||||
insert_offset,
|
||||
placeholder_expr,
|
||||
leading_ws,
|
||||
fn_def,
|
||||
trailing_ws,
|
||||
file: self.file,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +187,7 @@ impl GeneratedFunctionTarget {
|
|||
|
||||
fn fn_name(call: &ast::Path) -> Option<ast::Name> {
|
||||
let name = call.segment()?.syntax().to_string();
|
||||
Some(ast::make::name(&name))
|
||||
Some(make::name(&name))
|
||||
}
|
||||
|
||||
/// Computes the type variables and arguments required for the generated function
|
||||
|
@ -180,8 +209,8 @@ fn fn_args(
|
|||
});
|
||||
}
|
||||
deduplicate_arg_names(&mut arg_names);
|
||||
let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty));
|
||||
Some((None, ast::make::param_list(params)))
|
||||
let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty));
|
||||
Some((None, make::param_list(params)))
|
||||
}
|
||||
|
||||
/// Makes duplicate argument names unique by appending incrementing numbers.
|
||||
|
@ -316,7 +345,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -343,7 +372,7 @@ impl Foo {
|
|||
}
|
||||
|
||||
fn bar() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -367,7 +396,7 @@ fn foo1() {
|
|||
}
|
||||
|
||||
fn bar() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
|
||||
fn foo2() {}
|
||||
|
@ -393,7 +422,7 @@ mod baz {
|
|||
}
|
||||
|
||||
fn bar() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
}
|
||||
",
|
||||
|
@ -419,7 +448,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(baz: Baz) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -452,7 +481,7 @@ impl Baz {
|
|||
}
|
||||
|
||||
fn bar(baz: Baz) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -473,7 +502,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(arg: &str) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
|
@ -494,7 +523,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(arg: char) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
|
@ -515,7 +544,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(arg: i32) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -536,7 +565,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(arg: u8) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -561,7 +590,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(x: u8) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -584,7 +613,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(worble: ()) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -613,7 +642,7 @@ fn baz() {
|
|||
}
|
||||
|
||||
fn bar(foo: impl Foo) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -640,7 +669,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(baz: &Baz) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -669,7 +698,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(baz: Baz::Bof) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -692,7 +721,7 @@ fn foo<T>(t: T) {
|
|||
}
|
||||
|
||||
fn bar<T>(t: T) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -723,7 +752,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(arg: fn() -> Baz) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -748,7 +777,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(closure: impl Fn(i64) -> i64) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -769,7 +798,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(baz: ()) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -794,7 +823,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(baz_1: Baz, baz_2: Baz) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -819,7 +848,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
|
@ -839,7 +868,7 @@ fn foo() {
|
|||
r"
|
||||
mod bar {
|
||||
pub(crate) fn my_fn() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -878,7 +907,7 @@ fn bar() {
|
|||
}
|
||||
|
||||
fn baz(foo: foo::Foo) {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
",
|
||||
)
|
||||
|
@ -902,7 +931,7 @@ mod bar {
|
|||
fn something_else() {}
|
||||
|
||||
pub(crate) fn my_fn() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -930,7 +959,7 @@ fn foo() {
|
|||
mod bar {
|
||||
mod baz {
|
||||
pub(crate) fn my_fn() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -959,7 +988,7 @@ fn main() {
|
|||
|
||||
|
||||
pub(crate) fn bar() {
|
||||
<|>todo!()
|
||||
${0:todo!()}
|
||||
}",
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
use ra_syntax::{
|
||||
ast::{self, AstNode, NameOwner, TypeParamsOwner},
|
||||
TextSize,
|
||||
};
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
|
||||
use stdx::{format_to, SepBy};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
@ -12,17 +9,17 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
//
|
||||
// ```
|
||||
// struct Ctx<T: Clone> {
|
||||
// data: T,<|>
|
||||
// data: T,<|>
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Ctx<T: Clone> {
|
||||
// data: T,
|
||||
// data: T,
|
||||
// }
|
||||
//
|
||||
// impl<T: Clone> Ctx<T> {
|
||||
//
|
||||
// $0
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
|
@ -50,30 +47,37 @@ pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
let generic_params = lifetime_params.chain(type_params).sep_by(", ");
|
||||
format_to!(buf, "<{}>", generic_params)
|
||||
}
|
||||
buf.push_str(" {\n");
|
||||
edit.set_cursor(start_offset + TextSize::of(&buf));
|
||||
buf.push_str("\n}");
|
||||
edit.insert(start_offset, buf);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
buf.push_str(" {\n $0\n}");
|
||||
edit.insert_snippet(cap, start_offset, buf);
|
||||
}
|
||||
None => {
|
||||
buf.push_str(" {\n}");
|
||||
edit.insert(start_offset, buf);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::tests::{check_assist, check_assist_target};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_add_impl() {
|
||||
check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n<|>\n}\n");
|
||||
check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n");
|
||||
check_assist(
|
||||
add_impl,
|
||||
"struct Foo<T: Clone> {<|>}",
|
||||
"struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
|
||||
"struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
|
||||
);
|
||||
check_assist(
|
||||
add_impl,
|
||||
"struct Foo<'a, T: Foo<'a>> {<|>}",
|
||||
"struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}",
|
||||
"struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use ra_syntax::{
|
|||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
|
||||
utils::{get_missing_assoc_items, resolve_target_trait},
|
||||
utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
|
||||
AssistId,
|
||||
};
|
||||
|
||||
|
@ -46,7 +46,7 @@ enum AddMissingImplMembersMode {
|
|||
//
|
||||
// impl Trait<u32> for () {
|
||||
// fn foo(&self) -> u32 {
|
||||
// todo!()
|
||||
// ${0:todo!()}
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
@ -89,7 +89,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
|
|||
// impl Trait for () {
|
||||
// Type X = ();
|
||||
// fn foo(&self) {}
|
||||
// fn bar(&self) {}
|
||||
// $0fn bar(&self) {}
|
||||
//
|
||||
// }
|
||||
// ```
|
||||
|
@ -147,7 +147,7 @@ fn add_missing_impl_members_inner(
|
|||
}
|
||||
|
||||
let target = impl_def.syntax().text_range();
|
||||
acc.add(AssistId(assist_id), label, target, |edit| {
|
||||
acc.add(AssistId(assist_id), label, target, |builder| {
|
||||
let n_existing_items = impl_item_list.assoc_items().count();
|
||||
let source_scope = ctx.sema.scope_for_def(trait_);
|
||||
let target_scope = ctx.sema.scope(impl_item_list.syntax());
|
||||
|
@ -162,13 +162,29 @@ fn add_missing_impl_members_inner(
|
|||
})
|
||||
.map(|it| edit::remove_attrs_and_docs(&it));
|
||||
let new_impl_item_list = impl_item_list.append_items(items);
|
||||
let cursor_position = {
|
||||
let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
|
||||
first_new_item.syntax().text_range().start()
|
||||
};
|
||||
let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
|
||||
|
||||
edit.replace_ast(impl_item_list, new_impl_item_list);
|
||||
edit.set_cursor(cursor_position);
|
||||
let original_range = impl_item_list.syntax().text_range();
|
||||
match ctx.config.snippet_cap {
|
||||
None => builder.replace(original_range, new_impl_item_list.to_string()),
|
||||
Some(cap) => {
|
||||
let mut cursor = Cursor::Before(first_new_item.syntax());
|
||||
let placeholder;
|
||||
if let ast::AssocItem::FnDef(func) = &first_new_item {
|
||||
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) {
|
||||
if m.syntax().text() == "todo!()" {
|
||||
placeholder = m;
|
||||
cursor = Cursor::Replace(placeholder.syntax());
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.replace_snippet(
|
||||
cap,
|
||||
original_range,
|
||||
render_snippet(cap, new_impl_item_list.syntax(), cursor),
|
||||
)
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -222,7 +238,7 @@ struct S;
|
|||
|
||||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
<|>type Output;
|
||||
$0type Output;
|
||||
const CONST: usize = 42;
|
||||
fn foo(&self) {
|
||||
todo!()
|
||||
|
@ -263,8 +279,8 @@ struct S;
|
|||
|
||||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
<|>fn foo(&self) {
|
||||
todo!()
|
||||
fn foo(&self) {
|
||||
${0:todo!()}
|
||||
}
|
||||
|
||||
}"#,
|
||||
|
@ -283,8 +299,8 @@ impl Foo for S { <|> }"#,
|
|||
trait Foo { fn foo(&self); }
|
||||
struct S;
|
||||
impl Foo for S {
|
||||
<|>fn foo(&self) {
|
||||
todo!()
|
||||
fn foo(&self) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -302,8 +318,8 @@ impl Foo<u32> for S { <|> }"#,
|
|||
trait Foo<T> { fn foo(&self, t: T) -> &T; }
|
||||
struct S;
|
||||
impl Foo<u32> for S {
|
||||
<|>fn foo(&self, t: u32) -> &u32 {
|
||||
todo!()
|
||||
fn foo(&self, t: u32) -> &u32 {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -321,8 +337,8 @@ impl<U> Foo<U> for S { <|> }"#,
|
|||
trait Foo<T> { fn foo(&self, t: T) -> &T; }
|
||||
struct S;
|
||||
impl<U> Foo<U> for S {
|
||||
<|>fn foo(&self, t: U) -> &U {
|
||||
todo!()
|
||||
fn foo(&self, t: U) -> &U {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -340,8 +356,8 @@ impl Foo for S {}<|>"#,
|
|||
trait Foo { fn foo(&self); }
|
||||
struct S;
|
||||
impl Foo for S {
|
||||
<|>fn foo(&self) {
|
||||
todo!()
|
||||
fn foo(&self) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
|
@ -365,8 +381,8 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar) {
|
||||
todo!()
|
||||
fn foo(&self, bar: foo::Bar) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -390,8 +406,8 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>) {
|
||||
todo!()
|
||||
fn foo(&self, bar: foo::Bar<u32>) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -415,8 +431,8 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo<u32> for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>) {
|
||||
todo!()
|
||||
fn foo(&self, bar: foo::Bar<u32>) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -443,8 +459,8 @@ mod foo {
|
|||
struct Param;
|
||||
struct S;
|
||||
impl foo::Foo<Param> for S {
|
||||
<|>fn foo(&self, bar: Param) {
|
||||
todo!()
|
||||
fn foo(&self, bar: Param) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -470,8 +486,8 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<u32>::Assoc) {
|
||||
todo!()
|
||||
fn foo(&self, bar: foo::Bar<u32>::Assoc) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -497,8 +513,8 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: foo::Bar<foo::Baz>) {
|
||||
todo!()
|
||||
fn foo(&self, bar: foo::Bar<foo::Baz>) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -522,8 +538,8 @@ mod foo {
|
|||
}
|
||||
struct S;
|
||||
impl foo::Foo for S {
|
||||
<|>fn foo(&self, bar: dyn Fn(u32) -> i32) {
|
||||
todo!()
|
||||
fn foo(&self, bar: dyn Fn(u32) -> i32) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
@ -580,7 +596,7 @@ trait Foo {
|
|||
}
|
||||
struct S;
|
||||
impl Foo for S {
|
||||
<|>type Output;
|
||||
$0type Output;
|
||||
fn foo(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
@ -614,7 +630,7 @@ trait Foo {
|
|||
}
|
||||
struct S;
|
||||
impl Foo for S {
|
||||
<|>fn valid(some: u32) -> bool { false }
|
||||
$0fn valid(some: u32) -> bool { false }
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
@ -637,8 +653,8 @@ trait Foo<T = Self> {
|
|||
|
||||
struct S;
|
||||
impl Foo for S {
|
||||
<|>fn bar(&self, other: &Self) {
|
||||
todo!()
|
||||
fn bar(&self, other: &Self) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
|
@ -662,8 +678,8 @@ trait Foo<T1, T2 = Self> {
|
|||
|
||||
struct S<T>;
|
||||
impl Foo<T> for S<T> {
|
||||
<|>fn bar(&self, this: &T, that: &Self) {
|
||||
todo!()
|
||||
fn bar(&self, this: &T, that: &Self) {
|
||||
${0:todo!()}
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ use ra_syntax::{
|
|||
ast::{
|
||||
self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
|
||||
},
|
||||
TextSize, T,
|
||||
T,
|
||||
};
|
||||
use stdx::{format_to, SepBy};
|
||||
|
||||
|
@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
// }
|
||||
//
|
||||
// impl<T: Clone> Ctx<T> {
|
||||
// fn new(data: T) -> Self { Self { data } }
|
||||
// fn $0new(data: T) -> Self { Self { data } }
|
||||
// }
|
||||
//
|
||||
// ```
|
||||
|
@ -42,31 +42,26 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
let impl_def = find_struct_impl(&ctx, &strukt)?;
|
||||
|
||||
let target = strukt.syntax().text_range();
|
||||
acc.add(AssistId("add_new"), "Add default constructor", target, |edit| {
|
||||
acc.add(AssistId("add_new"), "Add default constructor", target, |builder| {
|
||||
let mut buf = String::with_capacity(512);
|
||||
|
||||
if impl_def.is_some() {
|
||||
buf.push('\n');
|
||||
}
|
||||
|
||||
let vis = strukt.visibility().map(|v| format!("{} ", v));
|
||||
let vis = vis.as_deref().unwrap_or("");
|
||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
|
||||
|
||||
let params = field_list
|
||||
.fields()
|
||||
.filter_map(|f| {
|
||||
Some(format!(
|
||||
"{}: {}",
|
||||
f.name()?.syntax().text(),
|
||||
f.ascribed_type()?.syntax().text()
|
||||
))
|
||||
Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
|
||||
})
|
||||
.sep_by(", ");
|
||||
let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
|
||||
|
||||
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
|
||||
|
||||
let (start_offset, end_offset) = impl_def
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| {
|
||||
buf.push('\n');
|
||||
let start = impl_def
|
||||
|
@ -76,17 +71,20 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
.text_range()
|
||||
.end();
|
||||
|
||||
Some((start, TextSize::of("\n")))
|
||||
Some(start)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&strukt, &buf);
|
||||
let start = strukt.syntax().text_range().end();
|
||||
|
||||
(start, TextSize::of("\n}\n"))
|
||||
strukt.syntax().text_range().end()
|
||||
});
|
||||
|
||||
edit.set_cursor(start_offset + TextSize::of(&buf) - end_offset);
|
||||
edit.insert(start_offset, buf);
|
||||
match ctx.config.snippet_cap {
|
||||
None => builder.insert(start_offset, buf),
|
||||
Some(cap) => {
|
||||
buf = buf.replace("fn new", "fn $0new");
|
||||
builder.insert_snippet(cap, start_offset, buf);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -191,7 +189,7 @@ mod tests {
|
|||
"struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Self { Self { } }<|>
|
||||
fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -201,7 +199,7 @@ impl Foo {
|
|||
"struct Foo<T: Clone> {}
|
||||
|
||||
impl<T: Clone> Foo<T> {
|
||||
fn new() -> Self { Self { } }<|>
|
||||
fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -211,7 +209,7 @@ impl<T: Clone> Foo<T> {
|
|||
"struct Foo<'a, T: Foo<'a>> {}
|
||||
|
||||
impl<'a, T: Foo<'a>> Foo<'a, T> {
|
||||
fn new() -> Self { Self { } }<|>
|
||||
fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -221,7 +219,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
|
|||
"struct Foo { baz: String }
|
||||
|
||||
impl Foo {
|
||||
fn new(baz: String) -> Self { Self { baz } }<|>
|
||||
fn $0new(baz: String) -> Self { Self { baz } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -231,7 +229,7 @@ impl Foo {
|
|||
"struct Foo { baz: String, qux: Vec<i32> }
|
||||
|
||||
impl Foo {
|
||||
fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|>
|
||||
fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -243,7 +241,7 @@ impl Foo {
|
|||
"struct Foo { pub baz: String, pub qux: Vec<i32> }
|
||||
|
||||
impl Foo {
|
||||
fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|>
|
||||
fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -258,7 +256,7 @@ impl Foo {}
|
|||
"struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Self { Self { } }<|>
|
||||
fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -273,7 +271,7 @@ impl Foo {
|
|||
"struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Self { Self { } }<|>
|
||||
fn $0new() -> Self { Self { } }
|
||||
|
||||
fn qux(&self) {}
|
||||
}
|
||||
|
@ -294,7 +292,7 @@ impl Foo {
|
|||
"struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
fn new() -> Self { Self { } }<|>
|
||||
fn $0new() -> Self { Self { } }
|
||||
|
||||
fn qux(&self) {}
|
||||
fn baz() -> i32 {
|
||||
|
@ -311,7 +309,7 @@ impl Foo {
|
|||
"pub struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub fn new() -> Self { Self { } }<|>
|
||||
pub fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -321,7 +319,7 @@ impl Foo {
|
|||
"pub(crate) struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub(crate) fn new() -> Self { Self { } }<|>
|
||||
pub(crate) fn $0new() -> Self { Self { } }
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -414,7 +412,7 @@ pub struct Source<T> {
|
|||
}
|
||||
|
||||
impl<T> Source<T> {
|
||||
pub fn new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }<|>
|
||||
pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
|
||||
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||
|
|
134
crates/ra_assists/src/handlers/add_turbo_fish.rs
Normal file
134
crates/ra_assists/src/handlers/add_turbo_fish.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass};
|
||||
use ra_syntax::{ast, AstNode, SyntaxKind, T};
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
AssistId,
|
||||
};
|
||||
|
||||
// Assist: add_turbo_fish
|
||||
//
|
||||
// Adds `::<_>` to a call of a generic method or function.
|
||||
//
|
||||
// ```
|
||||
// fn make<T>() -> T { todo!() }
|
||||
// fn main() {
|
||||
// let x = make<|>();
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn make<T>() -> T { todo!() }
|
||||
// fn main() {
|
||||
// let x = make::<${0:_}>();
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?;
|
||||
let next_token = ident.next_token()?;
|
||||
if next_token.kind() == T![::] {
|
||||
mark::hit!(add_turbo_fish_one_fish_is_enough);
|
||||
return None;
|
||||
}
|
||||
let name_ref = ast::NameRef::cast(ident.parent())?;
|
||||
let def = match classify_name_ref(&ctx.sema, &name_ref)? {
|
||||
NameRefClass::Definition(def) => def,
|
||||
NameRefClass::FieldShorthand { .. } => return None,
|
||||
};
|
||||
let fun = match def {
|
||||
Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
|
||||
if generics.is_empty() {
|
||||
mark::hit!(add_turbo_fish_non_generic);
|
||||
return None;
|
||||
}
|
||||
acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| {
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
|
||||
None => builder.insert(ident.text_range().end(), "::<_>"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
use test_utils::mark;
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_function() {
|
||||
check_assist(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
fn make<T>() -> T {}
|
||||
fn main() {
|
||||
make<|>();
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn make<T>() -> T {}
|
||||
fn main() {
|
||||
make::<${0:_}>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_method() {
|
||||
check_assist(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
struct S;
|
||||
impl S {
|
||||
fn make<T>(&self) -> T {}
|
||||
}
|
||||
fn main() {
|
||||
S.make<|>();
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct S;
|
||||
impl S {
|
||||
fn make<T>(&self) -> T {}
|
||||
}
|
||||
fn main() {
|
||||
S.make::<${0:_}>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_one_fish_is_enough() {
|
||||
mark::check!(add_turbo_fish_one_fish_is_enough);
|
||||
check_assist_not_applicable(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
fn make<T>() -> T {}
|
||||
fn main() {
|
||||
make<|>::<()>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_non_generic() {
|
||||
mark::check!(add_turbo_fish_non_generic);
|
||||
check_assist_not_applicable(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
fn make() -> () {}
|
||||
fn main() {
|
||||
make<|>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -63,22 +63,22 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn demorgan_turns_and_into_or() {
|
||||
check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }")
|
||||
check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x || x) }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demorgan_turns_or_into_and() {
|
||||
check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }")
|
||||
check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x && x) }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demorgan_removes_inequality() {
|
||||
check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }")
|
||||
check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x && x) }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demorgan_general_case() {
|
||||
check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }")
|
||||
check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x && !x) }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -50,7 +50,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
format!("Import `{}`", &import),
|
||||
range,
|
||||
|builder| {
|
||||
insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder);
|
||||
insert_use_statement(
|
||||
&auto_import_assets.syntax_under_caret,
|
||||
&import,
|
||||
ctx,
|
||||
builder.text_edit_builder(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -293,7 +298,7 @@ mod tests {
|
|||
}
|
||||
",
|
||||
r"
|
||||
<|>use PubMod::PubStruct;
|
||||
use PubMod::PubStruct;
|
||||
|
||||
PubStruct
|
||||
|
||||
|
@ -324,7 +329,7 @@ mod tests {
|
|||
macro_rules! foo {
|
||||
($i:ident) => { fn foo(a: $i) {} }
|
||||
}
|
||||
foo!(Pub<|>Struct);
|
||||
foo!(PubStruct);
|
||||
|
||||
pub mod PubMod {
|
||||
pub struct PubStruct;
|
||||
|
@ -355,7 +360,7 @@ mod tests {
|
|||
use PubMod::{PubStruct2, PubStruct1};
|
||||
|
||||
struct Test {
|
||||
test: Pub<|>Struct2<u8>,
|
||||
test: PubStruct2<u8>,
|
||||
}
|
||||
|
||||
pub mod PubMod {
|
||||
|
@ -388,7 +393,7 @@ mod tests {
|
|||
r"
|
||||
use PubMod3::PubStruct;
|
||||
|
||||
PubSt<|>ruct
|
||||
PubStruct
|
||||
|
||||
pub mod PubMod1 {
|
||||
pub struct PubStruct;
|
||||
|
@ -469,7 +474,7 @@ mod tests {
|
|||
r"
|
||||
use PubMod::test_function;
|
||||
|
||||
test_function<|>
|
||||
test_function
|
||||
|
||||
pub mod PubMod {
|
||||
pub fn test_function() {};
|
||||
|
@ -496,7 +501,7 @@ mod tests {
|
|||
r"use crate_with_macro::foo;
|
||||
|
||||
fn main() {
|
||||
foo<|>
|
||||
foo
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -582,7 +587,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
TestStruct::test_function<|>
|
||||
TestStruct::test_function
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -615,7 +620,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
TestStruct::TEST_CONST<|>
|
||||
TestStruct::TEST_CONST
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -654,7 +659,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
test_mod::TestStruct::test_function<|>
|
||||
test_mod::TestStruct::test_function
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -725,7 +730,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
test_mod::TestStruct::TEST_CONST<|>
|
||||
test_mod::TestStruct::TEST_CONST
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -798,7 +803,7 @@ fn main() {
|
|||
|
||||
fn main() {
|
||||
let test_struct = test_mod::TestStruct {};
|
||||
test_struct.test_meth<|>od()
|
||||
test_struct.test_method()
|
||||
}
|
||||
",
|
||||
);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use ra_syntax::{
|
||||
ast::{self, BlockExpr, Expr, LoopBodyOwner},
|
||||
AstNode,
|
||||
SyntaxKind::{COMMENT, WHITESPACE},
|
||||
SyntaxNode, TextSize,
|
||||
AstNode, SyntaxNode,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
@ -16,39 +14,40 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn foo() -> Result<i32, > { Ok(42i32) }
|
||||
// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
|
||||
// ```
|
||||
pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let fn_def = ctx.find_node_at_offset::<ast::FnDef>();
|
||||
let fn_def = &mut fn_def?;
|
||||
let ret_type = &fn_def.ret_type()?.type_ref()?;
|
||||
if ret_type.syntax().text().to_string().starts_with("Result<") {
|
||||
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
|
||||
// FIXME: extend to lambdas as well
|
||||
let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?;
|
||||
|
||||
let type_ref = &ret_type.type_ref()?;
|
||||
if type_ref.syntax().text().to_string().starts_with("Result<") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let block_expr = &fn_def.body()?;
|
||||
let cursor_in_ret_type =
|
||||
fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range);
|
||||
if !cursor_in_ret_type {
|
||||
return None;
|
||||
}
|
||||
|
||||
acc.add(
|
||||
AssistId("change_return_type_to_result"),
|
||||
"Change return type to Result",
|
||||
ret_type.syntax().text_range(),
|
||||
|edit| {
|
||||
type_ref.syntax().text_range(),
|
||||
|builder| {
|
||||
let mut tail_return_expr_collector = TailReturnCollector::new();
|
||||
tail_return_expr_collector.collect_jump_exprs(block_expr, false);
|
||||
tail_return_expr_collector.collect_tail_exprs(block_expr);
|
||||
|
||||
for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
|
||||
edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
|
||||
builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
|
||||
}
|
||||
edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type));
|
||||
|
||||
if let Some(node_start) = result_insertion_offset(&ret_type) {
|
||||
edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type)));
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
|
||||
builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
|
||||
}
|
||||
None => builder
|
||||
.replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -250,17 +249,8 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
|
|||
}
|
||||
}
|
||||
|
||||
fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option<TextSize> {
|
||||
let non_ws_child = ret_type
|
||||
.syntax()
|
||||
.children_with_tokens()
|
||||
.find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
|
||||
Some(non_ws_child.text_range().start())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
@ -273,7 +263,7 @@ mod tests {
|
|||
let test = "test";
|
||||
return 42i32;
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
return Ok(42i32);
|
||||
}"#,
|
||||
|
@ -288,7 +278,7 @@ mod tests {
|
|||
let test = "test";
|
||||
return 42i32;
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
return Ok(42i32);
|
||||
}"#,
|
||||
|
@ -314,7 +304,7 @@ mod tests {
|
|||
let test = "test";
|
||||
return 42i32;
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
return Ok(42i32);
|
||||
}"#,
|
||||
|
@ -329,7 +319,7 @@ mod tests {
|
|||
let test = "test";
|
||||
42i32
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
Ok(42i32)
|
||||
}"#,
|
||||
|
@ -343,7 +333,7 @@ mod tests {
|
|||
r#"fn foo() -> i32<|> {
|
||||
42i32
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
Ok(42i32)
|
||||
}"#,
|
||||
);
|
||||
|
@ -359,7 +349,7 @@ mod tests {
|
|||
24i32
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
if true {
|
||||
Ok(42i32)
|
||||
} else {
|
||||
|
@ -384,7 +374,7 @@ mod tests {
|
|||
24i32
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
if true {
|
||||
if false {
|
||||
Ok(1)
|
||||
|
@ -413,7 +403,7 @@ mod tests {
|
|||
24i32.await
|
||||
}
|
||||
}"#,
|
||||
r#"async fn foo() -> Result<i32, <|>> {
|
||||
r#"async fn foo() -> Result<i32, ${0:_}> {
|
||||
if true {
|
||||
if false {
|
||||
Ok(1.await)
|
||||
|
@ -434,7 +424,7 @@ mod tests {
|
|||
r#"fn foo() -> [i32;<|> 3] {
|
||||
[1, 2, 3]
|
||||
}"#,
|
||||
r#"fn foo() -> Result<[i32; 3], <|>> {
|
||||
r#"fn foo() -> Result<[i32; 3], ${0:_}> {
|
||||
Ok([1, 2, 3])
|
||||
}"#,
|
||||
);
|
||||
|
@ -455,7 +445,7 @@ mod tests {
|
|||
24 as i32
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
if true {
|
||||
if false {
|
||||
Ok(1 as i32)
|
||||
|
@ -480,7 +470,7 @@ mod tests {
|
|||
_ => 24i32,
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let my_var = 5;
|
||||
match my_var {
|
||||
5 => Ok(42i32),
|
||||
|
@ -503,7 +493,7 @@ mod tests {
|
|||
|
||||
my_var
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let my_var = 5;
|
||||
loop {
|
||||
println!("test");
|
||||
|
@ -526,7 +516,7 @@ mod tests {
|
|||
|
||||
my_var
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let my_var = let x = loop {
|
||||
break 1;
|
||||
};
|
||||
|
@ -549,7 +539,7 @@ mod tests {
|
|||
|
||||
res
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let my_var = 5;
|
||||
let res = match my_var {
|
||||
5 => 42i32,
|
||||
|
@ -572,7 +562,7 @@ mod tests {
|
|||
|
||||
res
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let my_var = 5;
|
||||
let res = if my_var == 5 {
|
||||
42i32
|
||||
|
@ -608,7 +598,7 @@ mod tests {
|
|||
},
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let my_var = 5;
|
||||
match my_var {
|
||||
5 => {
|
||||
|
@ -641,7 +631,7 @@ mod tests {
|
|||
}
|
||||
53i32
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
if test == "test" {
|
||||
return Ok(24i32);
|
||||
|
@ -672,7 +662,7 @@ mod tests {
|
|||
|
||||
the_field
|
||||
}"#,
|
||||
r#"fn foo(the_field: u32) -> Result<u32, <|>> {
|
||||
r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
|
||||
let true_closure = || {
|
||||
return true;
|
||||
};
|
||||
|
@ -711,7 +701,7 @@ mod tests {
|
|||
|
||||
t.unwrap_or_else(|| the_field)
|
||||
}"#,
|
||||
r#"fn foo(the_field: u32) -> Result<u32, <|>> {
|
||||
r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
|
||||
let true_closure = || {
|
||||
return true;
|
||||
};
|
||||
|
@ -749,7 +739,7 @@ mod tests {
|
|||
i += 1;
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
if test == "test" {
|
||||
return Ok(24i32);
|
||||
|
@ -781,7 +771,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
if test == "test" {
|
||||
return Ok(24i32);
|
||||
|
@ -819,7 +809,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}"#,
|
||||
r#"fn foo() -> Result<i32, <|>> {
|
||||
r#"fn foo() -> Result<i32, ${0:_}> {
|
||||
let test = "test";
|
||||
let other = 5;
|
||||
if test == "test" {
|
||||
|
@ -860,7 +850,7 @@ mod tests {
|
|||
|
||||
the_field
|
||||
}"#,
|
||||
r#"fn foo(the_field: u32) -> Result<u32, <|>> {
|
||||
r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
|
||||
if the_field < 5 {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
|
@ -894,7 +884,7 @@ mod tests {
|
|||
|
||||
the_field
|
||||
}"#,
|
||||
r#"fn foo(the_field: u32) -> Result<u32, <|>> {
|
||||
r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
|
||||
if the_field < 5 {
|
||||
let mut i = 0;
|
||||
|
||||
|
@ -923,7 +913,7 @@ mod tests {
|
|||
|
||||
the_field
|
||||
}"#,
|
||||
r#"fn foo(the_field: u32) -> Result<u32, <|>> {
|
||||
r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
|
||||
if the_field < 5 {
|
||||
let mut i = 0;
|
||||
|
||||
|
@ -953,7 +943,7 @@ mod tests {
|
|||
|
||||
the_field
|
||||
}"#,
|
||||
r#"fn foo(the_field: u32) -> Result<u32, <|>> {
|
||||
r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
|
||||
if the_field < 5 {
|
||||
let mut i = 0;
|
||||
|
||||
|
|
|
@ -5,14 +5,11 @@ use ra_syntax::{
|
|||
ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
|
||||
WHITESPACE,
|
||||
},
|
||||
SyntaxNode, TextRange, TextSize, T,
|
||||
SyntaxNode, TextSize, T,
|
||||
};
|
||||
|
||||
use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use ra_db::FileId;
|
||||
|
||||
// Assist: change_visibility
|
||||
//
|
||||
|
@ -30,8 +27,6 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
|
|||
return change_vis(acc, vis);
|
||||
}
|
||||
add_vis(acc, ctx)
|
||||
.or_else(|| add_vis_to_referenced_module_def(acc, ctx))
|
||||
.or_else(|| add_vis_to_referenced_record_field(acc, ctx))
|
||||
}
|
||||
|
||||
fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
|
@ -55,7 +50,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
} else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() {
|
||||
let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?;
|
||||
if field.name()? != field_name {
|
||||
tested_by!(change_visibility_field_false_positive);
|
||||
mark::hit!(change_visibility_field_false_positive);
|
||||
return None;
|
||||
}
|
||||
if field.visibility().is_some() {
|
||||
|
@ -73,147 +68,9 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
|
||||
acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| {
|
||||
edit.insert(offset, "pub(crate) ");
|
||||
edit.set_cursor(offset);
|
||||
})
|
||||
}
|
||||
|
||||
fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let path: ast::Path = ctx.find_node_at_offset()?;
|
||||
let path_res = ctx.sema.resolve_path(&path)?;
|
||||
let def = match path_res {
|
||||
PathResolution::Def(def) => def,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let current_module = ctx.sema.scope(&path.syntax()).module()?;
|
||||
let target_module = def.module(ctx.db)?;
|
||||
|
||||
let vis = target_module.visibility_of(ctx.db, &def)?;
|
||||
if vis.is_visible_from(ctx.db, current_module.into()) {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?;
|
||||
|
||||
let missing_visibility =
|
||||
if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
|
||||
|
||||
let assist_label = match target_name {
|
||||
None => format!("Change visibility to {}", missing_visibility),
|
||||
Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
|
||||
};
|
||||
|
||||
acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
|
||||
edit.set_file(target_file);
|
||||
edit.insert(offset, format!("{} ", missing_visibility));
|
||||
edit.set_cursor(offset);
|
||||
})
|
||||
}
|
||||
|
||||
fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let record_field: ast::RecordField = ctx.find_node_at_offset()?;
|
||||
let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
|
||||
|
||||
let current_module = ctx.sema.scope(record_field.syntax()).module()?;
|
||||
let visibility = record_field_def.visibility(ctx.db);
|
||||
if visibility.is_visible_from(ctx.db, current_module.into()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let parent = record_field_def.parent_def(ctx.db);
|
||||
let parent_name = parent.name(ctx.db);
|
||||
let target_module = parent.module(ctx.db);
|
||||
|
||||
let in_file_source = record_field_def.source(ctx.db);
|
||||
let (offset, target) = match in_file_source.value {
|
||||
hir::FieldSource::Named(it) => {
|
||||
let s = it.syntax();
|
||||
(vis_offset(s), s.text_range())
|
||||
}
|
||||
hir::FieldSource::Pos(it) => {
|
||||
let s = it.syntax();
|
||||
(vis_offset(s), s.text_range())
|
||||
}
|
||||
};
|
||||
|
||||
let missing_visibility =
|
||||
if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
|
||||
let target_file = in_file_source.file_id.original_file(ctx.db);
|
||||
|
||||
let target_name = record_field_def.name(ctx.db);
|
||||
let assist_label =
|
||||
format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
|
||||
|
||||
acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
|
||||
edit.set_file(target_file);
|
||||
edit.insert(offset, format!("{} ", missing_visibility));
|
||||
edit.set_cursor(offset)
|
||||
})
|
||||
}
|
||||
|
||||
fn target_data_for_def(
|
||||
db: &dyn HirDatabase,
|
||||
def: hir::ModuleDef,
|
||||
) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> {
|
||||
fn offset_target_and_file_id<S, Ast>(
|
||||
db: &dyn HirDatabase,
|
||||
x: S,
|
||||
) -> (TextSize, TextRange, FileId)
|
||||
where
|
||||
S: HasSource<Ast = Ast>,
|
||||
Ast: AstNode,
|
||||
{
|
||||
let source = x.source(db);
|
||||
let in_file_syntax = source.syntax();
|
||||
let file_id = in_file_syntax.file_id;
|
||||
let syntax = in_file_syntax.value;
|
||||
(vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast()))
|
||||
}
|
||||
|
||||
let target_name;
|
||||
let (offset, target, target_file) = match def {
|
||||
hir::ModuleDef::Function(f) => {
|
||||
target_name = Some(f.name(db));
|
||||
offset_target_and_file_id(db, f)
|
||||
}
|
||||
hir::ModuleDef::Adt(adt) => {
|
||||
target_name = Some(adt.name(db));
|
||||
match adt {
|
||||
hir::Adt::Struct(s) => offset_target_and_file_id(db, s),
|
||||
hir::Adt::Union(u) => offset_target_and_file_id(db, u),
|
||||
hir::Adt::Enum(e) => offset_target_and_file_id(db, e),
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Const(c) => {
|
||||
target_name = c.name(db);
|
||||
offset_target_and_file_id(db, c)
|
||||
}
|
||||
hir::ModuleDef::Static(s) => {
|
||||
target_name = s.name(db);
|
||||
offset_target_and_file_id(db, s)
|
||||
}
|
||||
hir::ModuleDef::Trait(t) => {
|
||||
target_name = Some(t.name(db));
|
||||
offset_target_and_file_id(db, t)
|
||||
}
|
||||
hir::ModuleDef::TypeAlias(t) => {
|
||||
target_name = Some(t.name(db));
|
||||
offset_target_and_file_id(db, t)
|
||||
}
|
||||
hir::ModuleDef::Module(m) => {
|
||||
target_name = m.name(db);
|
||||
let in_file_source = m.declaration_source(db)?;
|
||||
let file_id = in_file_source.file_id.original_file(db.upcast());
|
||||
let syntax = in_file_source.value.syntax();
|
||||
(vis_offset(syntax), syntax.text_range(), file_id)
|
||||
}
|
||||
// Enum variants can't be private, we can't modify builtin types
|
||||
hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
|
||||
};
|
||||
|
||||
Some((offset, target, target_file, target_name))
|
||||
}
|
||||
|
||||
fn vis_offset(node: &SyntaxNode) -> TextSize {
|
||||
node.children_with_tokens()
|
||||
.skip_while(|it| match it.kind() {
|
||||
|
@ -234,7 +91,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
|
|||
target,
|
||||
|edit| {
|
||||
edit.replace(vis.syntax().text_range(), "pub(crate)");
|
||||
edit.set_cursor(vis.syntax().text_range().start())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -246,7 +102,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
|
|||
target,
|
||||
|edit| {
|
||||
edit.replace(vis.syntax().text_range(), "pub");
|
||||
edit.set_cursor(vis.syntax().text_range().start());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -255,7 +110,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||
|
||||
|
@ -263,17 +118,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn change_visibility_adds_pub_crate_to_items() {
|
||||
check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}");
|
||||
check_assist(change_visibility, "f<|>n foo() {}", "<|>pub(crate) fn foo() {}");
|
||||
check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}");
|
||||
check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}");
|
||||
check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}");
|
||||
check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}");
|
||||
check_assist(
|
||||
change_visibility,
|
||||
"unsafe f<|>n foo() {}",
|
||||
"<|>pub(crate) unsafe fn foo() {}",
|
||||
);
|
||||
check_assist(change_visibility, "<|>fn foo() {}", "pub(crate) fn foo() {}");
|
||||
check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) fn foo() {}");
|
||||
check_assist(change_visibility, "<|>struct Foo {}", "pub(crate) struct Foo {}");
|
||||
check_assist(change_visibility, "<|>mod foo {}", "pub(crate) mod foo {}");
|
||||
check_assist(change_visibility, "<|>trait Foo {}", "pub(crate) trait Foo {}");
|
||||
check_assist(change_visibility, "m<|>od {}", "pub(crate) mod {}");
|
||||
check_assist(change_visibility, "unsafe f<|>n foo() {}", "pub(crate) unsafe fn foo() {}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -281,14 +132,14 @@ mod tests {
|
|||
check_assist(
|
||||
change_visibility,
|
||||
r"struct S { <|>field: u32 }",
|
||||
r"struct S { <|>pub(crate) field: u32 }",
|
||||
r"struct S { pub(crate) field: u32 }",
|
||||
);
|
||||
check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )");
|
||||
check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( pub(crate) u32 )");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_field_false_positive() {
|
||||
covers!(change_visibility_field_false_positive);
|
||||
mark::check!(change_visibility_field_false_positive);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"struct S { field: [(); { let <|>x = ();}] }",
|
||||
|
@ -297,17 +148,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn change_visibility_pub_to_pub_crate() {
|
||||
check_assist(change_visibility, "<|>pub fn foo() {}", "<|>pub(crate) fn foo() {}")
|
||||
check_assist(change_visibility, "<|>pub fn foo() {}", "pub(crate) fn foo() {}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_pub_crate_to_pub() {
|
||||
check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "<|>pub fn foo() {}")
|
||||
check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "pub fn foo() {}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_const() {
|
||||
check_assist(change_visibility, "<|>const FOO = 3u8;", "<|>pub(crate) const FOO = 3u8;");
|
||||
check_assist(change_visibility, "<|>const FOO = 3u8;", "pub(crate) const FOO = 3u8;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -328,198 +179,11 @@ mod tests {
|
|||
// comments
|
||||
|
||||
#[derive(Debug)]
|
||||
<|>pub(crate) struct Foo;
|
||||
pub(crate) struct Foo;
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_fn_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { fn foo() {} }
|
||||
fn main() { foo::foo<|>() } ",
|
||||
r"mod foo { <|>pub(crate) fn foo() {} }
|
||||
fn main() { foo::foo() } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub fn foo() {} }
|
||||
fn main() { foo::foo<|>() } ",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_adt_in_submodule_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { struct Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
r"mod foo { <|>pub(crate) struct Foo; }
|
||||
fn main() { foo::Foo } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub struct Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
);
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { enum Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
r"mod foo { <|>pub(crate) enum Foo; }
|
||||
fn main() { foo::Foo } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub enum Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
);
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { union Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
r"mod foo { <|>pub(crate) union Foo; }
|
||||
fn main() { foo::Foo } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub union Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_adt_in_other_file_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo<|> }
|
||||
|
||||
//- /foo.rs
|
||||
struct Foo;
|
||||
",
|
||||
r"<|>pub(crate) struct Foo;
|
||||
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_struct_field_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { pub struct Foo { bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
|
||||
fn main() { foo::Foo { bar: () }; } ",
|
||||
);
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub struct Foo { bar: () }
|
||||
",
|
||||
r"pub struct Foo { <|>pub(crate) bar: () }
|
||||
|
||||
",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub struct Foo { pub bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub struct Foo { pub bar: () }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_enum_variant_field_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { pub enum Foo { Bar { bar: () } } }
|
||||
fn main() { foo::Foo::Bar { <|>bar: () }; } ",
|
||||
r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } }
|
||||
fn main() { foo::Foo::Bar { bar: () }; } ",
|
||||
);
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo::Bar { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub enum Foo { Bar { bar: () } }
|
||||
",
|
||||
r"pub enum Foo { Bar { <|>pub(crate) bar: () } }
|
||||
|
||||
",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub struct Foo { pub bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub struct Foo { pub bar: () }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
// FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
|
||||
fn change_visibility_of_union_field_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { pub union Foo { bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
r"mod foo { pub union Foo { <|>pub(crate) bar: (), } }
|
||||
fn main() { foo::Foo { bar: () }; } ",
|
||||
);
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub union Foo { bar: () }
|
||||
",
|
||||
r"pub union Foo { <|>pub(crate) bar: () }
|
||||
|
||||
",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub union Foo { pub bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub union Foo { pub bar: () }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_applicable_for_enum_variants() {
|
||||
check_assist_not_applicable(
|
||||
|
@ -529,182 +193,6 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_const_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { const FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
r"mod foo { <|>pub(crate) const FOO: () = (); }
|
||||
fn main() { foo::FOO } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub const FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_static_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { static FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
r"mod foo { <|>pub(crate) static FOO: () = (); }
|
||||
fn main() { foo::FOO } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub static FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_trait_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { trait Foo { fn foo(&self) {} } }
|
||||
fn main() { let x: &dyn foo::<|>Foo; } ",
|
||||
r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
|
||||
fn main() { let x: &dyn foo::Foo; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub trait Foo { fn foo(&self) {} } }
|
||||
fn main() { let x: &dyn foo::Foo<|>; } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_type_alias_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { type Foo = (); }
|
||||
fn main() { let x: foo::Foo<|>; } ",
|
||||
r"mod foo { <|>pub(crate) type Foo = (); }
|
||||
fn main() { let x: foo::Foo; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub type Foo = (); }
|
||||
fn main() { let x: foo::Foo<|>; } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_module_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"mod foo { mod bar { fn bar() {} } }
|
||||
fn main() { foo::bar<|>::bar(); } ",
|
||||
r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
|
||||
fn main() { foo::bar::bar(); } ",
|
||||
);
|
||||
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::bar<|>::baz(); }
|
||||
|
||||
//- /foo.rs
|
||||
mod bar {
|
||||
pub fn baz() {}
|
||||
}
|
||||
",
|
||||
r"<|>pub(crate) mod bar {
|
||||
pub fn baz() {}
|
||||
}
|
||||
|
||||
",
|
||||
);
|
||||
|
||||
check_assist_not_applicable(
|
||||
change_visibility,
|
||||
r"mod foo { pub mod bar { pub fn bar() {} } }
|
||||
fn main() { foo::bar<|>::bar(); } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_inline_module_in_other_file_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::bar<|>::baz(); }
|
||||
|
||||
//- /foo.rs
|
||||
mod bar;
|
||||
|
||||
//- /foo/bar.rs
|
||||
pub fn baz() {}
|
||||
}
|
||||
",
|
||||
r"<|>pub(crate) mod bar;
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_of_module_declaration_in_other_file_via_path() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::bar<|>>::baz(); }
|
||||
|
||||
//- /foo.rs
|
||||
mod bar {
|
||||
pub fn baz() {}
|
||||
}",
|
||||
r"<|>pub(crate) mod bar {
|
||||
pub fn baz() {}
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
// FIXME handle reexports properly
|
||||
fn change_visibility_of_reexport() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"
|
||||
mod foo {
|
||||
use bar::Baz;
|
||||
mod bar { pub(super) struct Baz; }
|
||||
}
|
||||
foo::Baz<|>
|
||||
",
|
||||
r"
|
||||
mod foo {
|
||||
<|>pub(crate) use bar::Baz;
|
||||
mod bar { pub(super) struct Baz; }
|
||||
}
|
||||
foo::Baz
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_pub_when_target_is_in_another_crate() {
|
||||
check_assist(
|
||||
change_visibility,
|
||||
r"//- /main.rs crate:a deps:foo
|
||||
foo::Bar<|>
|
||||
//- /lib.rs crate:foo
|
||||
struct Bar;",
|
||||
r"<|>pub struct Bar;
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_visibility_target() {
|
||||
check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
|
||||
|
|
|
@ -97,7 +97,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
|
|||
}
|
||||
|
||||
then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
|
||||
let cursor_position = ctx.offset();
|
||||
|
||||
let target = if_expr.syntax().text_range();
|
||||
acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
|
||||
|
@ -148,7 +147,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
|
|||
}
|
||||
};
|
||||
edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
|
||||
edit.set_cursor(cursor_position);
|
||||
|
||||
fn replace(
|
||||
new_expr: &SyntaxNode,
|
||||
|
@ -207,7 +205,7 @@ mod tests {
|
|||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
if<|> !true {
|
||||
if !true {
|
||||
return;
|
||||
}
|
||||
foo();
|
||||
|
@ -237,7 +235,7 @@ mod tests {
|
|||
r#"
|
||||
fn main(n: Option<String>) {
|
||||
bar();
|
||||
le<|>t n = match n {
|
||||
let n = match n {
|
||||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
|
@ -263,7 +261,7 @@ mod tests {
|
|||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
le<|>t x = match Err(92) {
|
||||
let x = match Err(92) {
|
||||
Ok(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
|
@ -291,7 +289,7 @@ mod tests {
|
|||
r#"
|
||||
fn main(n: Option<String>) {
|
||||
bar();
|
||||
le<|>t n = match n {
|
||||
let n = match n {
|
||||
Ok(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
|
@ -321,7 +319,7 @@ mod tests {
|
|||
r#"
|
||||
fn main() {
|
||||
while true {
|
||||
if<|> !true {
|
||||
if !true {
|
||||
continue;
|
||||
}
|
||||
foo();
|
||||
|
@ -349,7 +347,7 @@ mod tests {
|
|||
r#"
|
||||
fn main() {
|
||||
while true {
|
||||
le<|>t n = match n {
|
||||
let n = match n {
|
||||
Some(it) => it,
|
||||
_ => continue,
|
||||
};
|
||||
|
@ -378,7 +376,7 @@ mod tests {
|
|||
r#"
|
||||
fn main() {
|
||||
loop {
|
||||
if<|> !true {
|
||||
if !true {
|
||||
continue;
|
||||
}
|
||||
foo();
|
||||
|
@ -406,7 +404,7 @@ mod tests {
|
|||
r#"
|
||||
fn main() {
|
||||
loop {
|
||||
le<|>t n = match n {
|
||||
let n = match n {
|
||||
Some(it) => it,
|
||||
_ => continue,
|
||||
};
|
||||
|
|
|
@ -4,8 +4,12 @@ use hir::{Adt, HasSource, ModuleDef, Semantics};
|
|||
use itertools::Itertools;
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{
|
||||
utils::{render_snippet, Cursor, FamousDefs},
|
||||
AssistContext, AssistId, Assists,
|
||||
};
|
||||
|
||||
// Assist: fill_match_arms
|
||||
//
|
||||
|
@ -26,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
//
|
||||
// fn handle(action: Action) {
|
||||
// match action {
|
||||
// Action::Move { distance } => {}
|
||||
// $0Action::Move { distance } => {}
|
||||
// Action::Stop => {}
|
||||
// }
|
||||
// }
|
||||
|
@ -49,12 +53,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
|||
let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
|
||||
let variants = enum_def.variants(ctx.db);
|
||||
|
||||
variants
|
||||
let mut variants = variants
|
||||
.into_iter()
|
||||
.filter_map(|variant| build_pat(ctx.db, module, variant))
|
||||
.filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
|
||||
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
|
||||
.collect()
|
||||
.collect::<Vec<_>>();
|
||||
if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() {
|
||||
// Match `Some` variant first.
|
||||
mark::hit!(option_order);
|
||||
variants.reverse()
|
||||
}
|
||||
variants
|
||||
} else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
|
||||
// Partial fill not currently supported for tuple of enums.
|
||||
if !arms.is_empty() {
|
||||
|
@ -93,10 +103,23 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
|||
}
|
||||
|
||||
let target = match_expr.syntax().text_range();
|
||||
acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| {
|
||||
let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
|
||||
edit.set_cursor(expr.syntax().text_range().start());
|
||||
edit.replace_ast(match_arm_list, new_arm_list);
|
||||
acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| {
|
||||
let new_arm_list = match_arm_list.remove_placeholder();
|
||||
let n_old_arms = new_arm_list.arms().count();
|
||||
let new_arm_list = new_arm_list.append_arms(missing_arms);
|
||||
let first_new_arm = new_arm_list.arms().nth(n_old_arms);
|
||||
let old_range = match_arm_list.syntax().text_range();
|
||||
match (first_new_arm, ctx.config.snippet_cap) {
|
||||
(Some(first_new_arm), Some(cap)) => {
|
||||
let snippet = render_snippet(
|
||||
cap,
|
||||
new_arm_list.syntax(),
|
||||
Cursor::Before(first_new_arm.syntax()),
|
||||
);
|
||||
builder.replace_snippet(cap, old_range, snippet);
|
||||
}
|
||||
_ => builder.replace(old_range, new_arm_list.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -167,7 +190,12 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
tests::{check_assist, check_assist_not_applicable, check_assist_target},
|
||||
utils::FamousDefs,
|
||||
};
|
||||
|
||||
use super::fill_match_arms;
|
||||
|
||||
|
@ -214,12 +242,12 @@ mod tests {
|
|||
r#"
|
||||
enum A {
|
||||
As,
|
||||
Bs{x:i32, y:Option<i32>},
|
||||
Bs { x: i32, y: Option<i32> },
|
||||
Cs(i32, Option<i32>),
|
||||
}
|
||||
fn main() {
|
||||
match A::As<|> {
|
||||
A::Bs{x,y:Some(_)} => {}
|
||||
A::Bs { x, y: Some(_) } => {}
|
||||
A::Cs(_, Some(_)) => {}
|
||||
}
|
||||
}
|
||||
|
@ -227,14 +255,14 @@ mod tests {
|
|||
r#"
|
||||
enum A {
|
||||
As,
|
||||
Bs{x:i32, y:Option<i32>},
|
||||
Bs { x: i32, y: Option<i32> },
|
||||
Cs(i32, Option<i32>),
|
||||
}
|
||||
fn main() {
|
||||
match <|>A::As {
|
||||
A::Bs{x,y:Some(_)} => {}
|
||||
match A::As {
|
||||
A::Bs { x, y: Some(_) } => {}
|
||||
A::Cs(_, Some(_)) => {}
|
||||
A::As => {}
|
||||
$0A::As => {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -264,9 +292,9 @@ mod tests {
|
|||
Cs(Option<i32>),
|
||||
}
|
||||
fn main() {
|
||||
match <|>A::As {
|
||||
match A::As {
|
||||
A::Cs(_) | A::Bs => {}
|
||||
A::As => {}
|
||||
$0A::As => {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -310,11 +338,11 @@ mod tests {
|
|||
Ys,
|
||||
}
|
||||
fn main() {
|
||||
match <|>A::As {
|
||||
match A::As {
|
||||
A::Bs if 0 < 1 => {}
|
||||
A::Ds(_value) => { let x = 1; }
|
||||
A::Es(B::Xs) => (),
|
||||
A::As => {}
|
||||
$0A::As => {}
|
||||
A::Cs => {}
|
||||
}
|
||||
}
|
||||
|
@ -332,7 +360,7 @@ mod tests {
|
|||
Bs,
|
||||
Cs(String),
|
||||
Ds(String, String),
|
||||
Es{ x: usize, y: usize }
|
||||
Es { x: usize, y: usize }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -346,13 +374,13 @@ mod tests {
|
|||
Bs,
|
||||
Cs(String),
|
||||
Ds(String, String),
|
||||
Es{ x: usize, y: usize }
|
||||
Es { x: usize, y: usize }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = A::As;
|
||||
match <|>a {
|
||||
A::As => {}
|
||||
match a {
|
||||
$0A::As => {}
|
||||
A::Bs => {}
|
||||
A::Cs(_) => {}
|
||||
A::Ds(_, _) => {}
|
||||
|
@ -368,14 +396,8 @@ mod tests {
|
|||
check_assist(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum B {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
enum B { One, Two }
|
||||
|
||||
fn main() {
|
||||
let a = A::One;
|
||||
|
@ -384,20 +406,14 @@ mod tests {
|
|||
}
|
||||
"#,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum B {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
enum B { One, Two }
|
||||
|
||||
fn main() {
|
||||
let a = A::One;
|
||||
let b = B::One;
|
||||
match <|>(a, b) {
|
||||
(A::One, B::One) => {}
|
||||
match (a, b) {
|
||||
$0(A::One, B::One) => {}
|
||||
(A::One, B::Two) => {}
|
||||
(A::Two, B::One) => {}
|
||||
(A::Two, B::Two) => {}
|
||||
|
@ -412,14 +428,8 @@ mod tests {
|
|||
check_assist(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum B {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
enum B { One, Two }
|
||||
|
||||
fn main() {
|
||||
let a = A::One;
|
||||
|
@ -428,20 +438,14 @@ mod tests {
|
|||
}
|
||||
"#,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum B {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
enum B { One, Two }
|
||||
|
||||
fn main() {
|
||||
let a = A::One;
|
||||
let b = B::One;
|
||||
match <|>(&a, &b) {
|
||||
(A::One, B::One) => {}
|
||||
match (&a, &b) {
|
||||
$0(A::One, B::One) => {}
|
||||
(A::One, B::Two) => {}
|
||||
(A::Two, B::One) => {}
|
||||
(A::Two, B::Two) => {}
|
||||
|
@ -456,14 +460,8 @@ mod tests {
|
|||
check_assist_not_applicable(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum B {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
enum B { One, Two }
|
||||
|
||||
fn main() {
|
||||
let a = A::One;
|
||||
|
@ -481,14 +479,8 @@ mod tests {
|
|||
check_assist_not_applicable(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum B {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
enum B { One, Two }
|
||||
|
||||
fn main() {
|
||||
let a = A::One;
|
||||
|
@ -512,10 +504,7 @@ mod tests {
|
|||
check_assist_not_applicable(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
|
||||
fn main() {
|
||||
let a = A::One;
|
||||
|
@ -531,9 +520,7 @@ mod tests {
|
|||
check_assist(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
As,
|
||||
}
|
||||
enum A { As }
|
||||
|
||||
fn foo(a: &A) {
|
||||
match a<|> {
|
||||
|
@ -541,13 +528,11 @@ mod tests {
|
|||
}
|
||||
"#,
|
||||
r#"
|
||||
enum A {
|
||||
As,
|
||||
}
|
||||
enum A { As }
|
||||
|
||||
fn foo(a: &A) {
|
||||
match <|>a {
|
||||
A::As => {}
|
||||
match a {
|
||||
$0A::As => {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -557,7 +542,7 @@ mod tests {
|
|||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
Es{ x: usize, y: usize }
|
||||
Es { x: usize, y: usize }
|
||||
}
|
||||
|
||||
fn foo(a: &mut A) {
|
||||
|
@ -567,12 +552,12 @@ mod tests {
|
|||
"#,
|
||||
r#"
|
||||
enum A {
|
||||
Es{ x: usize, y: usize }
|
||||
Es { x: usize, y: usize }
|
||||
}
|
||||
|
||||
fn foo(a: &mut A) {
|
||||
match <|>a {
|
||||
A::Es { x, y } => {}
|
||||
match a {
|
||||
$0A::Es { x, y } => {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -611,8 +596,8 @@ mod tests {
|
|||
enum E { X, Y }
|
||||
|
||||
fn main() {
|
||||
match <|>E::X {
|
||||
E::X => {}
|
||||
match E::X {
|
||||
$0E::X => {}
|
||||
E::Y => {}
|
||||
}
|
||||
}
|
||||
|
@ -639,8 +624,8 @@ mod tests {
|
|||
use foo::E::X;
|
||||
|
||||
fn main() {
|
||||
match <|>X {
|
||||
X => {}
|
||||
match X {
|
||||
$0X => {}
|
||||
foo::E::Y => {}
|
||||
}
|
||||
}
|
||||
|
@ -653,10 +638,7 @@ mod tests {
|
|||
check_assist(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
fn foo(a: A) {
|
||||
match a {
|
||||
// foo bar baz<|>
|
||||
|
@ -666,16 +648,13 @@ mod tests {
|
|||
}
|
||||
"#,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
fn foo(a: A) {
|
||||
match <|>a {
|
||||
match a {
|
||||
// foo bar baz
|
||||
A::One => {}
|
||||
// This is where the rest should be
|
||||
A::Two => {}
|
||||
$0A::Two => {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -687,10 +666,7 @@ mod tests {
|
|||
check_assist(
|
||||
fill_match_arms,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
fn foo(a: A) {
|
||||
match a {
|
||||
// foo bar baz<|>
|
||||
|
@ -698,14 +674,11 @@ mod tests {
|
|||
}
|
||||
"#,
|
||||
r#"
|
||||
enum A {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
enum A { One, Two }
|
||||
fn foo(a: A) {
|
||||
match <|>a {
|
||||
match a {
|
||||
// foo bar baz
|
||||
A::One => {}
|
||||
$0A::One => {}
|
||||
A::Two => {}
|
||||
}
|
||||
}
|
||||
|
@ -728,12 +701,37 @@ mod tests {
|
|||
r#"
|
||||
enum A { One, Two, }
|
||||
fn foo(a: A) {
|
||||
match <|>a {
|
||||
A::One => {}
|
||||
match a {
|
||||
$0A::One => {}
|
||||
A::Two => {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_order() {
|
||||
mark::check!(option_order);
|
||||
let before = r#"
|
||||
fn foo(opt: Option<i32>) {
|
||||
match opt<|> {
|
||||
}
|
||||
}"#;
|
||||
let before =
|
||||
&format!("//- main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE);
|
||||
|
||||
check_assist(
|
||||
fill_match_arms,
|
||||
before,
|
||||
r#"
|
||||
fn foo(opt: Option<i32>) {
|
||||
match opt {
|
||||
$0Some(_) => {}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
559
crates/ra_assists/src/handlers/fix_visibility.rs
Normal file
559
crates/ra_assists/src/handlers/fix_visibility.rs
Normal file
|
@ -0,0 +1,559 @@
|
|||
use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::{
|
||||
ast, AstNode,
|
||||
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
||||
SyntaxNode, TextRange, TextSize,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
||||
// FIXME: this really should be a fix for diagnostic, rather than an assist.
|
||||
|
||||
// Assist: fix_visibility
|
||||
//
|
||||
// Makes inaccessible item public.
|
||||
//
|
||||
// ```
|
||||
// mod m {
|
||||
// fn frobnicate() {}
|
||||
// }
|
||||
// fn main() {
|
||||
// m::frobnicate<|>() {}
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// mod m {
|
||||
// $0pub(crate) fn frobnicate() {}
|
||||
// }
|
||||
// fn main() {
|
||||
// m::frobnicate() {}
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
add_vis_to_referenced_module_def(acc, ctx)
|
||||
.or_else(|| add_vis_to_referenced_record_field(acc, ctx))
|
||||
}
|
||||
|
||||
fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let path: ast::Path = ctx.find_node_at_offset()?;
|
||||
let path_res = ctx.sema.resolve_path(&path)?;
|
||||
let def = match path_res {
|
||||
PathResolution::Def(def) => def,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let current_module = ctx.sema.scope(&path.syntax()).module()?;
|
||||
let target_module = def.module(ctx.db)?;
|
||||
|
||||
let vis = target_module.visibility_of(ctx.db, &def)?;
|
||||
if vis.is_visible_from(ctx.db, current_module.into()) {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?;
|
||||
|
||||
let missing_visibility =
|
||||
if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
|
||||
|
||||
let assist_label = match target_name {
|
||||
None => format!("Change visibility to {}", missing_visibility),
|
||||
Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
|
||||
};
|
||||
|
||||
acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
|
||||
builder.set_file(target_file);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
|
||||
None => builder.insert(offset, format!("{} ", missing_visibility)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let record_field: ast::RecordField = ctx.find_node_at_offset()?;
|
||||
let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
|
||||
|
||||
let current_module = ctx.sema.scope(record_field.syntax()).module()?;
|
||||
let visibility = record_field_def.visibility(ctx.db);
|
||||
if visibility.is_visible_from(ctx.db, current_module.into()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let parent = record_field_def.parent_def(ctx.db);
|
||||
let parent_name = parent.name(ctx.db);
|
||||
let target_module = parent.module(ctx.db);
|
||||
|
||||
let in_file_source = record_field_def.source(ctx.db);
|
||||
let (offset, target) = match in_file_source.value {
|
||||
hir::FieldSource::Named(it) => {
|
||||
let s = it.syntax();
|
||||
(vis_offset(s), s.text_range())
|
||||
}
|
||||
hir::FieldSource::Pos(it) => {
|
||||
let s = it.syntax();
|
||||
(vis_offset(s), s.text_range())
|
||||
}
|
||||
};
|
||||
|
||||
let missing_visibility =
|
||||
if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
|
||||
let target_file = in_file_source.file_id.original_file(ctx.db);
|
||||
|
||||
let target_name = record_field_def.name(ctx.db);
|
||||
let assist_label =
|
||||
format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
|
||||
|
||||
acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
|
||||
builder.set_file(target_file);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
|
||||
None => builder.insert(offset, format!("{} ", missing_visibility)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn target_data_for_def(
|
||||
db: &dyn HirDatabase,
|
||||
def: hir::ModuleDef,
|
||||
) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> {
|
||||
fn offset_target_and_file_id<S, Ast>(
|
||||
db: &dyn HirDatabase,
|
||||
x: S,
|
||||
) -> (TextSize, TextRange, FileId)
|
||||
where
|
||||
S: HasSource<Ast = Ast>,
|
||||
Ast: AstNode,
|
||||
{
|
||||
let source = x.source(db);
|
||||
let in_file_syntax = source.syntax();
|
||||
let file_id = in_file_syntax.file_id;
|
||||
let syntax = in_file_syntax.value;
|
||||
(vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast()))
|
||||
}
|
||||
|
||||
let target_name;
|
||||
let (offset, target, target_file) = match def {
|
||||
hir::ModuleDef::Function(f) => {
|
||||
target_name = Some(f.name(db));
|
||||
offset_target_and_file_id(db, f)
|
||||
}
|
||||
hir::ModuleDef::Adt(adt) => {
|
||||
target_name = Some(adt.name(db));
|
||||
match adt {
|
||||
hir::Adt::Struct(s) => offset_target_and_file_id(db, s),
|
||||
hir::Adt::Union(u) => offset_target_and_file_id(db, u),
|
||||
hir::Adt::Enum(e) => offset_target_and_file_id(db, e),
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Const(c) => {
|
||||
target_name = c.name(db);
|
||||
offset_target_and_file_id(db, c)
|
||||
}
|
||||
hir::ModuleDef::Static(s) => {
|
||||
target_name = s.name(db);
|
||||
offset_target_and_file_id(db, s)
|
||||
}
|
||||
hir::ModuleDef::Trait(t) => {
|
||||
target_name = Some(t.name(db));
|
||||
offset_target_and_file_id(db, t)
|
||||
}
|
||||
hir::ModuleDef::TypeAlias(t) => {
|
||||
target_name = Some(t.name(db));
|
||||
offset_target_and_file_id(db, t)
|
||||
}
|
||||
hir::ModuleDef::Module(m) => {
|
||||
target_name = m.name(db);
|
||||
let in_file_source = m.declaration_source(db)?;
|
||||
let file_id = in_file_source.file_id.original_file(db.upcast());
|
||||
let syntax = in_file_source.value.syntax();
|
||||
(vis_offset(syntax), syntax.text_range(), file_id)
|
||||
}
|
||||
// Enum variants can't be private, we can't modify builtin types
|
||||
hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
|
||||
};
|
||||
|
||||
Some((offset, target, target_file, target_name))
|
||||
}
|
||||
|
||||
fn vis_offset(node: &SyntaxNode) -> TextSize {
|
||||
node.children_with_tokens()
|
||||
.skip_while(|it| match it.kind() {
|
||||
WHITESPACE | COMMENT | ATTR => true,
|
||||
_ => false,
|
||||
})
|
||||
.next()
|
||||
.map(|it| it.text_range().start())
|
||||
.unwrap_or_else(|| node.text_range().start())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_fn() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { fn foo() {} }
|
||||
fn main() { foo::foo<|>() } ",
|
||||
r"mod foo { $0pub(crate) fn foo() {} }
|
||||
fn main() { foo::foo() } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub fn foo() {} }
|
||||
fn main() { foo::foo<|>() } ",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_adt_in_submodule() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { struct Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
r"mod foo { $0pub(crate) struct Foo; }
|
||||
fn main() { foo::Foo } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub struct Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
);
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { enum Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
r"mod foo { $0pub(crate) enum Foo; }
|
||||
fn main() { foo::Foo } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub enum Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
);
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { union Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
r"mod foo { $0pub(crate) union Foo; }
|
||||
fn main() { foo::Foo } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub union Foo; }
|
||||
fn main() { foo::Foo<|> } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_adt_in_other_file() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo<|> }
|
||||
|
||||
//- /foo.rs
|
||||
struct Foo;
|
||||
",
|
||||
r"$0pub(crate) struct Foo;
|
||||
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_struct_field() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { pub struct Foo { bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
r"mod foo { pub struct Foo { $0pub(crate) bar: (), } }
|
||||
fn main() { foo::Foo { bar: () }; } ",
|
||||
);
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub struct Foo { bar: () }
|
||||
",
|
||||
r"pub struct Foo { $0pub(crate) bar: () }
|
||||
|
||||
",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub struct Foo { pub bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub struct Foo { pub bar: () }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_enum_variant_field() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { pub enum Foo { Bar { bar: () } } }
|
||||
fn main() { foo::Foo::Bar { <|>bar: () }; } ",
|
||||
r"mod foo { pub enum Foo { Bar { $0pub(crate) bar: () } } }
|
||||
fn main() { foo::Foo::Bar { bar: () }; } ",
|
||||
);
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo::Bar { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub enum Foo { Bar { bar: () } }
|
||||
",
|
||||
r"pub enum Foo { Bar { $0pub(crate) bar: () } }
|
||||
|
||||
",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub struct Foo { pub bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub struct Foo { pub bar: () }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
// FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
|
||||
fn fix_visibility_of_union_field() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { pub union Foo { bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
r"mod foo { pub union Foo { $0pub(crate) bar: (), } }
|
||||
fn main() { foo::Foo { bar: () }; } ",
|
||||
);
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub union Foo { bar: () }
|
||||
",
|
||||
r"pub union Foo { $0pub(crate) bar: () }
|
||||
|
||||
",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub union Foo { pub bar: (), } }
|
||||
fn main() { foo::Foo { <|>bar: () }; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"//- /lib.rs
|
||||
mod foo;
|
||||
fn main() { foo::Foo { <|>bar: () }; }
|
||||
//- /foo.rs
|
||||
pub union Foo { pub bar: () }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_const() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { const FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
r"mod foo { $0pub(crate) const FOO: () = (); }
|
||||
fn main() { foo::FOO } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub const FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_static() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { static FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
r"mod foo { $0pub(crate) static FOO: () = (); }
|
||||
fn main() { foo::FOO } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub static FOO: () = (); }
|
||||
fn main() { foo::FOO<|> } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_trait() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { trait Foo { fn foo(&self) {} } }
|
||||
fn main() { let x: &dyn foo::<|>Foo; } ",
|
||||
r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } }
|
||||
fn main() { let x: &dyn foo::Foo; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub trait Foo { fn foo(&self) {} } }
|
||||
fn main() { let x: &dyn foo::Foo<|>; } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_type_alias() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { type Foo = (); }
|
||||
fn main() { let x: foo::Foo<|>; } ",
|
||||
r"mod foo { $0pub(crate) type Foo = (); }
|
||||
fn main() { let x: foo::Foo; } ",
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub type Foo = (); }
|
||||
fn main() { let x: foo::Foo<|>; } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_module() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"mod foo { mod bar { fn bar() {} } }
|
||||
fn main() { foo::bar<|>::bar(); } ",
|
||||
r"mod foo { $0pub(crate) mod bar { fn bar() {} } }
|
||||
fn main() { foo::bar::bar(); } ",
|
||||
);
|
||||
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::bar<|>::baz(); }
|
||||
|
||||
//- /foo.rs
|
||||
mod bar {
|
||||
pub fn baz() {}
|
||||
}
|
||||
",
|
||||
r"$0pub(crate) mod bar {
|
||||
pub fn baz() {}
|
||||
}
|
||||
|
||||
",
|
||||
);
|
||||
|
||||
check_assist_not_applicable(
|
||||
fix_visibility,
|
||||
r"mod foo { pub mod bar { pub fn bar() {} } }
|
||||
fn main() { foo::bar<|>::bar(); } ",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_inline_module_in_other_file() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"
|
||||
//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::bar<|>::baz(); }
|
||||
|
||||
//- /foo.rs
|
||||
mod bar;
|
||||
|
||||
//- /foo/bar.rs
|
||||
pub fn baz() {}
|
||||
}
|
||||
",
|
||||
r"$0pub(crate) mod bar;
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_visibility_of_module_declaration_in_other_file() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"//- /main.rs
|
||||
mod foo;
|
||||
fn main() { foo::bar<|>>::baz(); }
|
||||
|
||||
//- /foo.rs
|
||||
mod bar {
|
||||
pub fn baz() {}
|
||||
}",
|
||||
r"$0pub(crate) mod bar {
|
||||
pub fn baz() {}
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_pub_when_target_is_in_another_crate() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"//- /main.rs crate:a deps:foo
|
||||
foo::Bar<|>
|
||||
//- /lib.rs crate:foo
|
||||
struct Bar;",
|
||||
r"$0pub struct Bar;
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
// FIXME handle reexports properly
|
||||
fn fix_visibility_of_reexport() {
|
||||
check_assist(
|
||||
fix_visibility,
|
||||
r"
|
||||
mod foo {
|
||||
use bar::Baz;
|
||||
mod bar { pub(super) struct Baz; }
|
||||
}
|
||||
foo::Baz<|>
|
||||
",
|
||||
r"
|
||||
mod foo {
|
||||
$0pub(crate) use bar::Baz;
|
||||
mod bar { pub(super) struct Baz; }
|
||||
}
|
||||
foo::Baz
|
||||
",
|
||||
)
|
||||
}
|
||||
}
|
|
@ -85,17 +85,13 @@ mod tests {
|
|||
check_assist(
|
||||
flip_binexpr,
|
||||
"fn f() { let res = 1 ==<|> 2; }",
|
||||
"fn f() { let res = 2 ==<|> 1; }",
|
||||
"fn f() { let res = 2 == 1; }",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_binexpr_works_for_gt() {
|
||||
check_assist(
|
||||
flip_binexpr,
|
||||
"fn f() { let res = 1 ><|> 2; }",
|
||||
"fn f() { let res = 2 <<|> 1; }",
|
||||
)
|
||||
check_assist(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", "fn f() { let res = 2 < 1; }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -103,7 +99,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_binexpr,
|
||||
"fn f() { let res = 1 <=<|> 2; }",
|
||||
"fn f() { let res = 2 >=<|> 1; }",
|
||||
"fn f() { let res = 2 >= 1; }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -112,7 +108,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_binexpr,
|
||||
"fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
|
||||
"fn f() { let res = (2 + 2) ==<|> (1 + 1); }",
|
||||
"fn f() { let res = (2 + 2) == (1 + 1); }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -132,7 +128,7 @@ mod tests {
|
|||
fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
|
||||
match other.downcast_ref::<Self>() {
|
||||
None => false,
|
||||
Some(it) => self ==<|> it,
|
||||
Some(it) => self == it,
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
|
|
@ -45,7 +45,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_comma,
|
||||
"fn foo(x: i32,<|> y: Result<(), ()>) {}",
|
||||
"fn foo(y: Result<(), ()>,<|> x: i32) {}",
|
||||
"fn foo(y: Result<(), ()>, x: i32) {}",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_trait_bound,
|
||||
"struct S<T> where T: A <|>+ B { }",
|
||||
"struct S<T> where T: B <|>+ A { }",
|
||||
"struct S<T> where T: B + A { }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -69,13 +69,13 @@ mod tests {
|
|||
check_assist(
|
||||
flip_trait_bound,
|
||||
"impl X for S<T> where T: A +<|> B { }",
|
||||
"impl X for S<T> where T: B +<|> A { }",
|
||||
"impl X for S<T> where T: B + A { }",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flip_trait_bound_works_for_fn() {
|
||||
check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B <|>+ A>(t: T) { }")
|
||||
check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -83,7 +83,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_trait_bound,
|
||||
"fn f<T>(t: T) where T: A +<|> B { }",
|
||||
"fn f<T>(t: T) where T: B +<|> A { }",
|
||||
"fn f<T>(t: T) where T: B + A { }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_trait_bound,
|
||||
"fn f<T>(t: T) where T: A <|>+ 'static { }",
|
||||
"fn f<T>(t: T) where T: 'static <|>+ A { }",
|
||||
"fn f<T>(t: T) where T: 'static + A { }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_trait_bound,
|
||||
"struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }",
|
||||
"struct S<T> where T: b_mod::B<T> <|>+ A<T> + C<T> { }",
|
||||
"struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ mod tests {
|
|||
check_assist(
|
||||
flip_trait_bound,
|
||||
"struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }",
|
||||
"struct S<T> where T: A + B + C + D + E + G +<|> F + H + I + J { }",
|
||||
"struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use ra_syntax::{
|
|||
ast::{self, AstNode, AstToken},
|
||||
TextRange,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
|
@ -33,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
|
|||
_ => return None,
|
||||
};
|
||||
if bind_pat.mut_token().is_some() {
|
||||
tested_by!(test_not_inline_mut_variable);
|
||||
mark::hit!(test_not_inline_mut_variable);
|
||||
return None;
|
||||
}
|
||||
if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
|
||||
tested_by!(not_applicable_outside_of_bind_pat);
|
||||
mark::hit!(not_applicable_outside_of_bind_pat);
|
||||
return None;
|
||||
}
|
||||
let initializer_expr = let_stmt.initializer()?;
|
||||
|
@ -46,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
|
|||
let def = Definition::Local(def);
|
||||
let refs = def.find_usages(ctx.db, None);
|
||||
if refs.is_empty() {
|
||||
tested_by!(test_not_applicable_if_variable_unused);
|
||||
mark::hit!(test_not_applicable_if_variable_unused);
|
||||
return None;
|
||||
};
|
||||
|
||||
|
@ -116,13 +116,12 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
|
|||
let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() };
|
||||
builder.replace(desc.file_range.range, replacement)
|
||||
}
|
||||
builder.set_cursor(delete_range.start())
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
|
@ -149,7 +148,7 @@ fn foo() {
|
|||
r"
|
||||
fn bar(a: usize) {}
|
||||
fn foo() {
|
||||
<|>1 + 1;
|
||||
1 + 1;
|
||||
if 1 > 10 {
|
||||
}
|
||||
|
||||
|
@ -183,7 +182,7 @@ fn foo() {
|
|||
r"
|
||||
fn bar(a: usize) {}
|
||||
fn foo() {
|
||||
<|>(1 + 1) + 1;
|
||||
(1 + 1) + 1;
|
||||
if (1 + 1) > 10 {
|
||||
}
|
||||
|
||||
|
@ -217,7 +216,7 @@ fn foo() {
|
|||
r"
|
||||
fn bar(a: usize) {}
|
||||
fn foo() {
|
||||
<|>bar(1) + 1;
|
||||
bar(1) + 1;
|
||||
if bar(1) > 10 {
|
||||
}
|
||||
|
||||
|
@ -251,7 +250,7 @@ fn foo() {
|
|||
r"
|
||||
fn bar(a: usize): usize { a }
|
||||
fn foo() {
|
||||
<|>(bar(1) as u64) + 1;
|
||||
(bar(1) as u64) + 1;
|
||||
if (bar(1) as u64) > 10 {
|
||||
}
|
||||
|
||||
|
@ -283,7 +282,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>{ 10 + 1 } + 1;
|
||||
{ 10 + 1 } + 1;
|
||||
if { 10 + 1 } > 10 {
|
||||
}
|
||||
|
||||
|
@ -315,7 +314,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>( 10 + 1 ) + 1;
|
||||
( 10 + 1 ) + 1;
|
||||
if ( 10 + 1 ) > 10 {
|
||||
}
|
||||
|
||||
|
@ -330,7 +329,7 @@ fn foo() {
|
|||
|
||||
#[test]
|
||||
fn test_not_inline_mut_variable() {
|
||||
covers!(test_not_inline_mut_variable);
|
||||
mark::check!(test_not_inline_mut_variable);
|
||||
check_assist_not_applicable(
|
||||
inline_local_variable,
|
||||
r"
|
||||
|
@ -353,7 +352,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>let b = bar(10 + 1) * 10;
|
||||
let b = bar(10 + 1) * 10;
|
||||
let c = bar(10 + 1) as usize;
|
||||
}",
|
||||
);
|
||||
|
@ -373,7 +372,7 @@ fn foo() {
|
|||
r"
|
||||
fn foo() {
|
||||
let x = vec![1, 2, 3];
|
||||
<|>let b = x[0] * 10;
|
||||
let b = x[0] * 10;
|
||||
let c = x[0] as usize;
|
||||
}",
|
||||
);
|
||||
|
@ -393,7 +392,7 @@ fn foo() {
|
|||
r"
|
||||
fn foo() {
|
||||
let bar = vec![1];
|
||||
<|>let b = bar.len() * 10;
|
||||
let b = bar.len() * 10;
|
||||
let c = bar.len() as usize;
|
||||
}",
|
||||
);
|
||||
|
@ -421,7 +420,7 @@ struct Bar {
|
|||
|
||||
fn foo() {
|
||||
let bar = Bar { foo: 1 };
|
||||
<|>let b = bar.foo * 10;
|
||||
let b = bar.foo * 10;
|
||||
let c = bar.foo as usize;
|
||||
}",
|
||||
);
|
||||
|
@ -442,7 +441,7 @@ fn foo() -> Option<usize> {
|
|||
r"
|
||||
fn foo() -> Option<usize> {
|
||||
let bar = Some(1);
|
||||
<|>let b = bar? * 10;
|
||||
let b = bar? * 10;
|
||||
let c = bar? as usize;
|
||||
None
|
||||
}",
|
||||
|
@ -462,7 +461,7 @@ fn foo() {
|
|||
r"
|
||||
fn foo() {
|
||||
let bar = 10;
|
||||
<|>let b = &bar * 10;
|
||||
let b = &bar * 10;
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
@ -478,7 +477,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>let b = (10, 20)[0];
|
||||
let b = (10, 20)[0];
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
@ -494,7 +493,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>let b = [1, 2, 3].len();
|
||||
let b = [1, 2, 3].len();
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
@ -511,7 +510,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>let b = (10 + 20) * 10;
|
||||
let b = (10 + 20) * 10;
|
||||
let c = (10 + 20) as usize;
|
||||
}",
|
||||
);
|
||||
|
@ -531,7 +530,7 @@ fn foo() {
|
|||
r"
|
||||
fn foo() {
|
||||
let d = 10;
|
||||
<|>let b = d * 10;
|
||||
let b = d * 10;
|
||||
let c = d as usize;
|
||||
}",
|
||||
);
|
||||
|
@ -549,7 +548,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>let b = { 10 } * 10;
|
||||
let b = { 10 } * 10;
|
||||
let c = { 10 } as usize;
|
||||
}",
|
||||
);
|
||||
|
@ -569,7 +568,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>let b = (10 + 20) * 10;
|
||||
let b = (10 + 20) * 10;
|
||||
let c = (10 + 20, 20);
|
||||
let d = [10 + 20, 10];
|
||||
let e = (10 + 20);
|
||||
|
@ -588,7 +587,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>for i in vec![10, 20] {}
|
||||
for i in vec![10, 20] {}
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
@ -604,7 +603,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>while 1 > 0 {}
|
||||
while 1 > 0 {}
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
@ -622,7 +621,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>loop {
|
||||
loop {
|
||||
break 1 + 1;
|
||||
}
|
||||
}",
|
||||
|
@ -640,7 +639,7 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>return 1 > 0;
|
||||
return 1 > 0;
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
@ -656,14 +655,14 @@ fn foo() {
|
|||
}",
|
||||
r"
|
||||
fn foo() {
|
||||
<|>match 1 > 0 {}
|
||||
match 1 > 0 {}
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_applicable_if_variable_unused() {
|
||||
covers!(test_not_applicable_if_variable_unused);
|
||||
mark::check!(test_not_applicable_if_variable_unused);
|
||||
check_assist_not_applicable(
|
||||
inline_local_variable,
|
||||
r"
|
||||
|
@ -676,7 +675,7 @@ fn foo() {
|
|||
|
||||
#[test]
|
||||
fn not_applicable_outside_of_bind_pat() {
|
||||
covers!(not_applicable_outside_of_bind_pat);
|
||||
mark::check!(not_applicable_outside_of_bind_pat);
|
||||
check_assist_not_applicable(
|
||||
inline_local_variable,
|
||||
r"
|
||||
|
|
|
@ -4,10 +4,10 @@ use ra_syntax::{
|
|||
BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR,
|
||||
WHITESPACE,
|
||||
},
|
||||
SyntaxNode, TextSize,
|
||||
SyntaxNode,
|
||||
};
|
||||
use stdx::format_to;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
||||
|
@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
// ->
|
||||
// ```
|
||||
// fn main() {
|
||||
// let var_name = (1 + 2);
|
||||
// let $0var_name = (1 + 2);
|
||||
// var_name * 4;
|
||||
// }
|
||||
// ```
|
||||
|
@ -33,7 +33,7 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
|
|||
}
|
||||
let node = ctx.covering_element();
|
||||
if node.kind() == COMMENT {
|
||||
tested_by!(introduce_var_in_comment_is_not_applicable);
|
||||
mark::hit!(introduce_var_in_comment_is_not_applicable);
|
||||
return None;
|
||||
}
|
||||
let expr = node.ancestors().find_map(valid_target_expr)?;
|
||||
|
@ -46,14 +46,13 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
|
|||
acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| {
|
||||
let mut buf = String::new();
|
||||
|
||||
let cursor_offset = if wrap_in_block {
|
||||
if wrap_in_block {
|
||||
buf.push_str("{ let var_name = ");
|
||||
TextSize::of("{ let ")
|
||||
} else {
|
||||
buf.push_str("let var_name = ");
|
||||
TextSize::of("let ")
|
||||
};
|
||||
format_to!(buf, "{}", expr.syntax());
|
||||
|
||||
let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
|
||||
let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
|
||||
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
|
||||
|
@ -61,32 +60,47 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
|
|||
false
|
||||
};
|
||||
if is_full_stmt {
|
||||
tested_by!(test_introduce_var_expr_stmt);
|
||||
mark::hit!(test_introduce_var_expr_stmt);
|
||||
if full_stmt.unwrap().semicolon_token().is_none() {
|
||||
buf.push_str(";");
|
||||
}
|
||||
edit.replace(expr.syntax().text_range(), buf);
|
||||
} else {
|
||||
buf.push_str(";");
|
||||
|
||||
// We want to maintain the indent level,
|
||||
// but we do not want to duplicate possible
|
||||
// extra newlines in the indent block
|
||||
let text = indent.text();
|
||||
if text.starts_with('\n') {
|
||||
buf.push_str("\n");
|
||||
buf.push_str(text.trim_start_matches('\n'));
|
||||
} else {
|
||||
buf.push_str(text);
|
||||
}
|
||||
|
||||
edit.replace(expr.syntax().text_range(), "var_name".to_string());
|
||||
edit.insert(anchor_stmt.text_range().start(), buf);
|
||||
if wrap_in_block {
|
||||
edit.insert(anchor_stmt.text_range().end(), " }");
|
||||
let offset = expr.syntax().text_range();
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let snip = buf.replace("let var_name", "let $0var_name");
|
||||
edit.replace_snippet(cap, offset, snip)
|
||||
}
|
||||
None => edit.replace(offset, buf),
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buf.push_str(";");
|
||||
|
||||
// We want to maintain the indent level,
|
||||
// but we do not want to duplicate possible
|
||||
// extra newlines in the indent block
|
||||
let text = indent.text();
|
||||
if text.starts_with('\n') {
|
||||
buf.push_str("\n");
|
||||
buf.push_str(text.trim_start_matches('\n'));
|
||||
} else {
|
||||
buf.push_str(text);
|
||||
}
|
||||
|
||||
edit.replace(expr.syntax().text_range(), "var_name".to_string());
|
||||
let offset = anchor_stmt.text_range().start();
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let snip = buf.replace("let var_name", "let $0var_name");
|
||||
edit.insert_snippet(cap, offset, snip)
|
||||
}
|
||||
None => edit.insert(offset, buf),
|
||||
}
|
||||
|
||||
if wrap_in_block {
|
||||
edit.insert(anchor_stmt.text_range().end(), " }");
|
||||
}
|
||||
edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -113,7 +127,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
|
|||
expr.syntax().ancestors().find_map(|node| {
|
||||
if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) {
|
||||
if expr.syntax() == &node {
|
||||
tested_by!(test_introduce_var_last_expr);
|
||||
mark::hit!(test_introduce_var_last_expr);
|
||||
return Some((node, false));
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +148,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||
|
||||
|
@ -144,37 +158,37 @@ mod tests {
|
|||
fn test_introduce_var_simple() {
|
||||
check_assist(
|
||||
introduce_variable,
|
||||
"
|
||||
r#"
|
||||
fn foo() {
|
||||
foo(<|>1 + 1<|>);
|
||||
}",
|
||||
"
|
||||
}"#,
|
||||
r#"
|
||||
fn foo() {
|
||||
let <|>var_name = 1 + 1;
|
||||
let $0var_name = 1 + 1;
|
||||
foo(var_name);
|
||||
}",
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn introduce_var_in_comment_is_not_applicable() {
|
||||
covers!(introduce_var_in_comment_is_not_applicable);
|
||||
mark::check!(introduce_var_in_comment_is_not_applicable);
|
||||
check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_introduce_var_expr_stmt() {
|
||||
covers!(test_introduce_var_expr_stmt);
|
||||
mark::check!(test_introduce_var_expr_stmt);
|
||||
check_assist(
|
||||
introduce_variable,
|
||||
"
|
||||
r#"
|
||||
fn foo() {
|
||||
<|>1 + 1<|>;
|
||||
}",
|
||||
"
|
||||
}"#,
|
||||
r#"
|
||||
fn foo() {
|
||||
let <|>var_name = 1 + 1;
|
||||
}",
|
||||
let $0var_name = 1 + 1;
|
||||
}"#,
|
||||
);
|
||||
check_assist(
|
||||
introduce_variable,
|
||||
|
@ -185,7 +199,7 @@ fn foo() {
|
|||
}",
|
||||
"
|
||||
fn foo() {
|
||||
let <|>var_name = { let x = 0; x };
|
||||
let $0var_name = { let x = 0; x };
|
||||
something_else();
|
||||
}",
|
||||
);
|
||||
|
@ -201,7 +215,7 @@ fn foo() {
|
|||
}",
|
||||
"
|
||||
fn foo() {
|
||||
let <|>var_name = 1;
|
||||
let $0var_name = 1;
|
||||
var_name + 1;
|
||||
}",
|
||||
);
|
||||
|
@ -209,7 +223,7 @@ fn foo() {
|
|||
|
||||
#[test]
|
||||
fn test_introduce_var_last_expr() {
|
||||
covers!(test_introduce_var_last_expr);
|
||||
mark::check!(test_introduce_var_last_expr);
|
||||
check_assist(
|
||||
introduce_variable,
|
||||
"
|
||||
|
@ -218,7 +232,7 @@ fn foo() {
|
|||
}",
|
||||
"
|
||||
fn foo() {
|
||||
let <|>var_name = 1 + 1;
|
||||
let $0var_name = 1 + 1;
|
||||
bar(var_name)
|
||||
}",
|
||||
);
|
||||
|
@ -230,7 +244,7 @@ fn foo() {
|
|||
}",
|
||||
"
|
||||
fn foo() {
|
||||
let <|>var_name = bar(1 + 1);
|
||||
let $0var_name = bar(1 + 1);
|
||||
var_name
|
||||
}",
|
||||
)
|
||||
|
@ -253,7 +267,7 @@ fn main() {
|
|||
fn main() {
|
||||
let x = true;
|
||||
let tuple = match x {
|
||||
true => { let <|>var_name = 2 + 2; (var_name, true) }
|
||||
true => { let $0var_name = 2 + 2; (var_name, true) }
|
||||
_ => (0, false)
|
||||
};
|
||||
}
|
||||
|
@ -283,7 +297,7 @@ fn main() {
|
|||
let tuple = match x {
|
||||
true => {
|
||||
let y = 1;
|
||||
let <|>var_name = 2 + y;
|
||||
let $0var_name = 2 + y;
|
||||
(var_name, true)
|
||||
}
|
||||
_ => (0, false)
|
||||
|
@ -304,7 +318,7 @@ fn main() {
|
|||
",
|
||||
"
|
||||
fn main() {
|
||||
let lambda = |x: u32| { let <|>var_name = x * 2; var_name };
|
||||
let lambda = |x: u32| { let $0var_name = x * 2; var_name };
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -321,7 +335,7 @@ fn main() {
|
|||
",
|
||||
"
|
||||
fn main() {
|
||||
let lambda = |x: u32| { let <|>var_name = x * 2; var_name };
|
||||
let lambda = |x: u32| { let $0var_name = x * 2; var_name };
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -338,7 +352,7 @@ fn main() {
|
|||
",
|
||||
"
|
||||
fn main() {
|
||||
let <|>var_name = Some(true);
|
||||
let $0var_name = Some(true);
|
||||
let o = var_name;
|
||||
}
|
||||
",
|
||||
|
@ -356,7 +370,7 @@ fn main() {
|
|||
",
|
||||
"
|
||||
fn main() {
|
||||
let <|>var_name = bar.foo();
|
||||
let $0var_name = bar.foo();
|
||||
let v = var_name;
|
||||
}
|
||||
",
|
||||
|
@ -374,7 +388,7 @@ fn foo() -> u32 {
|
|||
",
|
||||
"
|
||||
fn foo() -> u32 {
|
||||
let <|>var_name = 2 + 2;
|
||||
let $0var_name = 2 + 2;
|
||||
return var_name;
|
||||
}
|
||||
",
|
||||
|
@ -396,7 +410,7 @@ fn foo() -> u32 {
|
|||
fn foo() -> u32 {
|
||||
|
||||
|
||||
let <|>var_name = 2 + 2;
|
||||
let $0var_name = 2 + 2;
|
||||
return var_name;
|
||||
}
|
||||
",
|
||||
|
@ -413,7 +427,7 @@ fn foo() -> u32 {
|
|||
"
|
||||
fn foo() -> u32 {
|
||||
|
||||
let <|>var_name = 2 + 2;
|
||||
let $0var_name = 2 + 2;
|
||||
return var_name;
|
||||
}
|
||||
",
|
||||
|
@ -438,7 +452,7 @@ fn foo() -> u32 {
|
|||
// bar
|
||||
|
||||
|
||||
let <|>var_name = 2 + 2;
|
||||
let $0var_name = 2 + 2;
|
||||
return var_name;
|
||||
}
|
||||
",
|
||||
|
@ -459,7 +473,7 @@ fn main() {
|
|||
"
|
||||
fn main() {
|
||||
let result = loop {
|
||||
let <|>var_name = 2 + 2;
|
||||
let $0var_name = 2 + 2;
|
||||
break var_name;
|
||||
};
|
||||
}
|
||||
|
@ -478,7 +492,7 @@ fn main() {
|
|||
",
|
||||
"
|
||||
fn main() {
|
||||
let <|>var_name = 0f32 as u32;
|
||||
let $0var_name = 0f32 as u32;
|
||||
let v = var_name;
|
||||
}
|
||||
",
|
||||
|
|
|
@ -72,7 +72,7 @@ mod tests {
|
|||
check_assist(
|
||||
invert_if,
|
||||
"fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }",
|
||||
"fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }",
|
||||
"fn f() { if x == 3 { 3 + 2 } else { 1 } }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ mod tests {
|
|||
check_assist(
|
||||
invert_if,
|
||||
"fn f() { <|>if !cond { 3 * 2 } else { 1 } }",
|
||||
"fn f() { <|>if cond { 1 } else { 3 * 2 } }",
|
||||
"fn f() { if cond { 1 } else { 3 * 2 } }",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ mod tests {
|
|||
check_assist(
|
||||
invert_if,
|
||||
"fn f() { i<|>f cond { 3 * 2 } else { 1 } }",
|
||||
"fn f() { i<|>f !cond { 1 } else { 3 * 2 } }",
|
||||
"fn f() { if !cond { 1 } else { 3 * 2 } }",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -58,8 +58,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
|
|||
let target = tree.syntax().text_range();
|
||||
acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| {
|
||||
builder.rewrite(rewriter);
|
||||
// FIXME: we only need because our diff is imprecise
|
||||
builder.set_cursor(offset);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -142,7 +140,7 @@ use std::fmt<|>::Debug;
|
|||
use std::fmt::Display;
|
||||
",
|
||||
r"
|
||||
use std::fmt<|>::{Debug, Display};
|
||||
use std::fmt::{Debug, Display};
|
||||
",
|
||||
)
|
||||
}
|
||||
|
@ -156,7 +154,7 @@ use std::fmt::Debug;
|
|||
use std::fmt<|>::Display;
|
||||
",
|
||||
r"
|
||||
use std::fmt:<|>:{Display, Debug};
|
||||
use std::fmt::{Display, Debug};
|
||||
",
|
||||
);
|
||||
}
|
||||
|
@ -169,7 +167,7 @@ use std::fmt:<|>:{Display, Debug};
|
|||
use std::{fmt<|>::Debug, fmt::Display};
|
||||
",
|
||||
r"
|
||||
use std::{fmt<|>::{Debug, Display}};
|
||||
use std::{fmt::{Debug, Display}};
|
||||
",
|
||||
);
|
||||
check_assist(
|
||||
|
@ -178,7 +176,7 @@ use std::{fmt<|>::{Debug, Display}};
|
|||
use std::{fmt::Debug, fmt<|>::Display};
|
||||
",
|
||||
r"
|
||||
use std::{fmt::<|>{Display, Debug}};
|
||||
use std::{fmt::{Display, Debug}};
|
||||
",
|
||||
);
|
||||
}
|
||||
|
@ -192,7 +190,7 @@ use std<|>::cell::*;
|
|||
use std::str;
|
||||
",
|
||||
r"
|
||||
use std<|>::{cell::*, str};
|
||||
use std::{cell::*, str};
|
||||
",
|
||||
)
|
||||
}
|
||||
|
@ -206,7 +204,7 @@ use std<|>::cell::*;
|
|||
use std::str::*;
|
||||
",
|
||||
r"
|
||||
use std<|>::{cell::*, str::*};
|
||||
use std::{cell::*, str::*};
|
||||
",
|
||||
)
|
||||
}
|
||||
|
@ -222,7 +220,7 @@ use foo::baz;
|
|||
/// Doc comment
|
||||
",
|
||||
r"
|
||||
use foo<|>::{bar, baz};
|
||||
use foo::{bar, baz};
|
||||
|
||||
/// Doc comment
|
||||
",
|
||||
|
@ -241,7 +239,7 @@ use {
|
|||
",
|
||||
r"
|
||||
use {
|
||||
foo<|>::{bar, baz},
|
||||
foo::{bar, baz},
|
||||
};
|
||||
",
|
||||
);
|
||||
|
@ -255,7 +253,7 @@ use {
|
|||
",
|
||||
r"
|
||||
use {
|
||||
foo::{bar<|>, baz},
|
||||
foo::{bar, baz},
|
||||
};
|
||||
",
|
||||
);
|
||||
|
@ -272,7 +270,7 @@ use foo::<|>{
|
|||
};
|
||||
",
|
||||
r"
|
||||
use foo::{<|>
|
||||
use foo::{
|
||||
FooBar,
|
||||
bar::baz};
|
||||
",
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::iter::successors;
|
|||
use ra_syntax::{
|
||||
algo::neighbor,
|
||||
ast::{self, AstNode},
|
||||
Direction, TextSize,
|
||||
Direction,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists, TextRange};
|
||||
|
@ -41,17 +41,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
|
|||
let current_expr = current_arm.expr()?;
|
||||
let current_text_range = current_arm.syntax().text_range();
|
||||
|
||||
enum CursorPos {
|
||||
InExpr(TextSize),
|
||||
InPat(TextSize),
|
||||
}
|
||||
let cursor_pos = ctx.offset();
|
||||
let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) {
|
||||
CursorPos::InExpr(current_text_range.end() - cursor_pos)
|
||||
} else {
|
||||
CursorPos::InPat(cursor_pos)
|
||||
};
|
||||
|
||||
// We check if the following match arms match this one. We could, but don't,
|
||||
// compare to the previous match arm as well.
|
||||
let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next))
|
||||
|
@ -87,10 +76,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
|
|||
let start = arms_to_merge.first().unwrap().syntax().text_range().start();
|
||||
let end = arms_to_merge.last().unwrap().syntax().text_range().end();
|
||||
|
||||
edit.set_cursor(match cursor_pos {
|
||||
CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset,
|
||||
CursorPos::InPat(offset) => offset,
|
||||
});
|
||||
edit.replace(TextRange::new(start, end), arm);
|
||||
})
|
||||
}
|
||||
|
@ -132,7 +117,7 @@ mod tests {
|
|||
fn main() {
|
||||
let x = X::A;
|
||||
let y = match x {
|
||||
X::A | X::B => { 1i32<|> }
|
||||
X::A | X::B => { 1i32 }
|
||||
X::C => { 2i32 }
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +149,7 @@ mod tests {
|
|||
fn main() {
|
||||
let x = X::A;
|
||||
let y = match x {
|
||||
X::A | X::B | X::C | X::D => {<|> 1i32 },
|
||||
X::A | X::B | X::C | X::D => { 1i32 },
|
||||
X::E => { 2i32 },
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +182,7 @@ mod tests {
|
|||
let x = X::A;
|
||||
let y = match x {
|
||||
X::A => { 1i32 },
|
||||
_ => { 2i<|>32 }
|
||||
_ => { 2i32 }
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -226,7 +211,7 @@ mod tests {
|
|||
|
||||
fn main() {
|
||||
match X::A {
|
||||
X::A<|> | X::B | X::C => 92,
|
||||
X::A | X::B | X::C => 92,
|
||||
X::D => 62,
|
||||
_ => panic!(),
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ mod tests {
|
|||
fn foo<T: u32, <|>F: FnOnce(T) -> T>() {}
|
||||
"#,
|
||||
r#"
|
||||
fn foo<T, <|>F>() where T: u32, F: FnOnce(T) -> T {}
|
||||
fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ mod tests {
|
|||
impl<U: u32, <|>T> A<U, T> {}
|
||||
"#,
|
||||
r#"
|
||||
impl<U, <|>T> A<U, T> where U: u32 {}
|
||||
impl<U, T> A<U, T> where U: u32 {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ mod tests {
|
|||
struct A<<|>T: Iterator<Item = u32>> {}
|
||||
"#,
|
||||
r#"
|
||||
struct A<<|>T> where T: Iterator<Item = u32> {}
|
||||
struct A<T> where T: Iterator<Item = u32> {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ mod tests {
|
|||
struct Pair<<|>T: u32>(T, T);
|
||||
"#,
|
||||
r#"
|
||||
struct Pair<<|>T>(T, T) where T: u32;
|
||||
struct Pair<T>(T, T) where T: u32;
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use ra_syntax::{
|
||||
ast,
|
||||
ast::{AstNode, AstToken, IfExpr, MatchArm},
|
||||
TextSize,
|
||||
ast::{AstNode, IfExpr, MatchArm},
|
||||
SyntaxKind::WHITESPACE,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
@ -42,24 +41,15 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
|
|||
|
||||
let target = guard.syntax().text_range();
|
||||
acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| {
|
||||
let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) {
|
||||
Some(tok) => {
|
||||
if ast::Whitespace::cast(tok.clone()).is_some() {
|
||||
let ele = tok.text_range();
|
||||
edit.delete(ele);
|
||||
ele.len()
|
||||
} else {
|
||||
TextSize::from(0)
|
||||
}
|
||||
match space_before_guard {
|
||||
Some(element) if element.kind() == WHITESPACE => {
|
||||
edit.delete(element.text_range());
|
||||
}
|
||||
_ => TextSize::from(0),
|
||||
_ => (),
|
||||
};
|
||||
|
||||
edit.delete(guard.syntax().text_range());
|
||||
edit.replace_node_and_indent(arm_expr.syntax(), buf);
|
||||
edit.set_cursor(
|
||||
arm_expr.syntax().text_range().start() + TextSize::from(3) - offseting_amount,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -124,7 +114,6 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
|
|||
}
|
||||
|
||||
edit.insert(match_pat.syntax().text_range().end(), buf);
|
||||
edit.set_cursor(match_pat.syntax().text_range().end() + TextSize::from(1));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -172,7 +161,7 @@ mod tests {
|
|||
let t = 'a';
|
||||
let chars = "abcd";
|
||||
match t {
|
||||
'\r' => if chars.clone().next() == Some('\n') { <|>false },
|
||||
'\r' => if chars.clone().next() == Some('\n') { false },
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +184,7 @@ mod tests {
|
|||
r#"
|
||||
fn f() {
|
||||
match x {
|
||||
y @ 4 | y @ 5 => if y > 5 { <|>true },
|
||||
y @ 4 | y @ 5 => if y > 5 { true },
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +211,7 @@ mod tests {
|
|||
let t = 'a';
|
||||
let chars = "abcd";
|
||||
match t {
|
||||
'\r' <|>if chars.clone().next() == Some('\n') => false,
|
||||
'\r' if chars.clone().next() == Some('\n') => false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +255,7 @@ mod tests {
|
|||
let t = 'a';
|
||||
let chars = "abcd";
|
||||
match t {
|
||||
'\r' <|>if chars.clone().next().is_some() => { },
|
||||
'\r' if chars.clone().next().is_some() => { },
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +285,7 @@ mod tests {
|
|||
let mut t = 'a';
|
||||
let chars = "abcd";
|
||||
match t {
|
||||
'\r' <|>if chars.clone().next().is_some() => {
|
||||
'\r' if chars.clone().next().is_some() => {
|
||||
t = 'e';
|
||||
false
|
||||
},
|
||||
|
|
|
@ -164,7 +164,7 @@ mod test {
|
|||
"#,
|
||||
r##"
|
||||
fn f() {
|
||||
let s = <|>r#"random
|
||||
let s = r#"random
|
||||
string"#;
|
||||
}
|
||||
"##,
|
||||
|
@ -182,7 +182,7 @@ string"#;
|
|||
"#,
|
||||
r##"
|
||||
fn f() {
|
||||
format!(<|>r#"x = {}"#, 92)
|
||||
format!(r#"x = {}"#, 92)
|
||||
}
|
||||
"##,
|
||||
)
|
||||
|
@ -199,7 +199,7 @@ string"#;
|
|||
"###,
|
||||
r####"
|
||||
fn f() {
|
||||
let s = <|>r#"#random##
|
||||
let s = r#"#random##
|
||||
string"#;
|
||||
}
|
||||
"####,
|
||||
|
@ -217,7 +217,7 @@ string"#;
|
|||
"###,
|
||||
r####"
|
||||
fn f() {
|
||||
let s = <|>r###"#random"##
|
||||
let s = r###"#random"##
|
||||
string"###;
|
||||
}
|
||||
"####,
|
||||
|
@ -235,7 +235,7 @@ string"###;
|
|||
"#,
|
||||
r##"
|
||||
fn f() {
|
||||
let s = <|>r#"random string"#;
|
||||
let s = r#"random string"#;
|
||||
}
|
||||
"##,
|
||||
)
|
||||
|
@ -289,7 +289,7 @@ string"###;
|
|||
"#,
|
||||
r##"
|
||||
fn f() {
|
||||
let s = <|>r#"random string"#;
|
||||
let s = r#"random string"#;
|
||||
}
|
||||
"##,
|
||||
)
|
||||
|
@ -306,7 +306,7 @@ string"###;
|
|||
"##,
|
||||
r###"
|
||||
fn f() {
|
||||
let s = <|>r##"random"string"##;
|
||||
let s = r##"random"string"##;
|
||||
}
|
||||
"###,
|
||||
)
|
||||
|
@ -348,7 +348,7 @@ string"###;
|
|||
"##,
|
||||
r#"
|
||||
fn f() {
|
||||
let s = <|>r"random string";
|
||||
let s = r"random string";
|
||||
}
|
||||
"#,
|
||||
)
|
||||
|
@ -365,7 +365,7 @@ string"###;
|
|||
"##,
|
||||
r#"
|
||||
fn f() {
|
||||
let s = <|>r"random\"str\"ing";
|
||||
let s = r"random\"str\"ing";
|
||||
}
|
||||
"#,
|
||||
)
|
||||
|
@ -382,7 +382,7 @@ string"###;
|
|||
"###,
|
||||
r##"
|
||||
fn f() {
|
||||
let s = <|>r#"random string"#;
|
||||
let s = r#"random string"#;
|
||||
}
|
||||
"##,
|
||||
)
|
||||
|
@ -436,7 +436,7 @@ string"###;
|
|||
"##,
|
||||
r#"
|
||||
fn f() {
|
||||
let s = <|>"random string";
|
||||
let s = "random string";
|
||||
}
|
||||
"#,
|
||||
)
|
||||
|
@ -453,7 +453,7 @@ string"###;
|
|||
"##,
|
||||
r#"
|
||||
fn f() {
|
||||
let s = <|>"random\"str\"ing";
|
||||
let s = "random\"str\"ing";
|
||||
}
|
||||
"#,
|
||||
)
|
||||
|
@ -470,7 +470,7 @@ string"###;
|
|||
"###,
|
||||
r##"
|
||||
fn f() {
|
||||
let s = <|>"random string";
|
||||
let s = "random string";
|
||||
}
|
||||
"##,
|
||||
)
|
||||
|
|
|
@ -29,26 +29,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
|
||||
let macro_range = macro_call.syntax().text_range();
|
||||
|
||||
// If the cursor is inside the macro call, we'll try to maintain the cursor
|
||||
// position by subtracting the length of dbg!( from the start of the file
|
||||
// range, otherwise we'll default to using the start of the macro call
|
||||
let cursor_pos = {
|
||||
let file_range = ctx.frange.range;
|
||||
|
||||
let offset_start = file_range
|
||||
.start()
|
||||
.checked_sub(macro_range.start())
|
||||
.unwrap_or_else(|| TextSize::from(0));
|
||||
|
||||
let dbg_size = TextSize::of("dbg!(");
|
||||
|
||||
if offset_start > dbg_size {
|
||||
file_range.start() - dbg_size
|
||||
} else {
|
||||
macro_range.start()
|
||||
}
|
||||
};
|
||||
|
||||
let macro_content = {
|
||||
let macro_args = macro_call.token_tree()?.syntax().clone();
|
||||
|
||||
|
@ -58,9 +38,8 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
};
|
||||
|
||||
let target = macro_call.syntax().text_range();
|
||||
acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| {
|
||||
edit.replace(macro_range, macro_content);
|
||||
edit.set_cursor(cursor_pos);
|
||||
acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| {
|
||||
builder.replace(macro_range, macro_content);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -94,13 +73,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_remove_dbg() {
|
||||
check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1");
|
||||
check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");
|
||||
|
||||
check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)");
|
||||
check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");
|
||||
|
||||
check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1");
|
||||
check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");
|
||||
|
||||
check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1");
|
||||
check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");
|
||||
|
||||
check_assist(
|
||||
remove_dbg,
|
||||
|
@ -113,7 +92,7 @@ fn foo(n: usize) {
|
|||
",
|
||||
"
|
||||
fn foo(n: usize) {
|
||||
if let Some(_) = n.<|>checked_sub(4) {
|
||||
if let Some(_) = n.checked_sub(4) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
@ -122,8 +101,8 @@ fn foo(n: usize) {
|
|||
}
|
||||
#[test]
|
||||
fn test_remove_dbg_with_brackets_and_braces() {
|
||||
check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1");
|
||||
check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1");
|
||||
check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
|
||||
check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -26,8 +26,7 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||
};
|
||||
|
||||
let target = mut_token.text_range();
|
||||
acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| {
|
||||
edit.set_cursor(delete_from);
|
||||
edit.delete(TextRange::new(delete_from, delete_to));
|
||||
acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| {
|
||||
builder.delete(TextRange::new(delete_from, delete_to));
|
||||
})
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ mod tests {
|
|||
"#,
|
||||
r#"
|
||||
struct Foo {foo: i32, bar: i32};
|
||||
const test: Foo = <|>Foo {foo: 1, bar: 0}
|
||||
const test: Foo = Foo {foo: 1, bar: 0}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ mod tests {
|
|||
|
||||
fn f(f: Foo) -> {
|
||||
match f {
|
||||
<|>Foo { ref mut bar, baz: 0, .. } => (),
|
||||
Foo { ref mut bar, baz: 0, .. } => (),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ mod tests {
|
|||
impl Foo {
|
||||
fn new() -> Foo {
|
||||
let foo = String::new();
|
||||
<|>Foo {
|
||||
Foo {
|
||||
foo,
|
||||
bar: foo.clone(),
|
||||
extra: "Extra field",
|
||||
|
|
|
@ -68,7 +68,6 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
|
|||
.indent(IndentLevel::from_node(if_expr.syntax()))
|
||||
};
|
||||
|
||||
edit.set_cursor(if_expr.syntax().text_range().start());
|
||||
edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
|
||||
})
|
||||
}
|
||||
|
@ -83,7 +82,7 @@ mod tests {
|
|||
fn test_replace_if_let_with_match_unwraps_simple_expressions() {
|
||||
check_assist(
|
||||
replace_if_let_with_match,
|
||||
"
|
||||
r#"
|
||||
impl VariantData {
|
||||
pub fn is_struct(&self) -> bool {
|
||||
if <|>let VariantData::Struct(..) = *self {
|
||||
|
@ -92,16 +91,16 @@ impl VariantData {
|
|||
false
|
||||
}
|
||||
}
|
||||
} ",
|
||||
"
|
||||
} "#,
|
||||
r#"
|
||||
impl VariantData {
|
||||
pub fn is_struct(&self) -> bool {
|
||||
<|>match *self {
|
||||
match *self {
|
||||
VariantData::Struct(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
} ",
|
||||
} "#,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -109,7 +108,7 @@ impl VariantData {
|
|||
fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() {
|
||||
check_assist(
|
||||
replace_if_let_with_match,
|
||||
"
|
||||
r#"
|
||||
fn foo() {
|
||||
if <|>let VariantData::Struct(..) = a {
|
||||
bar(
|
||||
|
@ -118,10 +117,10 @@ fn foo() {
|
|||
} else {
|
||||
false
|
||||
}
|
||||
} ",
|
||||
"
|
||||
} "#,
|
||||
r#"
|
||||
fn foo() {
|
||||
<|>match a {
|
||||
match a {
|
||||
VariantData::Struct(..) => {
|
||||
bar(
|
||||
123
|
||||
|
@ -129,7 +128,7 @@ fn foo() {
|
|||
}
|
||||
_ => false,
|
||||
}
|
||||
} ",
|
||||
} "#,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -137,7 +136,7 @@ fn foo() {
|
|||
fn replace_if_let_with_match_target() {
|
||||
check_assist_target(
|
||||
replace_if_let_with_match,
|
||||
"
|
||||
r#"
|
||||
impl VariantData {
|
||||
pub fn is_struct(&self) -> bool {
|
||||
if <|>let VariantData::Struct(..) = *self {
|
||||
|
@ -146,7 +145,7 @@ impl VariantData {
|
|||
false
|
||||
}
|
||||
}
|
||||
} ",
|
||||
} "#,
|
||||
"if let VariantData::Struct(..) = *self {
|
||||
true
|
||||
} else {
|
||||
|
@ -176,7 +175,7 @@ enum Option<T> { Some(T), None }
|
|||
use Option::*;
|
||||
|
||||
fn foo(x: Option<i32>) {
|
||||
<|>match x {
|
||||
match x {
|
||||
Some(x) => println!("{}", x),
|
||||
None => println!("none"),
|
||||
}
|
||||
|
@ -206,7 +205,7 @@ enum Result<T, E> { Ok(T), Err(E) }
|
|||
use Result::*;
|
||||
|
||||
fn foo(x: Result<i32, ()>) {
|
||||
<|>match x {
|
||||
match x {
|
||||
Ok(x) => println!("{}", x),
|
||||
Err(_) => println!("none"),
|
||||
}
|
||||
|
|
|
@ -58,12 +58,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
|
|||
let stmt = make::expr_stmt(if_);
|
||||
|
||||
let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
|
||||
let target_offset =
|
||||
let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start();
|
||||
let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
|
||||
|
||||
edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
|
||||
edit.set_cursor(target_offset);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -88,7 +85,7 @@ fn main() {
|
|||
enum E<T> { X(T), Y(T) }
|
||||
|
||||
fn main() {
|
||||
if let <|>x = E::X(92) {
|
||||
if let x = E::X(92) {
|
||||
}
|
||||
}
|
||||
",
|
||||
|
|
|
@ -39,7 +39,7 @@ pub(crate) fn replace_qualified_name_with_use(
|
|||
target,
|
||||
|builder| {
|
||||
let path_to_import = hir_path.mod_path().clone();
|
||||
insert_use_statement(path.syntax(), &path_to_import, ctx, builder);
|
||||
insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
|
||||
|
||||
if let Some(last) = path.segment() {
|
||||
// Here we are assuming the assist will provide a correct use statement
|
||||
|
@ -89,7 +89,7 @@ std::fmt::Debug<|>
|
|||
"
|
||||
use std::fmt::Debug;
|
||||
|
||||
Debug<|>
|
||||
Debug
|
||||
",
|
||||
);
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ fn main() {
|
|||
"
|
||||
use std::fmt::Debug;
|
||||
|
||||
Debug<|>
|
||||
Debug
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ use std::fmt::Debug;
|
|||
fn main() {
|
||||
}
|
||||
|
||||
Debug<|>
|
||||
Debug
|
||||
",
|
||||
);
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ std::fmt<|>::Debug
|
|||
"
|
||||
use std::fmt;
|
||||
|
||||
fmt<|>::Debug
|
||||
fmt::Debug
|
||||
",
|
||||
);
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ impl std::fmt::Debug<|> for Foo {
|
|||
use stdx;
|
||||
use std::fmt::Debug;
|
||||
|
||||
impl Debug<|> for Foo {
|
||||
impl Debug for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -181,7 +181,7 @@ impl std::fmt::Debug<|> for Foo {
|
|||
"
|
||||
use std::fmt::Debug;
|
||||
|
||||
impl Debug<|> for Foo {
|
||||
impl Debug for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -198,7 +198,7 @@ impl Debug<|> for Foo {
|
|||
"
|
||||
use std::fmt::Debug;
|
||||
|
||||
impl Debug<|> for Foo {
|
||||
impl Debug for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -217,7 +217,7 @@ impl std::io<|> for Foo {
|
|||
"
|
||||
use std::{io, fmt};
|
||||
|
||||
impl io<|> for Foo {
|
||||
impl io for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -236,7 +236,7 @@ impl std::fmt::Debug<|> for Foo {
|
|||
"
|
||||
use std::fmt::{self, Debug, };
|
||||
|
||||
impl Debug<|> for Foo {
|
||||
impl Debug for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -255,7 +255,7 @@ impl std::fmt<|> for Foo {
|
|||
"
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
impl fmt<|> for Foo {
|
||||
impl fmt for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -274,7 +274,7 @@ impl std::fmt::nested<|> for Foo {
|
|||
"
|
||||
use std::fmt::{Debug, nested::{Display, self}};
|
||||
|
||||
impl nested<|> for Foo {
|
||||
impl nested for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -293,7 +293,7 @@ impl std::fmt::nested<|> for Foo {
|
|||
"
|
||||
use std::fmt::{Debug, nested::{self, Display}};
|
||||
|
||||
impl nested<|> for Foo {
|
||||
impl nested for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -312,7 +312,7 @@ impl std::fmt::nested::Debug<|> for Foo {
|
|||
"
|
||||
use std::fmt::{Debug, nested::{Display, Debug}};
|
||||
|
||||
impl Debug<|> for Foo {
|
||||
impl Debug for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -331,7 +331,7 @@ impl std::fmt::nested::Display<|> for Foo {
|
|||
"
|
||||
use std::fmt::{nested::Display, Debug};
|
||||
|
||||
impl Display<|> for Foo {
|
||||
impl Display for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -350,7 +350,7 @@ impl std::fmt::Display<|> for Foo {
|
|||
"
|
||||
use std::fmt::{Display, nested::Debug};
|
||||
|
||||
impl Display<|> for Foo {
|
||||
impl Display for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -374,7 +374,7 @@ use crate::{
|
|||
AssocItem,
|
||||
};
|
||||
|
||||
fn foo() { lower<|>::trait_env() }
|
||||
fn foo() { lower::trait_env() }
|
||||
",
|
||||
);
|
||||
}
|
||||
|
@ -392,7 +392,7 @@ impl foo::Debug<|> for Foo {
|
|||
"
|
||||
use std::fmt as foo;
|
||||
|
||||
impl Debug<|> for Foo {
|
||||
impl Debug for Foo {
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -435,7 +435,7 @@ mod foo {
|
|||
mod bar {
|
||||
use std::fmt::Debug;
|
||||
|
||||
Debug<|>
|
||||
Debug
|
||||
}
|
||||
}
|
||||
",
|
||||
|
@ -458,7 +458,7 @@ fn main() {
|
|||
use std::fmt::Debug;
|
||||
|
||||
fn main() {
|
||||
Debug<|>
|
||||
Debug
|
||||
}
|
||||
",
|
||||
);
|
||||
|
|
|
@ -9,7 +9,10 @@ use ra_syntax::{
|
|||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
|
||||
use crate::{
|
||||
utils::{render_snippet, Cursor, TryEnum},
|
||||
AssistContext, AssistId, Assists,
|
||||
};
|
||||
|
||||
// Assist: replace_unwrap_with_match
|
||||
//
|
||||
|
@ -29,7 +32,7 @@ use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
|
|||
// let x: Result<i32, i32> = Result::Ok(92);
|
||||
// let y = match x {
|
||||
// Ok(a) => a,
|
||||
// _ => unreachable!(),
|
||||
// $0_ => unreachable!(),
|
||||
// };
|
||||
// }
|
||||
// ```
|
||||
|
@ -43,7 +46,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
|
|||
let ty = ctx.sema.type_of_expr(&caller)?;
|
||||
let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
|
||||
let target = method_call.syntax().text_range();
|
||||
acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| {
|
||||
acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| {
|
||||
let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
|
||||
let it = make::bind_pat(make::name("a")).into();
|
||||
let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
|
||||
|
@ -51,23 +54,37 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
|
|||
let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
|
||||
let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
|
||||
|
||||
let unreachable_call = make::unreachable_macro_call().into();
|
||||
let unreachable_call = make::expr_unreachable();
|
||||
let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
|
||||
|
||||
let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
|
||||
let match_expr = make::expr_match(caller.clone(), match_arm_list)
|
||||
.indent(IndentLevel::from_node(method_call.syntax()));
|
||||
|
||||
edit.set_cursor(caller.syntax().text_range().start());
|
||||
edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
|
||||
let range = method_call.syntax().text_range();
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let err_arm = match_expr
|
||||
.syntax()
|
||||
.descendants()
|
||||
.filter_map(ast::MatchArm::cast)
|
||||
.last()
|
||||
.unwrap();
|
||||
let snippet =
|
||||
render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
|
||||
builder.replace_snippet(cap, range, snippet)
|
||||
}
|
||||
None => builder.replace(range, match_expr.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::tests::{check_assist, check_assist_target};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_replace_result_unwrap_with_match() {
|
||||
check_assist(
|
||||
|
@ -85,9 +102,9 @@ enum Result<T, E> { Ok(T), Err(E) }
|
|||
fn i<T>(a: T) -> T { a }
|
||||
fn main() {
|
||||
let x: Result<i32, i32> = Result::Ok(92);
|
||||
let y = <|>match i(x) {
|
||||
let y = match i(x) {
|
||||
Ok(a) => a,
|
||||
_ => unreachable!(),
|
||||
$0_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
",
|
||||
|
@ -111,9 +128,9 @@ enum Option<T> { Some(T), None }
|
|||
fn i<T>(a: T) -> T { a }
|
||||
fn main() {
|
||||
let x = Option::Some(92);
|
||||
let y = <|>match i(x) {
|
||||
let y = match i(x) {
|
||||
Some(a) => a,
|
||||
_ => unreachable!(),
|
||||
$0_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
",
|
||||
|
@ -137,9 +154,9 @@ enum Result<T, E> { Ok(T), Err(E) }
|
|||
fn i<T>(a: T) -> T { a }
|
||||
fn main() {
|
||||
let x: Result<i32, i32> = Result::Ok(92);
|
||||
let y = <|>match i(x) {
|
||||
let y = match i(x) {
|
||||
Ok(a) => a,
|
||||
_ => unreachable!(),
|
||||
$0_ => unreachable!(),
|
||||
}.count_zeroes();
|
||||
}
|
||||
",
|
||||
|
|
|
@ -26,12 +26,10 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
if new_tree == use_tree {
|
||||
return None;
|
||||
}
|
||||
let cursor = ctx.offset();
|
||||
|
||||
let target = colon_colon.text_range();
|
||||
acc.add(AssistId("split_import"), "Split import", target, |edit| {
|
||||
edit.replace_ast(use_tree, new_tree);
|
||||
edit.set_cursor(cursor);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -46,7 +44,7 @@ mod tests {
|
|||
check_assist(
|
||||
split_import,
|
||||
"use crate::<|>db::RootDatabase;",
|
||||
"use crate::<|>{db::RootDatabase};",
|
||||
"use crate::{db::RootDatabase};",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -55,7 +53,7 @@ mod tests {
|
|||
check_assist(
|
||||
split_import,
|
||||
"use crate:<|>:db::{RootDatabase, FileSymbol}",
|
||||
"use crate:<|>:{db::{RootDatabase, FileSymbol}}",
|
||||
"use crate::{db::{RootDatabase, FileSymbol}}",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
||||
use ast::{ElseBranch, Expr, LoopBodyOwner};
|
||||
use ra_fmt::unwrap_trivial_block;
|
||||
use ra_syntax::{ast, match_ast, AstNode, TextRange, T};
|
||||
use ra_syntax::{
|
||||
ast::{self, ElseBranch, Expr, LoopBodyOwner},
|
||||
match_ast, AstNode, TextRange, T,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
||||
// Assist: unwrap_block
|
||||
//
|
||||
|
@ -60,7 +62,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
|
||||
let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
|
||||
|
||||
edit.set_cursor(ancestor_then_branch.syntax().text_range().end());
|
||||
edit.delete(range_to_del_rest);
|
||||
edit.delete(range_to_del_else_if);
|
||||
edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
|
||||
|
@ -77,7 +78,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
return acc.add(assist_id, assist_label, target, |edit| {
|
||||
let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
|
||||
|
||||
edit.set_cursor(then_branch.syntax().text_range().end());
|
||||
edit.delete(range_to_del);
|
||||
edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
|
||||
});
|
||||
|
@ -95,8 +95,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
|
||||
let target = expr_to_unwrap.syntax().text_range();
|
||||
acc.add(assist_id, assist_label, target, |edit| {
|
||||
edit.set_cursor(expr.syntax().text_range().start());
|
||||
|
||||
edit.replace(
|
||||
expr.syntax().text_range(),
|
||||
update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']),
|
||||
|
@ -152,7 +150,7 @@ mod tests {
|
|||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
<|>foo();
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
|
@ -186,7 +184,7 @@ mod tests {
|
|||
|
||||
//comment
|
||||
bar();
|
||||
}<|>
|
||||
}
|
||||
println!("bar");
|
||||
}
|
||||
"#,
|
||||
|
@ -220,7 +218,7 @@ mod tests {
|
|||
|
||||
//comment
|
||||
//bar();
|
||||
}<|>
|
||||
}
|
||||
println!("bar");
|
||||
}
|
||||
"#,
|
||||
|
@ -256,7 +254,7 @@ mod tests {
|
|||
//bar();
|
||||
} else if false {
|
||||
println!("bar");
|
||||
}<|>
|
||||
}
|
||||
println!("foo");
|
||||
}
|
||||
"#,
|
||||
|
@ -296,7 +294,7 @@ mod tests {
|
|||
println!("bar");
|
||||
} else if true {
|
||||
println!("foo");
|
||||
}<|>
|
||||
}
|
||||
println!("else");
|
||||
}
|
||||
"#,
|
||||
|
@ -334,7 +332,7 @@ mod tests {
|
|||
//bar();
|
||||
} else if false {
|
||||
println!("bar");
|
||||
}<|>
|
||||
}
|
||||
println!("foo");
|
||||
}
|
||||
"#,
|
||||
|
@ -381,7 +379,7 @@ mod tests {
|
|||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
|
@ -415,7 +413,7 @@ mod tests {
|
|||
r#"
|
||||
fn main() {
|
||||
for i in 0..5 {
|
||||
<|>foo();
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
|
@ -445,7 +443,7 @@ mod tests {
|
|||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
|
@ -478,7 +476,7 @@ mod tests {
|
|||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
|
|
|
@ -10,8 +10,8 @@ macro_rules! eprintln {
|
|||
($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
|
||||
}
|
||||
|
||||
mod assist_config;
|
||||
mod assist_context;
|
||||
mod marks;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod utils;
|
||||
|
@ -24,6 +24,8 @@ use ra_syntax::TextRange;
|
|||
|
||||
pub(crate) use crate::assist_context::{AssistContext, Assists};
|
||||
|
||||
pub use assist_config::AssistConfig;
|
||||
|
||||
/// Unique identifier of the assist, should not be shown to the user
|
||||
/// directly.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -54,9 +56,9 @@ impl Assist {
|
|||
///
|
||||
/// Assists are returned in the "unresolved" state, that is only labels are
|
||||
/// returned, without actual edits.
|
||||
pub fn unresolved(db: &RootDatabase, range: FileRange) -> Vec<Assist> {
|
||||
pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> {
|
||||
let sema = Semantics::new(db);
|
||||
let ctx = AssistContext::new(sema, range);
|
||||
let ctx = AssistContext::new(sema, config, range);
|
||||
let mut acc = Assists::new_unresolved(&ctx);
|
||||
handlers::all().iter().for_each(|handler| {
|
||||
handler(&mut acc, &ctx);
|
||||
|
@ -68,9 +70,13 @@ impl Assist {
|
|||
///
|
||||
/// Assists are returned in the "resolved" state, that is with edit fully
|
||||
/// computed.
|
||||
pub fn resolved(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
|
||||
pub fn resolved(
|
||||
db: &RootDatabase,
|
||||
config: &AssistConfig,
|
||||
range: FileRange,
|
||||
) -> Vec<ResolvedAssist> {
|
||||
let sema = Semantics::new(db);
|
||||
let ctx = AssistContext::new(sema, range);
|
||||
let ctx = AssistContext::new(sema, config, range);
|
||||
let mut acc = Assists::new_resolved(&ctx);
|
||||
handlers::all().iter().for_each(|handler| {
|
||||
handler(&mut acc, &ctx);
|
||||
|
@ -103,12 +109,14 @@ mod handlers {
|
|||
mod add_impl;
|
||||
mod add_missing_impl_members;
|
||||
mod add_new;
|
||||
mod add_turbo_fish;
|
||||
mod apply_demorgan;
|
||||
mod auto_import;
|
||||
mod change_return_type_to_result;
|
||||
mod change_visibility;
|
||||
mod early_return;
|
||||
mod fill_match_arms;
|
||||
mod fix_visibility;
|
||||
mod flip_binexpr;
|
||||
mod flip_comma;
|
||||
mod flip_trait_bound;
|
||||
|
@ -140,12 +148,14 @@ mod handlers {
|
|||
add_function::add_function,
|
||||
add_impl::add_impl,
|
||||
add_new::add_new,
|
||||
add_turbo_fish::add_turbo_fish,
|
||||
apply_demorgan::apply_demorgan,
|
||||
auto_import::auto_import,
|
||||
change_return_type_to_result::change_return_type_to_result,
|
||||
change_visibility::change_visibility,
|
||||
early_return::convert_to_guarded_return,
|
||||
fill_match_arms::fill_match_arms,
|
||||
fix_visibility::fix_visibility,
|
||||
flip_binexpr::flip_binexpr,
|
||||
flip_comma::flip_comma,
|
||||
flip_trait_bound::flip_trait_bound,
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
//! See test_utils/src/marks.rs
|
||||
|
||||
test_utils::marks![
|
||||
introduce_var_in_comment_is_not_applicable
|
||||
test_introduce_var_expr_stmt
|
||||
test_introduce_var_last_expr
|
||||
not_applicable_outside_of_bind_pat
|
||||
test_not_inline_mut_variable
|
||||
test_not_applicable_if_variable_unused
|
||||
change_visibility_field_false_positive
|
||||
test_add_from_impl_already_exists
|
||||
];
|
|
@ -11,7 +11,7 @@ use test_utils::{
|
|||
RangeOrOffset,
|
||||
};
|
||||
|
||||
use crate::{handlers::Handler, Assist, AssistContext, Assists};
|
||||
use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists};
|
||||
|
||||
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
|
||||
let (mut db, file_id) = RootDatabase::with_single_file(text);
|
||||
|
@ -41,14 +41,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
|
|||
let (db, file_id) = crate::tests::with_single_file(&before);
|
||||
let frange = FileRange { file_id, range: selection.into() };
|
||||
|
||||
let mut assist = Assist::resolved(&db, frange)
|
||||
let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange)
|
||||
.into_iter()
|
||||
.find(|assist| assist.assist.id.0 == assist_id)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"\n\nAssist is not applicable: {}\nAvailable assists: {}",
|
||||
assist_id,
|
||||
Assist::resolved(&db, frange)
|
||||
Assist::resolved(&db, &AssistConfig::default(), frange)
|
||||
.into_iter()
|
||||
.map(|assist| assist.assist.id.0)
|
||||
.collect::<Vec<_>>()
|
||||
|
@ -90,7 +90,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
|
|||
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
|
||||
|
||||
let sema = Semantics::new(&db);
|
||||
let ctx = AssistContext::new(sema, frange);
|
||||
let config = AssistConfig::default();
|
||||
let ctx = AssistContext::new(sema, &config, frange);
|
||||
let mut acc = Assists::new_resolved(&ctx);
|
||||
handler(&mut acc, &ctx);
|
||||
let mut res = acc.finish_resolved();
|
||||
|
@ -103,19 +104,11 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
|
|||
let mut actual = db.file_text(change.file_id).as_ref().to_owned();
|
||||
change.edit.apply(&mut actual);
|
||||
|
||||
match source_change.cursor_position {
|
||||
None => {
|
||||
if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
|
||||
let off = change
|
||||
.edit
|
||||
.apply_to_offset(before_cursor_pos)
|
||||
.expect("cursor position is affected by the edit");
|
||||
actual = add_cursor(&actual, off)
|
||||
}
|
||||
if !source_change.is_snippet {
|
||||
if let Some(off) = source_change.cursor_position {
|
||||
actual = add_cursor(&actual, off.offset)
|
||||
}
|
||||
Some(off) => actual = add_cursor(&actual, off.offset),
|
||||
};
|
||||
|
||||
}
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
(Some(assist), ExpectedResult::Target(target)) => {
|
||||
|
@ -136,7 +129,7 @@ fn assist_order_field_struct() {
|
|||
let (before_cursor_pos, before) = extract_offset(before);
|
||||
let (db, file_id) = with_single_file(&before);
|
||||
let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
|
||||
let assists = Assist::resolved(&db, frange);
|
||||
let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
|
||||
let mut assists = assists.iter();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -159,7 +152,7 @@ fn assist_order_if_expr() {
|
|||
let (range, before) = extract_range(before);
|
||||
let (db, file_id) = with_single_file(&before);
|
||||
let frange = FileRange { file_id, range };
|
||||
let assists = Assist::resolved(&db, frange);
|
||||
let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
|
||||
let mut assists = assists.iter();
|
||||
|
||||
assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
|
||||
|
|
|
@ -15,7 +15,7 @@ struct S;
|
|||
struct S;
|
||||
|
||||
impl Debug for S {
|
||||
|
||||
$0
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
|
@ -32,7 +32,7 @@ struct Point {
|
|||
}
|
||||
"#####,
|
||||
r#####"
|
||||
#[derive()]
|
||||
#[derive($0)]
|
||||
struct Point {
|
||||
x: u32,
|
||||
y: u32,
|
||||
|
@ -78,7 +78,7 @@ fn foo() {
|
|||
}
|
||||
|
||||
fn bar(arg: &str, baz: Baz) {
|
||||
todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
|
||||
"#####,
|
||||
|
@ -108,16 +108,16 @@ fn doctest_add_impl() {
|
|||
"add_impl",
|
||||
r#####"
|
||||
struct Ctx<T: Clone> {
|
||||
data: T,<|>
|
||||
data: T,<|>
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Ctx<T: Clone> {
|
||||
data: T,
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone> Ctx<T> {
|
||||
|
||||
$0
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
|
@ -150,7 +150,7 @@ trait Trait {
|
|||
impl Trait for () {
|
||||
Type X = ();
|
||||
fn foo(&self) {}
|
||||
fn bar(&self) {}
|
||||
$0fn bar(&self) {}
|
||||
|
||||
}
|
||||
"#####,
|
||||
|
@ -181,7 +181,7 @@ trait Trait<T> {
|
|||
|
||||
impl Trait<u32> for () {
|
||||
fn foo(&self) -> u32 {
|
||||
todo!()
|
||||
${0:todo!()}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -204,13 +204,32 @@ struct Ctx<T: Clone> {
|
|||
}
|
||||
|
||||
impl<T: Clone> Ctx<T> {
|
||||
fn new(data: T) -> Self { Self { data } }
|
||||
fn $0new(data: T) -> Self { Self { data } }
|
||||
}
|
||||
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_turbo_fish() {
|
||||
check_doc_test(
|
||||
"add_turbo_fish",
|
||||
r#####"
|
||||
fn make<T>() -> T { todo!() }
|
||||
fn main() {
|
||||
let x = make<|>();
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn make<T>() -> T { todo!() }
|
||||
fn main() {
|
||||
let x = make::<${0:_}>();
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_apply_demorgan() {
|
||||
check_doc_test(
|
||||
|
@ -257,7 +276,7 @@ fn doctest_change_return_type_to_result() {
|
|||
fn foo() -> i32<|> { 42i32 }
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo() -> Result<i32, > { Ok(42i32) }
|
||||
fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
@ -317,7 +336,7 @@ enum Action { Move { distance: u32 }, Stop }
|
|||
|
||||
fn handle(action: Action) {
|
||||
match action {
|
||||
Action::Move { distance } => {}
|
||||
$0Action::Move { distance } => {}
|
||||
Action::Stop => {}
|
||||
}
|
||||
}
|
||||
|
@ -325,6 +344,29 @@ fn handle(action: Action) {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_fix_visibility() {
|
||||
check_doc_test(
|
||||
"fix_visibility",
|
||||
r#####"
|
||||
mod m {
|
||||
fn frobnicate() {}
|
||||
}
|
||||
fn main() {
|
||||
m::frobnicate<|>() {}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
mod m {
|
||||
$0pub(crate) fn frobnicate() {}
|
||||
}
|
||||
fn main() {
|
||||
m::frobnicate() {}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_flip_binexpr() {
|
||||
check_doc_test(
|
||||
|
@ -401,7 +443,7 @@ fn main() {
|
|||
"#####,
|
||||
r#####"
|
||||
fn main() {
|
||||
let var_name = (1 + 2);
|
||||
let $0var_name = (1 + 2);
|
||||
var_name * 4;
|
||||
}
|
||||
"#####,
|
||||
|
@ -722,7 +764,7 @@ fn main() {
|
|||
let x: Result<i32, i32> = Result::Ok(92);
|
||||
let y = match x {
|
||||
Ok(a) => a,
|
||||
_ => unreachable!(),
|
||||
$0_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
"#####,
|
||||
|
|
|
@ -1,18 +1,57 @@
|
|||
//! Assorted functions shared by several assists.
|
||||
pub(crate) mod insert_use;
|
||||
|
||||
use std::iter;
|
||||
use std::{iter, ops};
|
||||
|
||||
use hir::{Adt, Crate, Semantics, Trait, Type};
|
||||
use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, make, NameOwner},
|
||||
AstNode, T,
|
||||
AstNode, SyntaxNode, T,
|
||||
};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::assist_config::SnippetCap;
|
||||
|
||||
pub(crate) use insert_use::insert_use_statement;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum Cursor<'a> {
|
||||
Replace(&'a SyntaxNode),
|
||||
Before(&'a SyntaxNode),
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
fn node(self) -> &'a SyntaxNode {
|
||||
match self {
|
||||
Cursor::Replace(node) | Cursor::Before(node) => node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor) -> String {
|
||||
assert!(cursor.node().ancestors().any(|it| it == *node));
|
||||
let range = cursor.node().text_range() - node.text_range().start();
|
||||
let range: ops::Range<usize> = range.into();
|
||||
|
||||
let mut placeholder = cursor.node().to_string();
|
||||
escape(&mut placeholder);
|
||||
let tab_stop = match cursor {
|
||||
Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder),
|
||||
Cursor::Before(placeholder) => format!("$0{}", placeholder),
|
||||
};
|
||||
|
||||
let mut buf = node.to_string();
|
||||
buf.replace_range(range, &tab_stop);
|
||||
return buf;
|
||||
|
||||
fn escape(buf: &mut String) {
|
||||
stdx::replace(buf, '{', r"\{");
|
||||
stdx::replace(buf, '}', r"\}");
|
||||
stdx::replace(buf, '$', r"\$");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_missing_assoc_items(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
impl_def: &ast::ImplDef,
|
||||
|
@ -161,13 +200,19 @@ impl FamousDefs<'_, '_> {
|
|||
#[cfg(test)]
|
||||
pub(crate) const FIXTURE: &'static str = r#"
|
||||
//- /libcore.rs crate:core
|
||||
pub mod convert{
|
||||
pub mod convert {
|
||||
pub trait From<T> {
|
||||
fn from(T) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod prelude { pub use crate::convert::From }
|
||||
pub mod option {
|
||||
pub enum Option<T> { None, Some(T)}
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{convert::From, option::Option::{self, *}};
|
||||
}
|
||||
#[prelude_import]
|
||||
pub use prelude::*;
|
||||
"#;
|
||||
|
@ -176,7 +221,25 @@ pub use prelude::*;
|
|||
self.find_trait("core:convert:From")
|
||||
}
|
||||
|
||||
pub(crate) fn core_option_Option(&self) -> Option<Enum> {
|
||||
self.find_enum("core:option:Option")
|
||||
}
|
||||
|
||||
fn find_trait(&self, path: &str) -> Option<Trait> {
|
||||
match self.find_def(path)? {
|
||||
hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_enum(&self, path: &str) -> Option<Enum> {
|
||||
match self.find_def(path)? {
|
||||
hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_def(&self, path: &str) -> Option<ScopeDef> {
|
||||
let db = self.0.db;
|
||||
let mut path = path.split(':');
|
||||
let trait_ = path.next_back()?;
|
||||
|
@ -201,9 +264,6 @@ pub use prelude::*;
|
|||
}
|
||||
let def =
|
||||
module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1;
|
||||
match def {
|
||||
hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
Some(def)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use ra_syntax::{
|
|||
};
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
|
||||
use crate::assist_context::{AssistBuilder, AssistContext};
|
||||
use crate::assist_context::AssistContext;
|
||||
|
||||
/// Creates and inserts a use statement for the given path to import.
|
||||
/// The use statement is inserted in the scope most appropriate to the
|
||||
|
@ -21,7 +21,7 @@ pub(crate) fn insert_use_statement(
|
|||
position: &SyntaxNode,
|
||||
path_to_import: &ModPath,
|
||||
ctx: &AssistContext,
|
||||
builder: &mut AssistBuilder,
|
||||
builder: &mut TextEditBuilder,
|
||||
) {
|
||||
let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
|
||||
let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| {
|
||||
|
@ -33,7 +33,7 @@ pub(crate) fn insert_use_statement(
|
|||
|
||||
if let Some(container) = container {
|
||||
let action = best_action_for_target(container, position.clone(), &target);
|
||||
make_assist(&action, &target, builder.text_edit_builder());
|
||||
make_assist(&action, &target, builder);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use ra_syntax::{
|
|||
},
|
||||
AstNode, AstPtr,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
adt::StructKind,
|
||||
|
@ -60,13 +60,10 @@ pub(super) fn lower(
|
|||
params: Option<ast::ParamList>,
|
||||
body: Option<ast::Expr>,
|
||||
) -> (Body, BodySourceMap) {
|
||||
let ctx = LowerCtx::new(db, expander.current_file_id.clone());
|
||||
|
||||
ExprCollector {
|
||||
db,
|
||||
def,
|
||||
expander,
|
||||
ctx,
|
||||
source_map: BodySourceMap::default(),
|
||||
body: Body {
|
||||
exprs: Arena::default(),
|
||||
|
@ -83,7 +80,6 @@ struct ExprCollector<'a> {
|
|||
db: &'a dyn DefDatabase,
|
||||
def: DefWithBodyId,
|
||||
expander: Expander,
|
||||
ctx: LowerCtx,
|
||||
body: Body,
|
||||
source_map: BodySourceMap,
|
||||
}
|
||||
|
@ -122,6 +118,10 @@ impl ExprCollector<'_> {
|
|||
(self.body, self.source_map)
|
||||
}
|
||||
|
||||
fn ctx(&self) -> LowerCtx {
|
||||
LowerCtx::new(self.db, self.expander.current_file_id)
|
||||
}
|
||||
|
||||
fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
|
||||
let src = self.expander.to_source(ptr);
|
||||
let id = self.make_expr(expr, Ok(src.clone()));
|
||||
|
@ -226,7 +226,7 @@ impl ExprCollector<'_> {
|
|||
None => self.collect_expr_opt(condition.expr()),
|
||||
// if let -- desugar to match
|
||||
Some(pat) => {
|
||||
tested_by!(infer_resolve_while_let);
|
||||
mark::hit!(infer_resolve_while_let);
|
||||
let pat = self.collect_pat(pat);
|
||||
let match_expr = self.collect_expr_opt(condition.expr());
|
||||
let placeholder_pat = self.missing_pat();
|
||||
|
@ -268,7 +268,7 @@ impl ExprCollector<'_> {
|
|||
};
|
||||
let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
|
||||
let generic_args =
|
||||
e.type_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx, it));
|
||||
e.type_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it));
|
||||
self.alloc_expr(
|
||||
Expr::MethodCall { receiver, method_name, args, generic_args },
|
||||
syntax_ptr,
|
||||
|
@ -373,7 +373,7 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
ast::Expr::CastExpr(e) => {
|
||||
let expr = self.collect_expr_opt(e.expr());
|
||||
let type_ref = TypeRef::from_ast_opt(&self.ctx, e.type_ref());
|
||||
let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.type_ref());
|
||||
self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::RefExpr(e) => {
|
||||
|
@ -396,7 +396,7 @@ impl ExprCollector<'_> {
|
|||
for param in pl.params() {
|
||||
let pat = self.collect_pat_opt(param.pat());
|
||||
let type_ref =
|
||||
param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it));
|
||||
param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it));
|
||||
args.push(pat);
|
||||
arg_types.push(type_ref);
|
||||
}
|
||||
|
@ -404,7 +404,7 @@ impl ExprCollector<'_> {
|
|||
let ret_type = e
|
||||
.ret_type()
|
||||
.and_then(|r| r.type_ref())
|
||||
.map(|it| TypeRef::from_ast(&self.ctx, it));
|
||||
.map(|it| TypeRef::from_ast(&self.ctx(), it));
|
||||
let body = self.collect_expr_opt(e.body());
|
||||
self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
|
||||
}
|
||||
|
@ -507,7 +507,8 @@ impl ExprCollector<'_> {
|
|||
.map(|s| match s {
|
||||
ast::Stmt::LetStmt(stmt) => {
|
||||
let pat = self.collect_pat_opt(stmt.pat());
|
||||
let type_ref = stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it));
|
||||
let type_ref =
|
||||
stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it));
|
||||
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
|
||||
Statement::Let { pat, type_ref, initializer }
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ mod tests {
|
|||
use hir_expand::{name::AsName, InFile};
|
||||
use ra_db::{fixture::WithFixture, FileId, SourceDatabase};
|
||||
use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
|
||||
use test_utils::{assert_eq_text, covers, extract_offset};
|
||||
use test_utils::{assert_eq_text, extract_offset, mark};
|
||||
|
||||
use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};
|
||||
|
||||
|
@ -388,7 +388,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn while_let_desugaring() {
|
||||
covers!(infer_resolve_while_let);
|
||||
mark::check!(infer_resolve_while_let);
|
||||
do_check_local_name(
|
||||
r#"
|
||||
fn test() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Defines database & queries for name resolution.
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{db::AstDatabase, HirFileId};
|
||||
use hir_expand::{db::AstDatabase, name::Name, HirFileId};
|
||||
use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::SmolStr;
|
||||
|
@ -12,9 +12,13 @@ use crate::{
|
|||
body::{scope::ExprScopes, Body, BodySourceMap},
|
||||
data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
|
||||
docs::Documentation,
|
||||
find_path,
|
||||
generics::GenericParams,
|
||||
item_scope::ItemInNs,
|
||||
lang_item::{LangItemTarget, LangItems},
|
||||
nameres::{raw::RawItems, CrateDefMap},
|
||||
path::ModPath,
|
||||
visibility::Visibility,
|
||||
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
|
||||
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
|
||||
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
||||
|
@ -108,6 +112,16 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
|||
// Remove this query completely, in favor of `Attrs::docs` method
|
||||
#[salsa::invoke(Documentation::documentation_query)]
|
||||
fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
|
||||
|
||||
#[salsa::invoke(find_path::importable_locations_of_query)]
|
||||
fn importable_locations_of(
|
||||
&self,
|
||||
item: ItemInNs,
|
||||
krate: CrateId,
|
||||
) -> Arc<[(ModuleId, Name, Visibility)]>;
|
||||
|
||||
#[salsa::invoke(find_path::find_path_inner_query)]
|
||||
fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
|
||||
}
|
||||
|
||||
fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
//! An algorithm to find a path to refer to a certain item.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::name::{known, AsName, Name};
|
||||
use ra_prof::profile;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
item_scope::ItemInNs,
|
||||
|
@ -7,25 +13,28 @@ use crate::{
|
|||
visibility::Visibility,
|
||||
CrateId, ModuleDefId, ModuleId,
|
||||
};
|
||||
use hir_expand::name::{known, AsName, Name};
|
||||
use test_utils::tested_by;
|
||||
|
||||
// FIXME: handle local items
|
||||
|
||||
/// Find a path that can be used to refer to a certain item. This can depend on
|
||||
/// *from where* you're referring to the item, hence the `from` parameter.
|
||||
pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
|
||||
let _p = profile("find_path");
|
||||
db.find_path_inner(item, from, MAX_PATH_LEN)
|
||||
}
|
||||
|
||||
const MAX_PATH_LEN: usize = 15;
|
||||
|
||||
impl ModPath {
|
||||
fn starts_with_std(&self) -> bool {
|
||||
self.segments.first().filter(|&first_segment| first_segment == &known::std).is_some()
|
||||
self.segments.first() == Some(&known::std)
|
||||
}
|
||||
|
||||
// When std library is present, paths starting with `std::`
|
||||
// should be preferred over paths starting with `core::` and `alloc::`
|
||||
fn can_start_with_std(&self) -> bool {
|
||||
self.segments
|
||||
.first()
|
||||
.filter(|&first_segment| {
|
||||
first_segment == &known::alloc || first_segment == &known::core
|
||||
})
|
||||
.is_some()
|
||||
let first_segment = self.segments.first();
|
||||
first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
|
@ -40,15 +49,7 @@ impl ModPath {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: handle local items
|
||||
|
||||
/// Find a path that can be used to refer to a certain item. This can depend on
|
||||
/// *from where* you're referring to the item, hence the `from` parameter.
|
||||
pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
|
||||
find_path_inner(db, item, from, MAX_PATH_LEN)
|
||||
}
|
||||
|
||||
fn find_path_inner(
|
||||
pub(crate) fn find_path_inner_query(
|
||||
db: &dyn DefDatabase,
|
||||
item: ItemInNs,
|
||||
from: ModuleId,
|
||||
|
@ -139,8 +140,7 @@ fn find_path_inner(
|
|||
let mut best_path = None;
|
||||
let mut best_path_len = max_len;
|
||||
for (module_id, name) in importable_locations {
|
||||
let mut path = match find_path_inner(
|
||||
db,
|
||||
let mut path = match db.find_path_inner(
|
||||
ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
|
||||
from,
|
||||
best_path_len - 1,
|
||||
|
@ -163,17 +163,19 @@ fn find_path_inner(
|
|||
|
||||
fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
|
||||
if old_path.starts_with_std() && new_path.can_start_with_std() {
|
||||
tested_by!(prefer_std_paths);
|
||||
if prefer_no_std {
|
||||
mark::hit!(prefer_no_std_paths);
|
||||
new_path
|
||||
} else {
|
||||
mark::hit!(prefer_std_paths);
|
||||
old_path
|
||||
}
|
||||
} else if new_path.starts_with_std() && old_path.can_start_with_std() {
|
||||
tested_by!(prefer_std_paths);
|
||||
if prefer_no_std {
|
||||
mark::hit!(prefer_no_std_paths);
|
||||
old_path
|
||||
} else {
|
||||
mark::hit!(prefer_std_paths);
|
||||
new_path
|
||||
}
|
||||
} else if new_path.len() < old_path.len() {
|
||||
|
@ -198,7 +200,7 @@ fn find_importable_locations(
|
|||
.chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id))
|
||||
{
|
||||
result.extend(
|
||||
importable_locations_in_crate(db, item, krate)
|
||||
db.importable_locations_of(item, krate)
|
||||
.iter()
|
||||
.filter(|(_, _, vis)| vis.is_visible_from(db, from))
|
||||
.map(|(m, n, _)| (*m, n.clone())),
|
||||
|
@ -213,11 +215,12 @@ fn find_importable_locations(
|
|||
///
|
||||
/// Note that the crate doesn't need to be the one in which the item is defined;
|
||||
/// it might be re-exported in other crates.
|
||||
fn importable_locations_in_crate(
|
||||
pub(crate) fn importable_locations_of_query(
|
||||
db: &dyn DefDatabase,
|
||||
item: ItemInNs,
|
||||
krate: CrateId,
|
||||
) -> Vec<(ModuleId, Name, Visibility)> {
|
||||
) -> Arc<[(ModuleId, Name, Visibility)]> {
|
||||
let _p = profile("importable_locations_of_query");
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let mut result = Vec::new();
|
||||
for (local_id, data) in def_map.modules.iter() {
|
||||
|
@ -243,17 +246,20 @@ fn importable_locations_in_crate(
|
|||
result.push((ModuleId { krate, local_id }, name.clone(), vis));
|
||||
}
|
||||
}
|
||||
result
|
||||
|
||||
Arc::from(result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_db::TestDB;
|
||||
use hir_expand::hygiene::Hygiene;
|
||||
use ra_db::fixture::WithFixture;
|
||||
use ra_syntax::ast::AstNode;
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// `code` needs to contain a cursor marker; checks that `find_path` for the
|
||||
/// item the `path` refers to returns that same path when called from the
|
||||
|
@ -508,7 +514,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn prefer_std_paths_over_alloc() {
|
||||
covers!(prefer_std_paths);
|
||||
mark::check!(prefer_std_paths);
|
||||
let code = r#"
|
||||
//- /main.rs crate:main deps:alloc,std
|
||||
<|>
|
||||
|
@ -526,33 +532,9 @@ mod tests {
|
|||
check_found_path(code, "std::sync::Arc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_alloc_paths_over_std() {
|
||||
covers!(prefer_std_paths);
|
||||
let code = r#"
|
||||
//- /main.rs crate:main deps:alloc,std
|
||||
#![no_std]
|
||||
|
||||
<|>
|
||||
|
||||
//- /std.rs crate:std deps:alloc
|
||||
|
||||
pub mod sync {
|
||||
pub use alloc::sync::Arc;
|
||||
}
|
||||
|
||||
//- /zzz.rs crate:alloc
|
||||
|
||||
pub mod sync {
|
||||
pub struct Arc;
|
||||
}
|
||||
"#;
|
||||
check_found_path(code, "alloc::sync::Arc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_core_paths_over_std() {
|
||||
covers!(prefer_std_paths);
|
||||
mark::check!(prefer_no_std_paths);
|
||||
let code = r#"
|
||||
//- /main.rs crate:main deps:core,std
|
||||
#![no_std]
|
||||
|
@ -574,6 +556,29 @@ mod tests {
|
|||
check_found_path(code, "core::fmt::Error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_alloc_paths_over_std() {
|
||||
let code = r#"
|
||||
//- /main.rs crate:main deps:alloc,std
|
||||
#![no_std]
|
||||
|
||||
<|>
|
||||
|
||||
//- /std.rs crate:std deps:alloc
|
||||
|
||||
pub mod sync {
|
||||
pub use alloc::sync::Arc;
|
||||
}
|
||||
|
||||
//- /zzz.rs crate:alloc
|
||||
|
||||
pub mod sync {
|
||||
pub struct Arc;
|
||||
}
|
||||
"#;
|
||||
check_found_path(code, "alloc::sync::Arc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_shorter_paths_if_not_alloc() {
|
||||
let code = r#"
|
||||
|
|
|
@ -46,8 +46,6 @@ pub mod find_path;
|
|||
|
||||
#[cfg(test)]
|
||||
mod test_db;
|
||||
#[cfg(test)]
|
||||
mod marks;
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
//! See test_utils/src/marks.rs
|
||||
|
||||
test_utils::marks!(
|
||||
bogus_paths
|
||||
name_res_works_for_broken_modules
|
||||
can_import_enum_variant
|
||||
glob_enum
|
||||
glob_enum_group
|
||||
glob_across_crates
|
||||
std_prelude
|
||||
macro_rules_from_other_crates_are_visible_with_macro_use
|
||||
prelude_is_macro_use
|
||||
macro_dollar_crate_self
|
||||
macro_dollar_crate_other
|
||||
infer_resolve_while_let
|
||||
prefer_std_paths
|
||||
);
|
|
@ -14,7 +14,7 @@ use ra_cfg::CfgOptions;
|
|||
use ra_db::{CrateId, FileId, ProcMacroId};
|
||||
use ra_syntax::ast;
|
||||
use rustc_hash::FxHashMap;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
|
@ -302,7 +302,7 @@ impl DefCollector<'_> {
|
|||
);
|
||||
|
||||
if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
|
||||
tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||
mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||
self.import_all_macros_exported(current_module_id, m.krate);
|
||||
}
|
||||
}
|
||||
|
@ -412,10 +412,10 @@ impl DefCollector<'_> {
|
|||
match def.take_types() {
|
||||
Some(ModuleDefId::ModuleId(m)) => {
|
||||
if import.is_prelude {
|
||||
tested_by!(std_prelude);
|
||||
mark::hit!(std_prelude);
|
||||
self.def_map.prelude = Some(m);
|
||||
} else if m.krate != self.def_map.krate {
|
||||
tested_by!(glob_across_crates);
|
||||
mark::hit!(glob_across_crates);
|
||||
// glob import from other crate => we can just import everything once
|
||||
let item_map = self.db.crate_def_map(m.krate);
|
||||
let scope = &item_map[m.local_id].scope;
|
||||
|
@ -461,7 +461,7 @@ impl DefCollector<'_> {
|
|||
}
|
||||
}
|
||||
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
|
||||
tested_by!(glob_enum);
|
||||
mark::hit!(glob_enum);
|
||||
// glob import from enum => just import all the variants
|
||||
|
||||
// XXX: urgh, so this works by accident! Here, we look at
|
||||
|
@ -510,7 +510,7 @@ impl DefCollector<'_> {
|
|||
|
||||
self.update(module_id, &[(name, def)], vis);
|
||||
}
|
||||
None => tested_by!(bogus_paths),
|
||||
None => mark::hit!(bogus_paths),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -683,7 +683,7 @@ impl ModCollector<'_, '_> {
|
|||
// Prelude module is always considered to be `#[macro_use]`.
|
||||
if let Some(prelude_module) = self.def_collector.def_map.prelude {
|
||||
if prelude_module.krate != self.def_collector.def_map.krate {
|
||||
tested_by!(prelude_is_macro_use);
|
||||
mark::hit!(prelude_is_macro_use);
|
||||
self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::iter::successors;
|
|||
|
||||
use hir_expand::name::Name;
|
||||
use ra_db::Edition;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
|
@ -108,7 +108,7 @@ impl CrateDefMap {
|
|||
let mut curr_per_ns: PerNs = match path.kind {
|
||||
PathKind::DollarCrate(krate) => {
|
||||
if krate == self.krate {
|
||||
tested_by!(macro_dollar_crate_self);
|
||||
mark::hit!(macro_dollar_crate_self);
|
||||
PerNs::types(
|
||||
ModuleId { krate: self.krate, local_id: self.root }.into(),
|
||||
Visibility::Public,
|
||||
|
@ -116,7 +116,7 @@ impl CrateDefMap {
|
|||
} else {
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let module = ModuleId { krate, local_id: def_map.root };
|
||||
tested_by!(macro_dollar_crate_other);
|
||||
mark::hit!(macro_dollar_crate_other);
|
||||
PerNs::types(module.into(), Visibility::Public)
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ impl CrateDefMap {
|
|||
}
|
||||
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
|
||||
// enum variant
|
||||
tested_by!(can_import_enum_variant);
|
||||
mark::hit!(can_import_enum_variant);
|
||||
let enum_data = db.enum_data(e);
|
||||
match enum_data.variant(&segment) {
|
||||
Some(local_id) => {
|
||||
|
|
|
@ -18,7 +18,7 @@ use ra_syntax::{
|
|||
ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
|
||||
AstNode,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
|
@ -346,7 +346,7 @@ impl RawItemsCollector {
|
|||
self.push_item(current_module, attrs, RawItemKind::Module(item));
|
||||
return;
|
||||
}
|
||||
tested_by!(name_res_works_for_broken_modules);
|
||||
mark::hit!(name_res_works_for_broken_modules);
|
||||
}
|
||||
|
||||
fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) {
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::sync::Arc;
|
|||
|
||||
use insta::assert_snapshot;
|
||||
use ra_db::{fixture::WithFixture, SourceDatabase};
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
|
||||
|
||||
|
@ -132,7 +132,7 @@ fn crate_def_map_fn_mod_same_name() {
|
|||
|
||||
#[test]
|
||||
fn bogus_paths() {
|
||||
covers!(bogus_paths);
|
||||
mark::check!(bogus_paths);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
@ -247,7 +247,7 @@ fn re_exports() {
|
|||
|
||||
#[test]
|
||||
fn std_prelude() {
|
||||
covers!(std_prelude);
|
||||
mark::check!(std_prelude);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /main.rs crate:main deps:test_crate
|
||||
|
@ -271,7 +271,7 @@ fn std_prelude() {
|
|||
|
||||
#[test]
|
||||
fn can_import_enum_variant() {
|
||||
covers!(can_import_enum_variant);
|
||||
mark::check!(can_import_enum_variant);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
|
|
@ -152,7 +152,7 @@ fn glob_privacy_2() {
|
|||
|
||||
#[test]
|
||||
fn glob_across_crates() {
|
||||
covers!(glob_across_crates);
|
||||
mark::check!(glob_across_crates);
|
||||
let map = def_map(
|
||||
r"
|
||||
//- /main.rs crate:main deps:test_crate
|
||||
|
@ -171,7 +171,6 @@ fn glob_across_crates() {
|
|||
|
||||
#[test]
|
||||
fn glob_privacy_across_crates() {
|
||||
covers!(glob_across_crates);
|
||||
let map = def_map(
|
||||
r"
|
||||
//- /main.rs crate:main deps:test_crate
|
||||
|
@ -191,7 +190,7 @@ fn glob_privacy_across_crates() {
|
|||
|
||||
#[test]
|
||||
fn glob_enum() {
|
||||
covers!(glob_enum);
|
||||
mark::check!(glob_enum);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
@ -212,7 +211,7 @@ fn glob_enum() {
|
|||
|
||||
#[test]
|
||||
fn glob_enum_group() {
|
||||
covers!(glob_enum_group);
|
||||
mark::check!(glob_enum_group);
|
||||
let map = def_map(
|
||||
r"
|
||||
//- /lib.rs
|
||||
|
|
|
@ -212,7 +212,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
|||
|
||||
#[test]
|
||||
fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
||||
covers!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||
mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /main.rs crate:main deps:foo
|
||||
|
@ -262,7 +262,7 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
|||
|
||||
#[test]
|
||||
fn prelude_is_macro_use() {
|
||||
covers!(prelude_is_macro_use);
|
||||
mark::check!(prelude_is_macro_use);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /main.rs crate:main deps:foo
|
||||
|
@ -544,8 +544,7 @@ fn path_qualified_macros() {
|
|||
|
||||
#[test]
|
||||
fn macro_dollar_crate_is_correct_in_item() {
|
||||
covers!(macro_dollar_crate_self);
|
||||
covers!(macro_dollar_crate_other);
|
||||
mark::check!(macro_dollar_crate_self);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /main.rs crate:main deps:foo
|
||||
|
@ -603,7 +602,7 @@ fn macro_dollar_crate_is_correct_in_item() {
|
|||
|
||||
#[test]
|
||||
fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
||||
covers!(macro_dollar_crate_other);
|
||||
mark::check!(macro_dollar_crate_other);
|
||||
// From std
|
||||
let map = def_map(
|
||||
r#"
|
||||
|
|
|
@ -2,7 +2,7 @@ use super::*;
|
|||
|
||||
#[test]
|
||||
fn name_res_works_for_broken_modules() {
|
||||
covers!(name_res_works_for_broken_modules);
|
||||
mark::check!(name_res_works_for_broken_modules);
|
||||
let map = def_map(
|
||||
r"
|
||||
//- /lib.rs
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::iter;
|
|||
use either::Either;
|
||||
use hir_expand::{hygiene::Hygiene, name::AsName};
|
||||
use ra_syntax::ast::{self, NameOwner};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::path::{ImportAlias, ModPath, PathKind};
|
||||
|
||||
|
@ -54,7 +54,7 @@ pub(crate) fn lower_use_tree(
|
|||
// FIXME: report errors somewhere
|
||||
// We get here if we do
|
||||
} else if is_glob {
|
||||
tested_by!(glob_enum_group);
|
||||
mark::hit!(glob_enum_group);
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, is_glob, None)
|
||||
}
|
||||
|
|
|
@ -1946,6 +1946,23 @@ mod tests {
|
|||
|
||||
check_no_diagnostic(content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_diverges_missing_arm() {
|
||||
let content = r"
|
||||
enum Either {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
fn test_fn() {
|
||||
match loop {} {
|
||||
Either::A => (),
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
check_no_diagnostic(content);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1997,26 +2014,6 @@ mod false_negatives {
|
|||
check_no_diagnostic(content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_diverges_missing_arm() {
|
||||
let content = r"
|
||||
enum Either {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
fn test_fn() {
|
||||
match loop {} {
|
||||
Either::A => (),
|
||||
}
|
||||
}
|
||||
";
|
||||
|
||||
// This is a false negative.
|
||||
// Even though the match expression diverges, rustc fails
|
||||
// to compile here since `Either::B` is missing.
|
||||
check_no_diagnostic(content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_loop_missing_arm() {
|
||||
let content = r"
|
||||
|
@ -2035,7 +2032,7 @@ mod false_negatives {
|
|||
// We currently infer the type of `loop { break Foo::A }` to `!`, which
|
||||
// causes us to skip the diagnostic since `Either::A` doesn't type check
|
||||
// with `!`.
|
||||
check_no_diagnostic(content);
|
||||
check_diagnostic(content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -218,6 +218,7 @@ struct InferenceContext<'a> {
|
|||
#[derive(Clone, Debug)]
|
||||
struct BreakableContext {
|
||||
pub may_break: bool,
|
||||
pub break_ty: Ty,
|
||||
}
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! See: https://doc.rust-lang.org/nomicon/coercions.html
|
||||
|
||||
use hir_def::{lang_item::LangItemTarget, type_ref::Mutability};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor};
|
||||
|
||||
|
@ -34,7 +34,7 @@ impl<'a> InferenceContext<'a> {
|
|||
ty1.clone()
|
||||
} else {
|
||||
if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) {
|
||||
tested_by!(coerce_fn_reification);
|
||||
mark::hit!(coerce_fn_reification);
|
||||
// Special case: two function types. Try to coerce both to
|
||||
// pointers to have a chance at getting a match. See
|
||||
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
|
||||
|
@ -44,7 +44,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let ptr_ty2 = Ty::fn_ptr(sig2);
|
||||
self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
|
||||
} else {
|
||||
tested_by!(coerce_merge_fail_fallback);
|
||||
mark::hit!(coerce_merge_fail_fallback);
|
||||
// For incompatible types, we use the latter one as result
|
||||
// to be better recovery for `if` without `else`.
|
||||
ty2.clone()
|
||||
|
|
|
@ -93,22 +93,25 @@ impl<'a> InferenceContext<'a> {
|
|||
Ty::Unknown
|
||||
}
|
||||
Expr::Loop { body } => {
|
||||
self.breakables.push(BreakableContext { may_break: false });
|
||||
self.breakables.push(BreakableContext {
|
||||
may_break: false,
|
||||
break_ty: self.table.new_type_var(),
|
||||
});
|
||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
||||
|
||||
let ctxt = self.breakables.pop().expect("breakable stack broken");
|
||||
if ctxt.may_break {
|
||||
self.diverges = Diverges::Maybe;
|
||||
}
|
||||
// FIXME handle break with value
|
||||
|
||||
if ctxt.may_break {
|
||||
Ty::unit()
|
||||
ctxt.break_ty
|
||||
} else {
|
||||
Ty::simple(TypeCtor::Never)
|
||||
}
|
||||
}
|
||||
Expr::While { condition, body } => {
|
||||
self.breakables.push(BreakableContext { may_break: false });
|
||||
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
|
||||
// while let is desugared to a match loop, so this is always simple while
|
||||
self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
|
||||
self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
|
||||
|
@ -120,7 +123,7 @@ impl<'a> InferenceContext<'a> {
|
|||
Expr::For { iterable, body, pat } => {
|
||||
let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
|
||||
|
||||
self.breakables.push(BreakableContext { may_break: false });
|
||||
self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
|
||||
let pat_ty =
|
||||
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
|
||||
|
||||
|
@ -229,17 +232,29 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
Expr::Continue => Ty::simple(TypeCtor::Never),
|
||||
Expr::Break { expr } => {
|
||||
if let Some(expr) = expr {
|
||||
// FIXME handle break with value
|
||||
self.infer_expr(*expr, &Expectation::none());
|
||||
}
|
||||
let val_ty = if let Some(expr) = expr {
|
||||
self.infer_expr(*expr, &Expectation::none())
|
||||
} else {
|
||||
Ty::unit()
|
||||
};
|
||||
|
||||
let last_ty = if let Some(ctxt) = self.breakables.last() {
|
||||
ctxt.break_ty.clone()
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
||||
let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
|
||||
|
||||
if let Some(ctxt) = self.breakables.last_mut() {
|
||||
ctxt.break_ty = merged_type;
|
||||
ctxt.may_break = true;
|
||||
} else {
|
||||
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
||||
expr: tgt_expr,
|
||||
});
|
||||
}
|
||||
|
||||
Ty::simple(TypeCtor::Never)
|
||||
}
|
||||
Expr::Return { expr } => {
|
||||
|
|
|
@ -10,7 +10,7 @@ use hir_def::{
|
|||
FieldId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use super::{BindingMode, Expectation, InferenceContext};
|
||||
use crate::{utils::variant_data, Substs, Ty, TypeCtor};
|
||||
|
@ -111,7 +111,7 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
} else if let Pat::Ref { .. } = &body[pat] {
|
||||
tested_by!(match_ergonomics_ref);
|
||||
mark::hit!(match_ergonomics_ref);
|
||||
// When you encounter a `&pat` pattern, reset to Move.
|
||||
// This is so that `w` is by value: `let (_, &w) = &(1, &2);`
|
||||
default_bm = BindingMode::Move;
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::borrow::Cow;
|
|||
|
||||
use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
|
||||
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use super::{InferenceContext, Obligation};
|
||||
use crate::{
|
||||
|
@ -313,7 +313,7 @@ impl InferenceTable {
|
|||
// more than once
|
||||
for i in 0..3 {
|
||||
if i > 0 {
|
||||
tested_by!(type_var_resolves_to_int_var);
|
||||
mark::hit!(type_var_resolves_to_int_var);
|
||||
}
|
||||
match &*ty {
|
||||
Ty::Infer(tv) => {
|
||||
|
@ -342,7 +342,7 @@ impl InferenceTable {
|
|||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
if tv_stack.contains(&inner) {
|
||||
tested_by!(type_var_cycles_resolve_as_possible);
|
||||
mark::hit!(type_var_cycles_resolve_as_possible);
|
||||
// recursive type
|
||||
return tv.fallback_value();
|
||||
}
|
||||
|
@ -369,7 +369,7 @@ impl InferenceTable {
|
|||
Ty::Infer(tv) => {
|
||||
let inner = tv.to_inner();
|
||||
if tv_stack.contains(&inner) {
|
||||
tested_by!(type_var_cycles_resolve_completely);
|
||||
mark::hit!(type_var_cycles_resolve_completely);
|
||||
// recursive type
|
||||
return tv.fallback_value();
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ pub mod expr;
|
|||
mod tests;
|
||||
#[cfg(test)]
|
||||
mod test_db;
|
||||
mod marks;
|
||||
mod _match;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
@ -808,15 +807,13 @@ impl Ty {
|
|||
}
|
||||
}
|
||||
|
||||
/// If this is an `impl Trait` or `dyn Trait`, returns that trait.
|
||||
pub fn inherent_trait(&self) -> Option<TraitId> {
|
||||
/// If this is a `dyn Trait`, returns that trait.
|
||||
pub fn dyn_trait(&self) -> Option<TraitId> {
|
||||
match self {
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
predicates.iter().find_map(|pred| match pred {
|
||||
GenericPredicate::Implemented(tr) => Some(tr.trait_),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred {
|
||||
GenericPredicate::Implemented(tr) => Some(tr.trait_),
|
||||
_ => None,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -812,7 +812,7 @@ impl TraitEnvironment {
|
|||
// add `Self: Trait<T1, T2, ...>` to the environment in trait
|
||||
// function default implementations (and hypothetical code
|
||||
// inside consts or type aliases)
|
||||
test_utils::tested_by!(trait_self_implements_self);
|
||||
test_utils::mark::hit!(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);
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
//! See test_utils/src/marks.rs
|
||||
|
||||
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
|
||||
coerce_fn_reification
|
||||
trait_self_implements_self
|
||||
);
|
|
@ -408,8 +408,9 @@ fn iterate_trait_method_candidates<T>(
|
|||
receiver_ty: Option<&Canonical<Ty>>,
|
||||
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
|
||||
let inherent_trait = self_ty.value.inherent_trait().into_iter();
|
||||
// if ty is `dyn Trait`, the trait doesn't need to be in scope
|
||||
let inherent_trait =
|
||||
self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
|
||||
let env_traits = if let Ty::Placeholder(_) = self_ty.value {
|
||||
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope
|
||||
env.trait_predicates_for_self_ty(&self_ty.value)
|
||||
|
@ -468,7 +469,7 @@ fn iterate_inherent_methods<T>(
|
|||
// already happens in `is_valid_candidate` above; if not, we
|
||||
// check it here
|
||||
if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() {
|
||||
test_utils::tested_by!(impl_self_type_match_without_receiver);
|
||||
test_utils::mark::hit!(impl_self_type_match_without_receiver);
|
||||
continue;
|
||||
}
|
||||
if let Some(result) = callback(&self_ty.value, item) {
|
||||
|
@ -601,11 +602,6 @@ pub fn implements_trait(
|
|||
krate: CrateId,
|
||||
trait_: TraitId,
|
||||
) -> bool {
|
||||
if ty.value.inherent_trait() == Some(trait_) {
|
||||
// FIXME this is a bit of a hack, since Chalk should say the same thing
|
||||
// anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet
|
||||
return true;
|
||||
}
|
||||
let goal = generic_implements_goal(db, env, trait_, ty.clone());
|
||||
let solution = db.trait_solve(krate, goal);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::infer_with_mismatches;
|
||||
use insta::assert_snapshot;
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
// Infer with some common definitions and impls.
|
||||
fn infer(source: &str) -> String {
|
||||
|
@ -339,7 +339,7 @@ fn test(i: i32) {
|
|||
|
||||
#[test]
|
||||
fn coerce_merge_one_by_one1() {
|
||||
covers!(coerce_merge_fail_fallback);
|
||||
mark::check!(coerce_merge_fail_fallback);
|
||||
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
|
@ -547,7 +547,7 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn coerce_fn_items_in_match_arms() {
|
||||
covers!(coerce_fn_reification);
|
||||
mark::check!(coerce_fn_reification);
|
||||
assert_snapshot!(
|
||||
infer_with_mismatches(r#"
|
||||
fn foo1(x: u32) -> isize { 1 }
|
||||
|
|
|
@ -984,7 +984,7 @@ fn test() { S2.into()<|>; }
|
|||
|
||||
#[test]
|
||||
fn method_resolution_overloaded_method() {
|
||||
test_utils::covers!(impl_self_type_match_without_receiver);
|
||||
test_utils::mark::check!(impl_self_type_match_without_receiver);
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- main.rs
|
||||
|
@ -1096,3 +1096,34 @@ fn test() { (S {}).method()<|>; }
|
|||
);
|
||||
assert_eq!(t, "()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_trait_super_trait_not_in_scope() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
mod m {
|
||||
pub trait SuperTrait {
|
||||
fn foo(&self) -> u32 { 0 }
|
||||
}
|
||||
}
|
||||
trait Trait: m::SuperTrait {}
|
||||
|
||||
struct S;
|
||||
impl m::SuperTrait for S {}
|
||||
impl Trait for S {}
|
||||
|
||||
fn test(d: &dyn Trait) {
|
||||
d.foo();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
52..56 'self': &Self
|
||||
65..70 '{ 0 }': u32
|
||||
67..68 '0': u32
|
||||
177..178 'd': &dyn Trait
|
||||
192..208 '{ ...o(); }': ()
|
||||
198..199 'd': &dyn Trait
|
||||
198..205 'd.foo()': u32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use insta::assert_snapshot;
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use super::{infer, infer_with_mismatches};
|
||||
|
||||
|
@ -197,7 +197,7 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn infer_pattern_match_ergonomics_ref() {
|
||||
covers!(match_ergonomics_ref);
|
||||
mark::check!(match_ergonomics_ref);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use insta::assert_snapshot;
|
||||
use test_utils::covers;
|
||||
use ra_db::fixture::WithFixture;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
use super::infer;
|
||||
use crate::test_db::TestDB;
|
||||
use ra_db::fixture::WithFixture;
|
||||
|
||||
#[test]
|
||||
fn bug_484() {
|
||||
|
@ -89,8 +90,8 @@ fn quux() {
|
|||
|
||||
#[test]
|
||||
fn recursive_vars() {
|
||||
covers!(type_var_cycles_resolve_completely);
|
||||
covers!(type_var_cycles_resolve_as_possible);
|
||||
mark::check!(type_var_cycles_resolve_completely);
|
||||
mark::check!(type_var_cycles_resolve_as_possible);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
|
@ -112,8 +113,6 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn recursive_vars_2() {
|
||||
covers!(type_var_cycles_resolve_completely);
|
||||
covers!(type_var_cycles_resolve_as_possible);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
fn test() {
|
||||
|
@ -170,7 +169,7 @@ fn write() {
|
|||
|
||||
#[test]
|
||||
fn infer_std_crash_2() {
|
||||
covers!(type_var_resolves_to_int_var);
|
||||
mark::check!(type_var_resolves_to_int_var);
|
||||
// caused "equating two type variables, ...", taken from std
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
|
@ -563,6 +562,37 @@ fn main() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_4465_dollar_crate_at_type() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
pub struct Foo {}
|
||||
pub fn anything<T>() -> T {
|
||||
loop {}
|
||||
}
|
||||
macro_rules! foo {
|
||||
() => {{
|
||||
let r: $crate::Foo = anything();
|
||||
r
|
||||
}};
|
||||
}
|
||||
fn main() {
|
||||
let _a = foo!();
|
||||
}
|
||||
"#), @r###"
|
||||
45..60 '{ loop {} }': T
|
||||
51..58 'loop {}': !
|
||||
56..58 '{}': ()
|
||||
!0..31 '{letr:...g();r}': Foo
|
||||
!4..5 'r': Foo
|
||||
!18..26 'anything': fn anything<Foo>() -> Foo
|
||||
!18..28 'anything()': Foo
|
||||
!29..30 'r': Foo
|
||||
164..188 '{ ...!(); }': ()
|
||||
174..176 '_a': Foo
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_4053_diesel_where_clauses() {
|
||||
assert_snapshot!(
|
||||
|
|
|
@ -1860,3 +1860,66 @@ fn test() {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_loop_break_with_val() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
enum Option<T> { Some(T), None }
|
||||
use Option::*;
|
||||
|
||||
fn test() {
|
||||
let x = loop {
|
||||
if false {
|
||||
break None;
|
||||
}
|
||||
|
||||
break Some(true);
|
||||
};
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
60..169 '{ ... }; }': ()
|
||||
70..71 'x': Option<bool>
|
||||
74..166 'loop {... }': Option<bool>
|
||||
79..166 '{ ... }': ()
|
||||
89..133 'if fal... }': ()
|
||||
92..97 'false': bool
|
||||
98..133 '{ ... }': ()
|
||||
112..122 'break None': !
|
||||
118..122 'None': Option<bool>
|
||||
143..159 'break ...(true)': !
|
||||
149..153 'Some': Some<bool>(bool) -> Option<bool>
|
||||
149..159 'Some(true)': Option<bool>
|
||||
154..158 'true': bool
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_loop_break_without_val() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
enum Option<T> { Some(T), None }
|
||||
use Option::*;
|
||||
|
||||
fn test() {
|
||||
let x = loop {
|
||||
if false {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
60..137 '{ ... }; }': ()
|
||||
70..71 'x': ()
|
||||
74..134 'loop {... }': ()
|
||||
79..134 '{ ... }': ()
|
||||
89..128 'if fal... }': ()
|
||||
92..97 'false': bool
|
||||
98..128 '{ ... }': ()
|
||||
112..117 'break': !
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use insta::assert_snapshot;
|
||||
|
||||
use ra_db::fixture::WithFixture;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
use super::{infer, infer_with_mismatches, type_at, type_at_pos};
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
#[test]
|
||||
fn infer_await() {
|
||||
|
@ -301,7 +302,7 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn trait_default_method_self_bound_implements_trait() {
|
||||
test_utils::covers!(trait_self_implements_self);
|
||||
mark::check!(trait_self_implements_self);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait Trait {
|
||||
|
@ -324,7 +325,6 @@ trait Trait {
|
|||
|
||||
#[test]
|
||||
fn trait_default_method_self_bound_implements_super_trait() {
|
||||
test_utils::covers!(trait_self_implements_self);
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait SuperTrait {
|
||||
|
@ -1616,6 +1616,138 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_ptr_and_item() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[lang="fn_once"]
|
||||
trait FnOnce<Args> {
|
||||
type Output;
|
||||
|
||||
fn call_once(self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
trait Foo<T> {
|
||||
fn foo(&self) -> T;
|
||||
}
|
||||
|
||||
struct Bar<T>(T);
|
||||
|
||||
impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> {
|
||||
fn foo(&self) -> (A1, R) {}
|
||||
}
|
||||
|
||||
enum Opt<T> { None, Some(T) }
|
||||
impl<T> Opt<T> {
|
||||
fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> {}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let bar: Bar<fn(u8) -> u32>;
|
||||
bar.foo();
|
||||
|
||||
let opt: Opt<u8>;
|
||||
let f: fn(u8) -> u32;
|
||||
opt.map(f);
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
75..79 'self': Self
|
||||
81..85 'args': Args
|
||||
140..144 'self': &Self
|
||||
244..248 'self': &Bar<F>
|
||||
261..263 '{}': ()
|
||||
347..351 'self': Opt<T>
|
||||
353..354 'f': F
|
||||
369..371 '{}': ()
|
||||
385..501 '{ ...(f); }': ()
|
||||
395..398 'bar': Bar<fn(u8) -> u32>
|
||||
424..427 'bar': Bar<fn(u8) -> u32>
|
||||
424..433 'bar.foo()': {unknown}
|
||||
444..447 'opt': Opt<u8>
|
||||
466..467 'f': fn(u8) -> u32
|
||||
488..491 'opt': Opt<u8>
|
||||
488..498 'opt.map(f)': Opt<FnOnce::Output<fn(u8) -> u32, (u8,)>>
|
||||
496..497 'f': fn(u8) -> u32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_trait_deref_with_ty_default() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[lang = "deref"]
|
||||
trait Deref {
|
||||
type Target;
|
||||
|
||||
fn deref(&self) -> &Self::Target;
|
||||
}
|
||||
|
||||
#[lang="fn_once"]
|
||||
trait FnOnce<Args> {
|
||||
type Output;
|
||||
|
||||
fn call_once(self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn foo(&self) -> usize {}
|
||||
}
|
||||
|
||||
struct Lazy<T, F = fn() -> T>(F);
|
||||
|
||||
impl<T, F> Lazy<T, F> {
|
||||
pub fn new(f: F) -> Lazy<T, F> {}
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> {
|
||||
type Target = T;
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo);
|
||||
let r1 = lazy1.foo();
|
||||
|
||||
fn make_foo_fn() -> Foo {}
|
||||
let make_foo_fn_ptr: fn() -> Foo = make_foo_fn;
|
||||
let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr);
|
||||
let r2 = lazy2.foo();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
65..69 'self': &Self
|
||||
166..170 'self': Self
|
||||
172..176 'args': Args
|
||||
240..244 'self': &Foo
|
||||
255..257 '{}': ()
|
||||
335..336 'f': F
|
||||
355..357 '{}': ()
|
||||
444..690 '{ ...o(); }': ()
|
||||
454..459 'lazy1': Lazy<Foo, fn() -> T>
|
||||
476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T>
|
||||
476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T>
|
||||
486..492 '|| Foo': || -> T
|
||||
489..492 'Foo': Foo
|
||||
503..505 'r1': {unknown}
|
||||
508..513 'lazy1': Lazy<Foo, fn() -> T>
|
||||
508..519 'lazy1.foo()': {unknown}
|
||||
561..576 'make_foo_fn_ptr': fn() -> Foo
|
||||
592..603 'make_foo_fn': fn make_foo_fn() -> Foo
|
||||
613..618 'lazy2': Lazy<Foo, fn() -> T>
|
||||
635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T>
|
||||
635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T>
|
||||
645..660 'make_foo_fn_ptr': fn() -> Foo
|
||||
671..673 'r2': {unknown}
|
||||
676..681 'lazy2': Lazy<Foo, fn() -> T>
|
||||
676..687 'lazy2.foo()': {unknown}
|
||||
550..552 '{}': ()
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_1() {
|
||||
assert_snapshot!(
|
||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
|||
ast::{self, ArgListOwner},
|
||||
match_ast, AstNode, SyntaxNode, SyntaxToken,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{CallInfo, FilePosition, FunctionSignature};
|
||||
|
||||
|
@ -84,7 +84,7 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op
|
|||
|
||||
let arg_list_range = arg_list.syntax().text_range();
|
||||
if !arg_list_range.contains_inclusive(token.text_range().start()) {
|
||||
tested_by!(call_info_bad_offset);
|
||||
mark::hit!(call_info_bad_offset);
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ impl CallInfo {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::mock_analysis::single_file_with_position;
|
||||
|
||||
|
@ -529,7 +529,7 @@ By default this method stops actor's `Context`."#
|
|||
|
||||
#[test]
|
||||
fn call_info_bad_offset() {
|
||||
covers!(call_info_bad_offset);
|
||||
mark::check!(call_info_bad_offset);
|
||||
let (analysis, position) = single_file_with_position(
|
||||
r#"fn foo(x: u32, y: u32) -> u32 {x + y}
|
||||
fn bar() { foo <|> (3, ); }"#,
|
||||
|
|
|
@ -59,8 +59,8 @@ pub use crate::completion::{
|
|||
/// with ordering of completions (currently this is done by the client).
|
||||
pub(crate) fn completions(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
config: &CompletionConfig,
|
||||
position: FilePosition,
|
||||
) -> Option<Completions> {
|
||||
let ctx = CompletionContext::new(db, position, config)?;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
|
||||
use ra_syntax::AstNode;
|
||||
use rustc_hash::FxHashSet;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::completion::{CompletionContext, Completions};
|
||||
|
||||
|
@ -40,7 +40,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
|
||||
if name_ref.syntax().text() == name.to_string().as_str() {
|
||||
// for `use self::foo<|>`, don't suggest `foo` as a completion
|
||||
tested_by!(dont_complete_current_use);
|
||||
mark::hit!(dont_complete_current_use);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
|
||||
use insta::assert_debug_snapshot;
|
||||
|
@ -158,7 +158,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn dont_complete_current_use() {
|
||||
covers!(dont_complete_current_use);
|
||||
mark::check!(dont_complete_current_use);
|
||||
let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference);
|
||||
assert!(completions.is_empty());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Completion of names from the current scope, e.g. locals and imported items.
|
||||
|
||||
use hir::ScopeDef;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::completion::{CompletionContext, Completions};
|
||||
use hir::{Adt, ModuleDef, Type};
|
||||
|
@ -30,7 +30,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
|||
if ctx.use_item_syntax.is_some() {
|
||||
if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
|
||||
if name_ref.syntax().text() == name.to_string().as_str() {
|
||||
tested_by!(self_fulfilling_completion);
|
||||
mark::hit!(self_fulfilling_completion);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
|
||||
|
||||
|
@ -76,7 +76,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn self_fulfilling_completion() {
|
||||
covers!(self_fulfilling_completion);
|
||||
mark::check!(self_fulfilling_completion);
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r#"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
|
||||
use ra_syntax::ast::NameOwner;
|
||||
use stdx::SepBy;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
completion::{
|
||||
|
@ -121,7 +121,7 @@ impl Completions {
|
|||
_ => false,
|
||||
};
|
||||
if has_non_default_type_params {
|
||||
tested_by!(inserts_angle_brackets_for_generics);
|
||||
mark::hit!(inserts_angle_brackets_for_generics);
|
||||
completion_item = completion_item
|
||||
.lookup_by(local_name.clone())
|
||||
.label(format!("{}<…>", local_name))
|
||||
|
@ -176,7 +176,7 @@ impl Completions {
|
|||
}
|
||||
None if needs_bang => builder.insert_text(format!("{}!", name)),
|
||||
_ => {
|
||||
tested_by!(dont_insert_macro_call_parens_unncessary);
|
||||
mark::hit!(dont_insert_macro_call_parens_unncessary);
|
||||
builder.insert_text(name)
|
||||
}
|
||||
};
|
||||
|
@ -330,14 +330,14 @@ pub(crate) fn compute_score(
|
|||
// FIXME: this should not fall back to string equality.
|
||||
let ty = &ty.display(ctx.db).to_string();
|
||||
let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
|
||||
tested_by!(test_struct_field_completion_in_record_lit);
|
||||
mark::hit!(test_struct_field_completion_in_record_lit);
|
||||
let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
|
||||
(
|
||||
struct_field.name(ctx.db).to_string(),
|
||||
struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
|
||||
)
|
||||
} else if let Some(active_parameter) = &ctx.active_parameter {
|
||||
tested_by!(test_struct_field_completion_in_func_call);
|
||||
mark::hit!(test_struct_field_completion_in_func_call);
|
||||
(active_parameter.name.clone(), active_parameter.ty.clone())
|
||||
} else {
|
||||
return None;
|
||||
|
@ -398,7 +398,7 @@ impl Builder {
|
|||
None => return self,
|
||||
};
|
||||
// If not an import, add parenthesis automatically.
|
||||
tested_by!(inserts_parens_for_function_calls);
|
||||
mark::hit!(inserts_parens_for_function_calls);
|
||||
|
||||
let (snippet, label) = if params.is_empty() {
|
||||
(format!("{}()$0", name), format!("{}()", name))
|
||||
|
@ -457,7 +457,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::completion::{
|
||||
test_utils::{do_completion, do_completion_with_options},
|
||||
|
@ -607,7 +607,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn inserts_parens_for_function_calls() {
|
||||
covers!(inserts_parens_for_function_calls);
|
||||
mark::check!(inserts_parens_for_function_calls);
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r"
|
||||
|
@ -992,7 +992,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn inserts_angle_brackets_for_generics() {
|
||||
covers!(inserts_angle_brackets_for_generics);
|
||||
mark::check!(inserts_angle_brackets_for_generics);
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r"
|
||||
|
@ -1115,7 +1115,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn dont_insert_macro_call_parens_unncessary() {
|
||||
covers!(dont_insert_macro_call_parens_unncessary);
|
||||
mark::check!(dont_insert_macro_call_parens_unncessary);
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r"
|
||||
|
@ -1181,7 +1181,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_struct_field_completion_in_func_call() {
|
||||
covers!(test_struct_field_completion_in_func_call);
|
||||
mark::check!(test_struct_field_completion_in_func_call);
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r"
|
||||
|
@ -1271,7 +1271,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_struct_field_completion_in_record_lit() {
|
||||
covers!(test_struct_field_completion_in_record_lit);
|
||||
mark::check!(test_struct_field_completion_in_record_lit);
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r"
|
||||
|
|
|
@ -20,7 +20,7 @@ pub(crate) fn do_completion_with_options(
|
|||
} else {
|
||||
single_file_with_position(code)
|
||||
};
|
||||
let completions = analysis.completions(position, options).unwrap().unwrap();
|
||||
let completions = analysis.completions(options, position).unwrap().unwrap();
|
||||
let completion_items: Vec<CompletionItem> = completions.into();
|
||||
let mut kind_completions: Vec<CompletionItem> =
|
||||
completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();
|
||||
|
|
|
@ -629,6 +629,7 @@ mod tests {
|
|||
},
|
||||
],
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
},
|
||||
),
|
||||
severity: Error,
|
||||
|
@ -685,6 +686,7 @@ mod tests {
|
|||
],
|
||||
file_system_edits: [],
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
},
|
||||
),
|
||||
severity: Error,
|
||||
|
|
|
@ -93,7 +93,7 @@ pub(crate) fn reference_definition(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::{assert_eq_text, covers};
|
||||
use test_utils::assert_eq_text;
|
||||
|
||||
use crate::mock_analysis::analysis_and_position;
|
||||
|
||||
|
@ -208,7 +208,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_macros() {
|
||||
covers!(ra_ide_db::goto_def_for_macros);
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
@ -225,7 +224,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_macros_from_other_crates() {
|
||||
covers!(ra_ide_db::goto_def_for_macros);
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
@ -245,7 +243,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_use_alias() {
|
||||
covers!(ra_ide_db::goto_def_for_use_alias);
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
@ -370,7 +367,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_methods() {
|
||||
covers!(ra_ide_db::goto_def_for_methods);
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
@ -390,7 +386,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_fields() {
|
||||
covers!(ra_ide_db::goto_def_for_fields);
|
||||
check_goto(
|
||||
r"
|
||||
//- /lib.rs
|
||||
|
@ -409,7 +404,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_record_fields() {
|
||||
covers!(ra_ide_db::goto_def_for_record_fields);
|
||||
check_goto(
|
||||
r"
|
||||
//- /lib.rs
|
||||
|
@ -430,7 +424,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_record_pat_fields() {
|
||||
covers!(ra_ide_db::goto_def_for_record_field_pats);
|
||||
check_goto(
|
||||
r"
|
||||
//- /lib.rs
|
||||
|
@ -873,7 +866,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_def_for_field_init_shorthand() {
|
||||
covers!(ra_ide_db::goto_def_for_field_init_shorthand);
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
|
|
@ -42,8 +42,6 @@ mod inlay_hints;
|
|||
mod expand_macro;
|
||||
mod ssr;
|
||||
|
||||
#[cfg(test)]
|
||||
mod marks;
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
|
||||
|
@ -82,7 +80,7 @@ pub use crate::{
|
|||
};
|
||||
|
||||
pub use hir::Documentation;
|
||||
pub use ra_assists::AssistId;
|
||||
pub use ra_assists::{AssistConfig, AssistId};
|
||||
pub use ra_db::{
|
||||
Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
|
||||
};
|
||||
|
@ -458,17 +456,17 @@ impl Analysis {
|
|||
/// Computes completions at the given position.
|
||||
pub fn completions(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
config: &CompletionConfig,
|
||||
position: FilePosition,
|
||||
) -> Cancelable<Option<Vec<CompletionItem>>> {
|
||||
self.with_db(|db| completion::completions(db, position, config).map(Into::into))
|
||||
self.with_db(|db| completion::completions(db, config, position).map(Into::into))
|
||||
}
|
||||
|
||||
/// Computes assists (aka code actions aka intentions) for the given
|
||||
/// position.
|
||||
pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<Assist>> {
|
||||
pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> {
|
||||
self.with_db(|db| {
|
||||
ra_assists::Assist::resolved(db, frange)
|
||||
ra_assists::Assist::resolved(db, config, frange)
|
||||
.into_iter()
|
||||
.map(|assist| Assist {
|
||||
id: assist.assist.id,
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
//! See test_utils/src/marks.rs
|
||||
|
||||
test_utils::marks!(
|
||||
inserts_angle_brackets_for_generics
|
||||
inserts_parens_for_function_calls
|
||||
call_info_bad_offset
|
||||
dont_complete_current_use
|
||||
test_resolve_parent_module_on_module_decl
|
||||
search_filters_by_range
|
||||
dont_insert_macro_call_parens_unncessary
|
||||
self_fulfilling_completion
|
||||
test_struct_field_completion_in_func_call
|
||||
test_struct_field_completion_in_record_lit
|
||||
test_rename_struct_field_for_shorthand
|
||||
test_rename_local_for_field_shorthand
|
||||
);
|
|
@ -7,7 +7,7 @@ use ra_syntax::{
|
|||
algo::find_node_at_offset,
|
||||
ast::{self, AstNode},
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::NavigationTarget;
|
||||
|
||||
|
@ -25,7 +25,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
|
|||
.item_list()
|
||||
.map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset))
|
||||
{
|
||||
tested_by!(test_resolve_parent_module_on_module_decl);
|
||||
mark::hit!(test_resolve_parent_module_on_module_decl);
|
||||
module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast);
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
|
|||
mod tests {
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_db::Env;
|
||||
use test_utils::covers;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
mock_analysis::{analysis_and_position, MockAnalysis},
|
||||
|
@ -81,7 +81,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_resolve_parent_module_on_module_decl() {
|
||||
covers!(test_resolve_parent_module_on_module_decl);
|
||||
mark::check!(test_resolve_parent_module_on_module_decl);
|
||||
let (analysis, pos) = analysis_and_position(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
|
|
@ -190,8 +190,6 @@ fn get_struct_def_name_for_struct_literal_search(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_utils::covers;
|
||||
|
||||
use crate::{
|
||||
mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis},
|
||||
Declaration, Reference, ReferenceSearchResult, SearchScope,
|
||||
|
@ -301,7 +299,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn search_filters_by_range() {
|
||||
covers!(ra_ide_db::search_filters_by_range);
|
||||
let code = r#"
|
||||
fn foo() {
|
||||
let spam<|> = 92;
|
||||
|
|
|
@ -9,7 +9,7 @@ use ra_syntax::{
|
|||
};
|
||||
use ra_text_edit::TextEdit;
|
||||
use std::convert::TryInto;
|
||||
use test_utils::tested_by;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind,
|
||||
|
@ -57,13 +57,13 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
|
|||
let file_id = reference.file_range.file_id;
|
||||
let range = match reference.kind {
|
||||
ReferenceKind::FieldShorthandForField => {
|
||||
tested_by!(test_rename_struct_field_for_shorthand);
|
||||
mark::hit!(test_rename_struct_field_for_shorthand);
|
||||
replacement_text.push_str(new_name);
|
||||
replacement_text.push_str(": ");
|
||||
TextRange::new(reference.file_range.range.start(), reference.file_range.range.start())
|
||||
}
|
||||
ReferenceKind::FieldShorthandForLocal => {
|
||||
tested_by!(test_rename_local_for_field_shorthand);
|
||||
mark::hit!(test_rename_local_for_field_shorthand);
|
||||
replacement_text.push_str(": ");
|
||||
replacement_text.push_str(new_name);
|
||||
TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
|
||||
|
@ -260,7 +260,7 @@ fn rename_reference(
|
|||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
use test_utils::{assert_eq_text, covers};
|
||||
use test_utils::{assert_eq_text, mark};
|
||||
|
||||
use crate::{
|
||||
mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
|
||||
|
@ -492,7 +492,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_rename_struct_field_for_shorthand() {
|
||||
covers!(test_rename_struct_field_for_shorthand);
|
||||
mark::check!(test_rename_struct_field_for_shorthand);
|
||||
test_rename(
|
||||
r#"
|
||||
struct Foo {
|
||||
|
@ -522,7 +522,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_rename_local_for_field_shorthand() {
|
||||
covers!(test_rename_local_for_field_shorthand);
|
||||
mark::check!(test_rename_local_for_field_shorthand);
|
||||
test_rename(
|
||||
r#"
|
||||
struct Foo {
|
||||
|
@ -670,6 +670,7 @@ mod tests {
|
|||
},
|
||||
],
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -722,6 +723,7 @@ mod tests {
|
|||
},
|
||||
],
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -818,6 +820,7 @@ mod tests {
|
|||
},
|
||||
],
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{Attrs, HirFileId, InFile, Semantics};
|
||||
use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
|
||||
use itertools::Itertools;
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
|
@ -70,14 +70,36 @@ fn runnable_fn(
|
|||
RunnableKind::Bin
|
||||
} else {
|
||||
let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
|
||||
let path = module
|
||||
let def = sema.to_def(&fn_def)?;
|
||||
let impl_trait_name =
|
||||
def.as_assoc_item(sema.db).and_then(|assoc_item| {
|
||||
match assoc_item.container(sema.db) {
|
||||
hir::AssocItemContainer::Trait(trait_item) => {
|
||||
Some(trait_item.name(sema.db).to_string())
|
||||
}
|
||||
hir::AssocItemContainer::ImplDef(impl_def) => impl_def
|
||||
.target_ty(sema.db)
|
||||
.as_adt()
|
||||
.map(|adt| adt.name(sema.db).to_string()),
|
||||
}
|
||||
});
|
||||
|
||||
let path_iter = module
|
||||
.path_to_root(sema.db)
|
||||
.into_iter()
|
||||
.rev()
|
||||
.filter_map(|it| it.name(sema.db))
|
||||
.map(|name| name.to_string())
|
||||
.chain(std::iter::once(name_string))
|
||||
.join("::");
|
||||
.map(|name| name.to_string());
|
||||
|
||||
let path = if let Some(impl_trait_name) = impl_trait_name {
|
||||
path_iter
|
||||
.chain(std::iter::once(impl_trait_name))
|
||||
.chain(std::iter::once(name_string))
|
||||
.join("::")
|
||||
} else {
|
||||
path_iter.chain(std::iter::once(name_string)).join("::")
|
||||
};
|
||||
|
||||
TestId::Path(path)
|
||||
} else {
|
||||
TestId::Name(name_string)
|
||||
|
@ -278,6 +300,46 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runnables_doc_test_in_impl() {
|
||||
let (analysis, pos) = analysis_and_position(
|
||||
r#"
|
||||
//- /lib.rs
|
||||
<|> //empty
|
||||
fn main() {}
|
||||
|
||||
struct Data;
|
||||
impl Data {
|
||||
/// ```
|
||||
/// let x = 5;
|
||||
/// ```
|
||||
fn foo() {}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
let runnables = analysis.runnables(pos.file_id).unwrap();
|
||||
assert_debug_snapshot!(&runnables,
|
||||
@r###"
|
||||
[
|
||||
Runnable {
|
||||
range: 1..21,
|
||||
kind: Bin,
|
||||
features_needed: None,
|
||||
},
|
||||
Runnable {
|
||||
range: 51..105,
|
||||
kind: DocTest {
|
||||
test_id: Path(
|
||||
"Data::foo",
|
||||
),
|
||||
},
|
||||
features_needed: None,
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_runnables_module() {
|
||||
let (analysis, pos) = analysis_and_position(
|
||||
|
|
|
@ -82,7 +82,6 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange>
|
|||
Some(SingleFileChange {
|
||||
label: "add semicolon".to_string(),
|
||||
edit: TextEdit::insert(offset, ";".to_string()),
|
||||
cursor_position: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -111,7 +110,6 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange>
|
|||
Some(SingleFileChange {
|
||||
label: "reindent dot".to_string(),
|
||||
edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent),
|
||||
cursor_position: Some(offset + target_indent_len - current_indent_len + TextSize::of('.')),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -130,7 +128,6 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang
|
|||
Some(SingleFileChange {
|
||||
label: "add space after return type".to_string(),
|
||||
edit: TextEdit::insert(after_arrow, " ".to_string()),
|
||||
cursor_position: Some(after_arrow),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -140,7 +137,7 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
|
||||
fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> {
|
||||
fn do_type_char(char_typed: char, before: &str) -> Option<String> {
|
||||
let (offset, before) = extract_offset(before);
|
||||
let edit = TextEdit::insert(offset, char_typed.to_string());
|
||||
let mut before = before.to_string();
|
||||
|
@ -148,21 +145,15 @@ mod tests {
|
|||
let parse = SourceFile::parse(&before);
|
||||
on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| {
|
||||
it.edit.apply(&mut before);
|
||||
(before.to_string(), it)
|
||||
before.to_string()
|
||||
})
|
||||
}
|
||||
|
||||
fn type_char(char_typed: char, before: &str, after: &str) {
|
||||
let (actual, file_change) = do_type_char(char_typed, before)
|
||||
let actual = do_type_char(char_typed, before)
|
||||
.unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
|
||||
|
||||
if after.contains("<|>") {
|
||||
let (offset, after) = extract_offset(after);
|
||||
assert_eq_text!(&after, &actual);
|
||||
assert_eq!(file_change.cursor_position, Some(offset))
|
||||
} else {
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
|
||||
fn type_char_noop(char_typed: char, before: &str) {
|
||||
|
@ -350,6 +341,6 @@ fn foo() {
|
|||
|
||||
#[test]
|
||||
fn adds_space_after_return_type() {
|
||||
type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }")
|
||||
type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ use ra_syntax::{
|
|||
ast::{self, AstNode},
|
||||
match_ast,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::RootDatabase;
|
||||
|
||||
|
@ -118,7 +117,6 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
|
|||
match_ast! {
|
||||
match parent {
|
||||
ast::Alias(it) => {
|
||||
tested_by!(goto_def_for_use_alias; force);
|
||||
let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?;
|
||||
let path = use_tree.path()?;
|
||||
let path_segment = path.segment()?;
|
||||
|
@ -203,6 +201,8 @@ impl NameRefClass {
|
|||
}
|
||||
}
|
||||
|
||||
// Note: we don't have unit-tests for this rather important function.
|
||||
// It is primarily exercised via goto definition tests in `ra_ide`.
|
||||
pub fn classify_name_ref(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
name_ref: &ast::NameRef,
|
||||
|
@ -212,22 +212,18 @@ pub fn classify_name_ref(
|
|||
let parent = name_ref.syntax().parent()?;
|
||||
|
||||
if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
|
||||
tested_by!(goto_def_for_methods; force);
|
||||
if let Some(func) = sema.resolve_method_call(&method_call) {
|
||||
return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
|
||||
tested_by!(goto_def_for_fields; force);
|
||||
if let Some(field) = sema.resolve_field(&field_expr) {
|
||||
return Some(NameRefClass::Definition(Definition::Field(field)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(record_field) = ast::RecordField::for_field_name(name_ref) {
|
||||
tested_by!(goto_def_for_record_fields; force);
|
||||
tested_by!(goto_def_for_field_init_shorthand; force);
|
||||
if let Some((field, local)) = sema.resolve_record_field(&record_field) {
|
||||
let field = Definition::Field(field);
|
||||
let res = match local {
|
||||
|
@ -239,7 +235,6 @@ pub fn classify_name_ref(
|
|||
}
|
||||
|
||||
if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
|
||||
tested_by!(goto_def_for_record_field_pats; force);
|
||||
if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
|
||||
let field = Definition::Field(field);
|
||||
return Some(NameRefClass::Definition(field));
|
||||
|
@ -247,7 +242,6 @@ pub fn classify_name_ref(
|
|||
}
|
||||
|
||||
if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
|
||||
tested_by!(goto_def_for_macros; force);
|
||||
if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
|
||||
return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
//!
|
||||
//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
|
||||
|
||||
pub mod marks;
|
||||
pub mod line_index;
|
||||
pub mod line_index_utils;
|
||||
pub mod symbol_index;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
//! See test_utils/src/marks.rs
|
||||
|
||||
test_utils::marks![
|
||||
goto_def_for_macros
|
||||
goto_def_for_use_alias
|
||||
goto_def_for_methods
|
||||
goto_def_for_fields
|
||||
goto_def_for_record_fields
|
||||
goto_def_for_field_init_shorthand
|
||||
goto_def_for_record_field_pats
|
||||
search_filters_by_range
|
||||
];
|
|
@ -12,7 +12,6 @@ use ra_db::{FileId, FileRange, SourceDatabaseExt};
|
|||
use ra_prof::profile;
|
||||
use ra_syntax::{ast, match_ast, AstNode, TextRange, TextSize};
|
||||
use rustc_hash::FxHashMap;
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::{
|
||||
defs::{classify_name_ref, Definition, NameRefClass},
|
||||
|
@ -209,7 +208,6 @@ impl Definition {
|
|||
for (idx, _) in text.match_indices(pat) {
|
||||
let offset: TextSize = idx.try_into().unwrap();
|
||||
if !search_range.contains_inclusive(offset) {
|
||||
tested_by!(search_filters_by_range; force);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! It can be viewed as a dual for `AnalysisChange`.
|
||||
|
||||
use ra_db::{FileId, FilePosition, RelativePathBuf, SourceRootId};
|
||||
use ra_text_edit::{TextEdit, TextSize};
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SourceChange {
|
||||
|
@ -13,6 +13,7 @@ pub struct SourceChange {
|
|||
pub source_file_edits: Vec<SourceFileEdit>,
|
||||
pub file_system_edits: Vec<FileSystemEdit>,
|
||||
pub cursor_position: Option<FilePosition>,
|
||||
pub is_snippet: bool,
|
||||
}
|
||||
|
||||
impl SourceChange {
|
||||
|
@ -28,6 +29,7 @@ impl SourceChange {
|
|||
source_file_edits,
|
||||
file_system_edits,
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +43,7 @@ impl SourceChange {
|
|||
source_file_edits: edits,
|
||||
file_system_edits: vec![],
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +55,7 @@ impl SourceChange {
|
|||
source_file_edits: vec![],
|
||||
file_system_edits: edits,
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +109,6 @@ pub enum FileSystemEdit {
|
|||
pub struct SingleFileChange {
|
||||
pub label: String,
|
||||
pub edit: TextEdit,
|
||||
pub cursor_position: Option<TextSize>,
|
||||
}
|
||||
|
||||
impl SingleFileChange {
|
||||
|
@ -114,7 +117,8 @@ impl SingleFileChange {
|
|||
label: self.label,
|
||||
source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }],
|
||||
file_system_edits: Vec::new(),
|
||||
cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }),
|
||||
cursor_position: None,
|
||||
is_snippet: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Transcraber takes a template, like `fn $ident() {}`, a set of bindings like
|
||||
//! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like
|
||||
//! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}`
|
||||
|
||||
use ra_syntax::SmolStr;
|
||||
|
@ -53,7 +53,8 @@ impl Bindings {
|
|||
pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> {
|
||||
assert!(template.delimiter == None);
|
||||
let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() };
|
||||
expand_subtree(&mut ctx, template)
|
||||
let mut arena: Vec<tt::TokenTree> = Vec::new();
|
||||
expand_subtree(&mut ctx, template, &mut arena)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -73,8 +74,13 @@ struct ExpandCtx<'a> {
|
|||
nesting: Vec<NestingState>,
|
||||
}
|
||||
|
||||
fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
let mut buf: Vec<tt::TokenTree> = Vec::new();
|
||||
fn expand_subtree(
|
||||
ctx: &mut ExpandCtx,
|
||||
template: &tt::Subtree,
|
||||
arena: &mut Vec<tt::TokenTree>,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation
|
||||
let start_elements = arena.len();
|
||||
let mut err = None;
|
||||
for op in parse_template(template) {
|
||||
let op = match op {
|
||||
|
@ -85,25 +91,27 @@ fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult<t
|
|||
}
|
||||
};
|
||||
match op {
|
||||
Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => buf.push(tt.clone()),
|
||||
Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()),
|
||||
Op::TokenTree(tt::TokenTree::Subtree(tt)) => {
|
||||
let ExpandResult(tt, e) = expand_subtree(ctx, tt);
|
||||
let ExpandResult(tt, e) = expand_subtree(ctx, tt, arena);
|
||||
err = err.or(e);
|
||||
buf.push(tt.into());
|
||||
arena.push(tt.into());
|
||||
}
|
||||
Op::Var { name, kind: _ } => {
|
||||
let ExpandResult(fragment, e) = expand_var(ctx, name);
|
||||
err = err.or(e);
|
||||
push_fragment(&mut buf, fragment);
|
||||
push_fragment(arena, fragment);
|
||||
}
|
||||
Op::Repeat { subtree, kind, separator } => {
|
||||
let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator);
|
||||
let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator, arena);
|
||||
err = err.or(e);
|
||||
push_fragment(&mut buf, fragment)
|
||||
push_fragment(arena, fragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: buf }, err)
|
||||
// drain the elements added in this instance of expand_subtree
|
||||
let tts = arena.drain(start_elements..arena.len()).collect();
|
||||
ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err)
|
||||
}
|
||||
|
||||
fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
|
||||
|
@ -155,6 +163,7 @@ fn expand_repeat(
|
|||
template: &tt::Subtree,
|
||||
kind: RepeatKind,
|
||||
separator: Option<Separator>,
|
||||
arena: &mut Vec<tt::TokenTree>,
|
||||
) -> ExpandResult<Fragment> {
|
||||
let mut buf: Vec<tt::TokenTree> = Vec::new();
|
||||
ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false });
|
||||
|
@ -165,7 +174,7 @@ fn expand_repeat(
|
|||
let mut counter = 0;
|
||||
|
||||
loop {
|
||||
let ExpandResult(mut t, e) = expand_subtree(ctx, template);
|
||||
let ExpandResult(mut t, e) = expand_subtree(ctx, template, arena);
|
||||
let nesting_state = ctx.nesting.last_mut().unwrap();
|
||||
if nesting_state.at_end || !nesting_state.hit {
|
||||
break;
|
||||
|
|
|
@ -25,7 +25,7 @@ pub(crate) use token_set::TokenSet;
|
|||
pub use syntax_kind::SyntaxKind;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ParseError(pub String);
|
||||
pub struct ParseError(pub Box<String>);
|
||||
|
||||
/// `TokenSource` abstracts the source of the tokens parser operates on.
|
||||
///
|
||||
|
|
|
@ -192,7 +192,7 @@ impl<'t> Parser<'t> {
|
|||
/// structured errors with spans and notes, like rustc
|
||||
/// does.
|
||||
pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
|
||||
let msg = ParseError(message.into());
|
||||
let msg = ParseError(Box::new(message.into()));
|
||||
self.push_event(Event::Error { msg })
|
||||
}
|
||||
|
||||
|
|
|
@ -266,6 +266,15 @@ impl<'a> SyntaxRewriter<'a> {
|
|||
let replacement = Replacement::Single(with.clone().into());
|
||||
self.replacements.insert(what, replacement);
|
||||
}
|
||||
pub fn replace_with_many<T: Clone + Into<SyntaxElement>>(
|
||||
&mut self,
|
||||
what: &T,
|
||||
with: Vec<SyntaxElement>,
|
||||
) {
|
||||
let what = what.clone().into();
|
||||
let replacement = Replacement::Many(with);
|
||||
self.replacements.insert(what, replacement);
|
||||
}
|
||||
pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) {
|
||||
self.replace(what.syntax(), with.syntax())
|
||||
}
|
||||
|
@ -302,31 +311,41 @@ impl<'a> SyntaxRewriter<'a> {
|
|||
|
||||
fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode {
|
||||
// FIXME: this could be made much faster.
|
||||
let new_children =
|
||||
node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>();
|
||||
let mut new_children = Vec::new();
|
||||
for child in node.children_with_tokens() {
|
||||
self.rewrite_self(&mut new_children, &child);
|
||||
}
|
||||
with_children(node, new_children)
|
||||
}
|
||||
|
||||
fn rewrite_self(
|
||||
&self,
|
||||
acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
|
||||
element: &SyntaxElement,
|
||||
) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> {
|
||||
) {
|
||||
if let Some(replacement) = self.replacement(&element) {
|
||||
return match replacement {
|
||||
match replacement {
|
||||
Replacement::Single(NodeOrToken::Node(it)) => {
|
||||
Some(NodeOrToken::Node(it.green().clone()))
|
||||
acc.push(NodeOrToken::Node(it.green().clone()))
|
||||
}
|
||||
Replacement::Single(NodeOrToken::Token(it)) => {
|
||||
Some(NodeOrToken::Token(it.green().clone()))
|
||||
acc.push(NodeOrToken::Token(it.green().clone()))
|
||||
}
|
||||
Replacement::Delete => None,
|
||||
Replacement::Many(replacements) => {
|
||||
acc.extend(replacements.iter().map(|it| match it {
|
||||
NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()),
|
||||
NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
|
||||
}))
|
||||
}
|
||||
Replacement::Delete => (),
|
||||
};
|
||||
return;
|
||||
}
|
||||
let res = match element {
|
||||
NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
|
||||
NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()),
|
||||
};
|
||||
Some(res)
|
||||
acc.push(res)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,6 +360,7 @@ impl ops::AddAssign for SyntaxRewriter<'_> {
|
|||
enum Replacement {
|
||||
Delete,
|
||||
Single(SyntaxElement),
|
||||
Many(Vec<SyntaxElement>),
|
||||
}
|
||||
|
||||
fn with_children(
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
//! This module contains functions for editing syntax trees. As the trees are
|
||||
//! immutable, all function here return a fresh copy of the tree, instead of
|
||||
//! doing an in-place modification.
|
||||
use std::{iter, ops::RangeInclusive};
|
||||
use std::{
|
||||
fmt, iter,
|
||||
ops::{self, RangeInclusive},
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
|
||||
|
@ -437,6 +440,28 @@ impl From<u8> for IndentLevel {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IndentLevel {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let spaces = " ";
|
||||
let buf;
|
||||
let len = self.0 as usize * 4;
|
||||
let indent = if len <= spaces.len() {
|
||||
&spaces[..len]
|
||||
} else {
|
||||
buf = iter::repeat(' ').take(len).collect::<String>();
|
||||
&buf
|
||||
};
|
||||
fmt::Display::fmt(indent, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<u8> for IndentLevel {
|
||||
type Output = IndentLevel;
|
||||
fn add(self, rhs: u8) -> IndentLevel {
|
||||
IndentLevel(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndentLevel {
|
||||
pub fn from_node(node: &SyntaxNode) -> IndentLevel {
|
||||
let first_token = match node.first_token() {
|
||||
|
@ -453,6 +478,14 @@ impl IndentLevel {
|
|||
IndentLevel(0)
|
||||
}
|
||||
|
||||
/// XXX: this intentionally doesn't change the indent of the very first token.
|
||||
/// Ie, in something like
|
||||
/// ```
|
||||
/// fn foo() {
|
||||
/// 92
|
||||
/// }
|
||||
/// ```
|
||||
/// if you indent the block, the `{` token would stay put.
|
||||
fn increase_indent(self, node: SyntaxNode) -> SyntaxNode {
|
||||
let mut rewriter = SyntaxRewriter::default();
|
||||
node.descendants_with_tokens()
|
||||
|
@ -463,12 +496,7 @@ impl IndentLevel {
|
|||
text.contains('\n')
|
||||
})
|
||||
.for_each(|ws| {
|
||||
let new_ws = make::tokens::whitespace(&format!(
|
||||
"{}{:width$}",
|
||||
ws.syntax().text(),
|
||||
"",
|
||||
width = self.0 as usize * 4
|
||||
));
|
||||
let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,));
|
||||
rewriter.replace(ws.syntax(), &new_ws)
|
||||
});
|
||||
rewriter.rewrite(&node)
|
||||
|
@ -485,7 +513,7 @@ impl IndentLevel {
|
|||
})
|
||||
.for_each(|ws| {
|
||||
let new_ws = make::tokens::whitespace(
|
||||
&ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"),
|
||||
&ws.syntax().text().replace(&format!("\n{}", self), "\n"),
|
||||
);
|
||||
rewriter.replace(ws.syntax(), &new_ws)
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue