Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links

This commit is contained in:
Zac Pullar-Strecker 2020-08-24 21:19:53 +12:00
commit 7bbca7a1b3
1218 changed files with 10753 additions and 8561 deletions

View file

@ -3,3 +3,6 @@ xtask = "run --package xtask --bin xtask --"
install-ra = "run --package xtask --bin xtask -- install" # for backwards compat
tq = "test -- -q"
qt = "tq"
[target.x86_64-pc-windows-msvc]
linker = "rust-lld"

2
.gitattributes vendored
View file

@ -1,5 +1,5 @@
* text=auto eol=lf
crates/ra_syntax/test_data/** -text eof=LF
crates/syntax/test_data/** -text eof=LF
# Older git versions try to fix line endings on images, this prevents it.
*.png binary
*.jpg binary

12
.github/FUNDING.yml vendored
View file

@ -1,12 +1,2 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
github: rust-analyzer
open_collective: rust-analyzer
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View file

@ -16,20 +16,6 @@ env:
RUSTUP_MAX_RETRIES: 10
jobs:
# rust-audit:
# name: Audit Rust vulnerabilities
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v2
# - uses: actions-rs/install@v0.1
# with:
# crate: cargo-audit
# use-tool-cache: true
# - run: cargo audit
rust:
name: Rust
runs-on: ${{ matrix.os }}
@ -84,15 +70,14 @@ jobs:
- name: Prepare cache
run: cargo xtask pre-cache
- name: Prepare cache 2
if: matrix.os == 'windows-latest'
run: Remove-Item ./target/debug/xtask.exe, ./target/debug/deps/xtask.exe
# Weird target to catch non-portable code
rust-power:
name: Rust Power
# Weird targets to catch non-portable code
rust-cross:
name: Rust Cross
runs-on: ubuntu-latest
env:
targets: "powerpc-unknown-linux-gnu x86_64-unknown-linux-musl"
steps:
- name: Checkout repository
uses: actions/checkout@v2
@ -103,7 +88,9 @@ jobs:
toolchain: stable
profile: minimal
override: true
target: 'powerpc-unknown-linux-gnu'
- name: Install Rust targets
run: rustup target add ${{ env.targets }}
- name: Cache cargo directories
uses: actions/cache@v2
@ -114,14 +101,17 @@ jobs:
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Check
run: cargo check --target=powerpc-unknown-linux-gnu --all-targets
run: |
for target in ${{ env.targets }}; do
cargo check --target=$target --all-targets
done
typescript:
name: TypeScript
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}

875
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,37 +2,34 @@
members = [ "crates/*", "xtask/" ]
[profile.dev]
# disabling debug info speeds up builds a bunch,
# Disabling debug info speeds up builds a bunch,
# and we don't rely on it for debugging that much.
debug = 0
[profile.dev.package]
# These speed up local tests.
rowan.opt-level = 3
rustc-hash.opt-level = 3
smol_str.opt-level = 3
text-size.opt-level = 3
# This speeds up `cargo xtask dist`.
miniz_oxide.opt-level = 3
[profile.release]
incremental = true
debug = 0 # set this to 1 or 2 to get more useful backtraces in debugger
debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger.
# ideally, we would use `build-override` here, but some crates are also
# needed at run-time and we end up compiling them twice
[profile.release.package.proc-macro2]
opt-level = 0
[profile.release.package.quote]
opt-level = 0
[profile.release.package.syn]
opt-level = 0
[profile.release.package.serde_derive]
opt-level = 0
[profile.release.package.chalk-derive]
opt-level = 0
[profile.release.package.salsa-macros]
opt-level = 0
[profile.release.package.tracing-attributes]
opt-level = 0
[profile.release.package.xtask]
opt-level = 0
# Gzipping the artifacts is up to 10 times faster with optimizations (`cargo xtask dist`).
# `miniz_oxide` is the direct dependency of `flate2` which does all the heavy lifting
[profile.dev.package.miniz_oxide]
opt-level = 3
# Ideally, we would use `build-override` here, but some crates are also
# needed at run-time and we end up compiling them twice.
[profile.release.package]
chalk-derive.opt-level = 0
proc-macro2.opt-level = 0
quote.opt-level = 0
salsa-macros.opt-level = 0
serde_derive.opt-level = 0
syn.opt-level = 0
tracing-attributes.opt-level = 0
xtask.opt-level = 0
[patch.'crates-io']
# rowan = { path = "../rowan" }

View file

@ -39,7 +39,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0
* Website: https://rust-analyzer.github.io/
* Metrics: https://rust-analyzer.github.io/metrics/
* API docs: https://rust-analyzer.github.io/rust-analyzer/ra_ide/
* API docs: https://rust-analyzer.github.io/rust-analyzer/ide/
## License

View file

@ -1,9 +1,8 @@
status = [
"Rust (ubuntu-latest)",
"Rust (windows-latest)",
"Rust (macos-latest)",
# "Rust (macos-latest)",
"TypeScript (ubuntu-latest)",
"TypeScript (windows-latest)",
"TypeScript (macos-latest)",
]
delete_merged_branches = true

View file

@ -1,9 +1,9 @@
[package]
edition = "2018"
name = "ra_arena"
version = "0.1.0"
authors = ["rust-analyzer developers"]
name = "arena"
version = "0.0.0"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false

23
crates/assists/Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "assists"
version = "0.0.0"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false
[dependencies]
rustc-hash = "1.1.0"
itertools = "0.9.0"
either = "1.5.3"
stdx = { path = "../stdx" }
syntax = { path = "../syntax" }
text_edit = { path = "../text_edit" }
profile = { path = "../profile" }
base_db = { path = "../base_db" }
ide_db = { path = "../ide_db" }
hir = { path = "../hir" }
test_utils = { path = "../test_utils" }

View file

@ -3,19 +3,19 @@
use std::mem;
use algo::find_covering_element;
use base_db::{FileId, FileRange};
use hir::Semantics;
use ra_db::{FileId, FileRange};
use ra_fmt::{leading_indent, reindent};
use ra_ide_db::{
use ide_db::{
label::Label,
source_change::{SourceChange, SourceFileEdit},
RootDatabase,
};
use ra_syntax::{
use syntax::{
algo::{self, find_node_at_offset, SyntaxRewriter},
AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange, TextSize,
TokenAtOffset,
};
use ra_text_edit::TextEditBuilder;
use text_edit::{TextEdit, TextEditBuilder};
use crate::{
assist_config::{AssistConfig, SnippetCap},
@ -73,6 +73,10 @@ impl<'a> AssistContext<'a> {
self.sema.db
}
pub(crate) fn source_file(&self) -> &SourceFile {
&self.source_file
}
// NB, this ignores active selection.
pub(crate) fn offset(&self) -> TextSize {
self.frange.range.start()
@ -154,8 +158,9 @@ impl Assists {
if !self.is_allowed(&id) {
return None;
}
let label = Assist::new(id, label.into(), None, target);
self.add_impl(label, f)
let label = Label::new(label.into());
let assist = Assist { id, label, group: None, target };
self.add_impl(assist, f)
}
pub(crate) fn add_group(
@ -169,12 +174,12 @@ impl Assists {
if !self.is_allowed(&id) {
return None;
}
let label = Assist::new(id, label.into(), Some(group.clone()), target);
self.add_impl(label, f)
let label = Label::new(label.into());
let assist = Assist { id, label, group: Some(group.clone()), target };
self.add_impl(assist, f)
}
fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
let source_change = if self.resolve {
let mut builder = AssistBuilder::new(self.file);
f(&mut builder);
@ -183,7 +188,7 @@ impl Assists {
None
};
self.buf.push((label, source_change));
self.buf.push((assist, source_change));
Some(())
}
@ -210,7 +215,7 @@ pub(crate) struct AssistBuilder {
impl AssistBuilder {
pub(crate) fn new(file_id: FileId) -> AssistBuilder {
AssistBuilder {
edit: TextEditBuilder::default(),
edit: TextEdit::builder(),
file_id,
is_snippet: false,
change: SourceChange::default(),
@ -265,20 +270,6 @@ impl AssistBuilder {
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)
}
/// Replaces specified `node` of text with a given string, reindenting the
/// string to maintain `node`'s existing indent.
// FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
pub(crate) fn replace_node_and_indent(
&mut self,
node: &SyntaxNode,
replace_with: impl Into<String>,
) {
let mut replace_with = replace_with.into();
if let Some(indent) = leading_indent(node) {
replace_with = reindent(&replace_with, &indent)
}
self.replace(node.text_range(), replace_with)
}
pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
let node = rewriter.rewrite_root().unwrap();
let new = rewriter.rewrite(&node);

View file

@ -2,13 +2,24 @@
use rustc_hash::FxHashMap;
use hir::{HirDisplay, PathResolution, SemanticsScope};
use ra_syntax::{
use syntax::{
algo::SyntaxRewriter,
ast::{self, AstNode},
};
pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
SyntaxRewriter::from_fn(|element| match element {
syntax::SyntaxElement::Node(n) => {
let replacement = transformer.get_substitution(&n)?;
Some(replacement.into())
}
_ => None,
})
.rewrite_ast(&node)
}
pub trait AstTransform<'a> {
fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>;
fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>;
fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
@ -22,7 +33,7 @@ pub trait AstTransform<'a> {
struct NullTransformer;
impl<'a> AstTransform<'a> for NullTransformer {
fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
fn get_substitution(&self, _node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
None
}
fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
@ -32,7 +43,7 @@ impl<'a> AstTransform<'a> for NullTransformer {
pub struct SubstituteTypeParams<'a> {
source_scope: &'a SemanticsScope<'a>,
substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
substs: FxHashMap<hir::TypeParam, ast::Type>,
previous: Box<dyn AstTransform<'a> + 'a>,
}
@ -51,7 +62,7 @@ impl<'a> SubstituteTypeParams<'a> {
// this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
.skip(1)
// The actual list of trait type parameters may be longer than the one
// used in the `impl` block due to trailing default type parametrs.
// used in the `impl` block due to trailing default type parameters.
// For that case we extend the `substs` with an empty iterator so we
// can still hit those trailing values and check if they actually have
// a default type. If they do, go for that type from `hir` to `ast` so
@ -63,7 +74,7 @@ impl<'a> SubstituteTypeParams<'a> {
let default = k.default(source_scope.db)?;
Some((
k,
ast::make::type_ref(
ast::make::ty(
&default
.display_source_code(source_scope.db, source_scope.module()?.into())
.ok()?,
@ -79,35 +90,35 @@ impl<'a> SubstituteTypeParams<'a> {
};
// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
// trait ref, and then go from the types in the substs back to the syntax)
fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::TypeRef>> {
let target_trait = impl_def.target_trait()?;
// trait ref, and then go from the types in the substs back to the syntax).
fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
let target_trait = impl_def.trait_()?;
let path_type = match target_trait {
ast::TypeRef::PathType(path) => path,
ast::Type::PathType(path) => path,
_ => return None,
};
let type_arg_list = path_type.path()?.segment()?.type_arg_list()?;
let generic_arg_list = path_type.path()?.segment()?.generic_arg_list()?;
let mut result = Vec::new();
for type_arg in type_arg_list.type_args() {
let type_arg: ast::TypeArg = type_arg;
result.push(type_arg.type_ref()?);
for generic_arg in generic_arg_list.generic_args() {
match generic_arg {
ast::GenericArg::TypeArg(type_arg) => result.push(type_arg.ty()?),
ast::GenericArg::AssocTypeArg(_)
| ast::GenericArg::LifetimeArg(_)
| ast::GenericArg::ConstArg(_) => (),
}
}
Some(result)
}
}
fn get_substitution_inner(
&self,
node: &ra_syntax::SyntaxNode,
) -> Option<ra_syntax::SyntaxNode> {
let type_ref = ast::TypeRef::cast(node.clone())?;
fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
let type_ref = ast::Type::cast(node.clone())?;
let path = match &type_ref {
ast::TypeRef::PathType(path_type) => path_type.path()?,
ast::Type::PathType(path_type) => path_type.path()?,
_ => return None,
};
// FIXME: use `hir::Path::from_src` instead.
#[allow(deprecated)]
let path = hir::Path::from_ast(path)?;
let resolution = self.source_scope.resolve_hir_path(&path)?;
let resolution = self.source_scope.speculative_resolve(&path)?;
match resolution {
hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
_ => None,
@ -116,7 +127,7 @@ impl<'a> SubstituteTypeParams<'a> {
}
impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
}
fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
@ -135,10 +146,7 @@ impl<'a> QualifyPaths<'a> {
Self { target_scope, source_scope, previous: Box::new(NullTransformer) }
}
fn get_substitution_inner(
&self,
node: &ra_syntax::SyntaxNode,
) -> Option<ra_syntax::SyntaxNode> {
fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
// FIXME handle value ns?
let from = self.target_scope.module()?;
let p = ast::Path::cast(node.clone())?;
@ -146,10 +154,7 @@ impl<'a> QualifyPaths<'a> {
// don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
return None;
}
// FIXME: use `hir::Path::from_src` instead.
#[allow(deprecated)]
let hir_path = hir::Path::from_ast(p.clone());
let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
let resolution = self.source_scope.speculative_resolve(&p)?;
match resolution {
PathResolution::Def(def) => {
let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
@ -157,7 +162,7 @@ impl<'a> QualifyPaths<'a> {
let type_args = p
.segment()
.and_then(|s| s.type_arg_list())
.and_then(|s| s.generic_arg_list())
.map(|arg_list| apply(self, arg_list));
if let Some(type_args) = type_args {
let last_segment = path.segment().unwrap();
@ -175,19 +180,8 @@ impl<'a> QualifyPaths<'a> {
}
}
pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
SyntaxRewriter::from_fn(|element| match element {
ra_syntax::SyntaxElement::Node(n) => {
let replacement = transformer.get_substitution(&n)?;
Some(replacement.into())
}
_ => None,
})
.rewrite_ast(&node)
}
impl<'a> AstTransform<'a> for QualifyPaths<'a> {
fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
}
fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {

View file

@ -1,10 +1,10 @@
use ra_syntax::{
use itertools::Itertools;
use syntax::{
ast::{self, AstNode},
Direction, SmolStr,
SyntaxKind::{IDENT, WHITESPACE},
TextRange, TextSize,
};
use stdx::SepBy;
use crate::{
assist_context::{AssistContext, Assists},
@ -61,9 +61,9 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
.filter(|t| t != trait_token.text())
.collect::<Vec<SmolStr>>();
let has_more_derives = !new_attr_input.is_empty();
let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
if has_more_derives {
let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
builder.replace(input.syntax().text_range(), new_attr_input);
} else {
let attr_range = attr.syntax().text_range();

View file

@ -1,5 +1,5 @@
use hir::HirDisplay;
use ra_syntax::{
use syntax::{
ast::{self, AstNode, LetStmt, NameOwner},
TextRange,
};
@ -27,7 +27,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
let expr = let_stmt.initializer()?;
// Must be a binding
let pat = match let_stmt.pat()? {
ast::Pat::BindPat(bind_pat) => bind_pat,
ast::Pat::IdentPat(bind_pat) => bind_pat,
_ => return None,
};
let pat_range = pat.syntax().text_range();
@ -46,7 +46,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
// and it has no placeholders
let ascribed_ty = let_stmt.ty();
if let Some(ty) = &ascribed_ty {
if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
if ty.syntax().descendants().find_map(ast::InferType::cast).is_none() {
return None;
}
}

View file

@ -1,5 +1,5 @@
use hir::HasSource;
use ra_syntax::{
use syntax::{
ast::{
self,
edit::{self, AstNodeEdit, IndentLevel},
@ -48,7 +48,6 @@ enum AddMissingImplMembersMode {
// fn foo(&self) -> u32 {
// ${0:todo!()}
// }
//
// }
// ```
pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@ -89,8 +88,8 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
// impl Trait for () {
// Type X = ();
// fn foo(&self) {}
// $0fn bar(&self) {}
//
// $0fn bar(&self) {}
// }
// ```
pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@ -110,7 +109,7 @@ fn add_missing_impl_members_inner(
assist_id: &'static str,
label: &'static str,
) -> Option<()> {
let _p = ra_prof::profile("add_missing_impl_members_inner");
let _p = profile::span("add_missing_impl_members_inner");
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
let impl_item_list = impl_def.assoc_item_list()?;
@ -240,15 +239,18 @@ struct S;
impl Foo for S {
fn bar(&self) {}
$0type Output;
const CONST: usize = 42;
fn foo(&self) {
todo!()
}
fn baz(&self) {
todo!()
}
}"#,
);
}
@ -281,10 +283,10 @@ struct S;
impl Foo for S {
fn bar(&self) {}
fn foo(&self) {
${0:todo!()}
}
}"#,
);
}
@ -599,6 +601,7 @@ trait Foo {
struct S;
impl Foo for S {
$0type Output;
fn foo(&self) {
todo!()
}
@ -705,6 +708,58 @@ trait Tr {
impl Tr for () {
$0type Ty;
}"#,
)
}
#[test]
fn test_whitespace_fixup_preserves_bad_tokens() {
check_assist(
add_missing_impl_members,
r#"
trait Tr {
fn foo();
}
impl Tr for ()<|> {
+++
}"#,
r#"
trait Tr {
fn foo();
}
impl Tr for () {
fn foo() {
${0:todo!()}
}
+++
}"#,
)
}
#[test]
fn test_whitespace_fixup_preserves_comments() {
check_assist(
add_missing_impl_members,
r#"
trait Tr {
fn foo();
}
impl Tr for ()<|> {
// very important
}"#,
r#"
trait Tr {
fn foo();
}
impl Tr for () {
fn foo() {
${0:todo!()}
}
// very important
}"#,
)
}

View file

@ -1,5 +1,5 @@
use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass};
use ra_syntax::{ast, AstNode, SyntaxKind, T};
use ide_db::defs::{classify_name_ref, Definition, NameRefClass};
use syntax::{ast, AstNode, SyntaxKind, T};
use test_utils::mark;
use crate::{
@ -41,7 +41,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
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,
NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None,
};
let fun = match def {
Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,

View file

@ -1,10 +1,10 @@
use ra_syntax::ast::{self, AstNode};
use syntax::ast::{self, AstNode};
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
// Assist: apply_demorgan
//
// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
// This transforms expressions of the form `!l || !r` into `!(l && r)`.
// This also works with `&&`. This assist can only be applied with the cursor
// on either `||` or `&&`, with both operands being a negation of some kind.

View file

@ -5,13 +5,12 @@ use hir::{
AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
Type,
};
use ra_ide_db::{imports_locator, RootDatabase};
use ra_prof::profile;
use ra_syntax::{
use ide_db::{imports_locator, RootDatabase};
use rustc_hash::FxHashSet;
use syntax::{
ast::{self, AstNode},
SyntaxNode,
};
use rustc_hash::FxHashSet;
use crate::{
utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel,
@ -54,7 +53,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|builder| {
insert_use_statement(
&auto_import_assets.syntax_under_caret,
&import,
&import.to_string(),
ctx,
builder.text_edit_builder(),
);
@ -130,7 +129,7 @@ impl AutoImportAssets {
}
fn search_for_imports(&self, ctx: &AssistContext) -> BTreeSet<ModPath> {
let _p = profile("auto_import::search_for_imports");
let _p = profile::span("auto_import::search_for_imports");
let db = ctx.db();
let current_crate = self.module_with_name_to_import.krate();
imports_locator::find_imports(&ctx.sema, current_crate, &self.get_search_query())
@ -240,7 +239,7 @@ impl ImportCandidate {
return None;
}
Some(Self::TraitMethod(
sema.type_of_expr(&method_call.expr()?)?,
sema.type_of_expr(&method_call.receiver()?)?,
method_call.name_ref()?.syntax().to_string(),
))
}

View file

@ -1,10 +1,12 @@
use ra_syntax::{
ast::{self, BlockExpr, Expr, LoopBodyOwner},
use std::iter;
use syntax::{
ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
AstNode, SyntaxNode,
};
use test_utils::mark;
use crate::{AssistContext, AssistId, AssistKind, Assists};
use test_utils::mark;
// Assist: change_return_type_to_result
//
@ -44,7 +46,13 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
tail_return_expr_collector.collect_tail_exprs(block_expr);
for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
let ok_wrapped = make::expr_call(
make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
"Ok",
)))),
make::arg_list(iter::once(ret_expr_arg.clone())),
);
builder.replace_ast(ret_expr_arg, ok_wrapped);
}
match ctx.config.snippet_cap {
@ -60,7 +68,7 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
}
struct TailReturnCollector {
exprs_to_wrap: Vec<SyntaxNode>,
exprs_to_wrap: Vec<ast::Expr>,
}
impl TailReturnCollector {
@ -74,6 +82,7 @@ impl TailReturnCollector {
let expr = match &stmt {
ast::Stmt::ExprStmt(stmt) => stmt.expr(),
ast::Stmt::LetStmt(stmt) => stmt.initializer(),
ast::Stmt::Item(_) => continue,
};
if let Some(expr) = &expr {
self.handle_exprs(expr, collect_break);
@ -85,7 +94,8 @@ impl TailReturnCollector {
if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
for last_expr in last_exprs {
let last_expr = match last_expr {
NodeType::Node(expr) | NodeType::Leaf(expr) => expr,
NodeType::Node(expr) => expr,
NodeType::Leaf(expr) => expr.syntax().clone(),
};
if let Some(last_expr) = Expr::cast(last_expr.clone()) {
@ -94,6 +104,7 @@ impl TailReturnCollector {
let expr_stmt = match &expr_stmt {
ast::Stmt::ExprStmt(stmt) => stmt.expr(),
ast::Stmt::LetStmt(stmt) => stmt.initializer(),
ast::Stmt::Item(_) => None,
};
if let Some(expr) = &expr_stmt {
self.handle_exprs(expr, collect_break);
@ -111,12 +122,12 @@ impl TailReturnCollector {
}
Expr::ReturnExpr(ret_expr) => {
if let Some(ret_expr_arg) = &ret_expr.expr() {
self.exprs_to_wrap.push(ret_expr_arg.syntax().clone());
self.exprs_to_wrap.push(ret_expr_arg.clone());
}
}
Expr::BreakExpr(break_expr) if collect_break => {
if let Some(break_expr_arg) = &break_expr.expr() {
self.exprs_to_wrap.push(break_expr_arg.syntax().clone());
self.exprs_to_wrap.push(break_expr_arg.clone());
}
}
Expr::IfExpr(if_expr) => {
@ -164,14 +175,11 @@ impl TailReturnCollector {
NodeType::Leaf(expr) => {
self.exprs_to_wrap.push(expr.clone());
}
NodeType::Node(expr) => match &Expr::cast(expr.clone()) {
Some(last_expr) => {
self.fetch_tail_exprs(last_expr);
NodeType::Node(expr) => {
if let Some(last_expr) = Expr::cast(expr.clone()) {
self.fetch_tail_exprs(&last_expr);
}
None => {
self.exprs_to_wrap.push(expr.clone());
}
},
}
}
}
}
@ -180,7 +188,7 @@ impl TailReturnCollector {
#[derive(Debug)]
enum NodeType {
Leaf(SyntaxNode),
Leaf(ast::Expr),
Node(SyntaxNode),
}
@ -231,26 +239,26 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
Some(arms)
}
Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e.syntax().clone())]),
Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
Expr::CallExpr(call_expr) => Some(vec![NodeType::Leaf(call_expr.syntax().clone())]),
Expr::Literal(lit_expr) => Some(vec![NodeType::Leaf(lit_expr.syntax().clone())]),
Expr::TupleExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::ArrayExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::ParenExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::PathExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::Label(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::RecordExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::IndexExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::MethodCallExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::AwaitExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::CastExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::RefExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::PrefixExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::RangeExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::BinExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::MacroCall(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::BoxExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
Expr::CallExpr(_)
| Expr::Literal(_)
| Expr::TupleExpr(_)
| Expr::ArrayExpr(_)
| Expr::ParenExpr(_)
| Expr::PathExpr(_)
| Expr::RecordExpr(_)
| Expr::IndexExpr(_)
| Expr::MethodCallExpr(_)
| Expr::AwaitExpr(_)
| Expr::CastExpr(_)
| Expr::RefExpr(_)
| Expr::PrefixExpr(_)
| Expr::RangeExpr(_)
| Expr::BinExpr(_)
| Expr::MacroCall(_)
| Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
_ => None,
}
}

View file

@ -1,4 +1,4 @@
use ra_syntax::{
use syntax::{
ast::{self, NameOwner, VisibilityOwner},
AstNode,
SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, VISIBILITY},

View file

@ -1,6 +1,6 @@
use std::{iter::once, ops::RangeInclusive};
use ra_syntax::{
use syntax::{
algo::replace_children,
ast::{
self,
@ -51,11 +51,11 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
// Check if there is an IfLet that we can handle.
let if_let_pat = match cond.pat() {
None => None, // No IfLet, supported.
Some(ast::Pat::TupleStructPat(pat)) if pat.args().count() == 1 => {
Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
let path = pat.path()?;
match path.qualifier() {
None => {
let bound_ident = pat.args().next().unwrap();
let bound_ident = pat.fields().next().unwrap();
Some((path, bound_ident))
}
Some(_) => return None,
@ -123,7 +123,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
let happy_arm = {
let pat = make::tuple_struct_pat(
path,
once(make::bind_pat(make::name("it")).into()),
once(make::ident_pat(make::name("it")).into()),
);
let expr = {
let name_ref = make::name_ref("it");
@ -136,7 +136,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
let sad_arm = make::match_arm(
// FIXME: would be cool to use `None` or `Err(_)` if appropriate
once(make::placeholder_pat().into()),
once(make::wildcard_pat().into()),
early_expression,
);
@ -144,7 +144,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
};
let let_stmt = make::let_stmt(
make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
make::ident_pat(make::name(&bound_ident.syntax().to_string())).into(),
Some(match_expr),
);
let let_stmt = let_stmt.indent(if_indent_level);

View file

@ -0,0 +1,385 @@
use either::Either;
use hir::{AssocItem, MacroDef, ModuleDef, Name, PathResolution, ScopeDef, SemanticsScope};
use ide_db::{
defs::{classify_name_ref, Definition, NameRefClass},
RootDatabase,
};
use syntax::{algo, ast, match_ast, AstNode, SyntaxNode, SyntaxToken, T};
use crate::{
assist_context::{AssistBuilder, AssistContext, Assists},
AssistId, AssistKind,
};
// Assist: expand_glob_import
//
// Expands glob imports.
//
// ```
// mod foo {
// pub struct Bar;
// pub struct Baz;
// }
//
// use foo::*<|>;
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
// ->
// ```
// mod foo {
// pub struct Bar;
// pub struct Baz;
// }
//
// use foo::{Baz, Bar};
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let star = ctx.find_token_at_offset(T![*])?;
let mod_path = find_mod_path(&star)?;
let module = match ctx.sema.resolve_path(&mod_path)? {
PathResolution::Def(ModuleDef::Module(it)) => it,
_ => return None,
};
let source_file = ctx.source_file();
let scope = ctx.sema.scope_at_offset(source_file.syntax(), ctx.offset());
let defs_in_mod = find_defs_in_mod(ctx, scope, module)?;
let name_refs_in_source_file =
source_file.syntax().descendants().filter_map(ast::NameRef::cast).collect();
let used_names = find_used_names(ctx, defs_in_mod, name_refs_in_source_file);
let parent = star.parent().parent()?;
acc.add(
AssistId("expand_glob_import", AssistKind::RefactorRewrite),
"Expand glob import",
parent.text_range(),
|builder| {
replace_ast(builder, &parent, mod_path, used_names);
},
)
}
fn find_mod_path(star: &SyntaxToken) -> Option<ast::Path> {
star.ancestors().find_map(|n| ast::UseTree::cast(n).and_then(|u| u.path()))
}
#[derive(PartialEq)]
enum Def {
ModuleDef(ModuleDef),
MacroDef(MacroDef),
}
impl Def {
fn name(&self, db: &RootDatabase) -> Option<Name> {
match self {
Def::ModuleDef(def) => def.name(db),
Def::MacroDef(def) => def.name(db),
}
}
}
fn find_defs_in_mod(
ctx: &AssistContext,
from: SemanticsScope<'_>,
module: hir::Module,
) -> Option<Vec<Def>> {
let module_scope = module.scope(ctx.db(), from.module());
let mut defs = vec![];
for (_, def) in module_scope {
match def {
ScopeDef::ModuleDef(def) => defs.push(Def::ModuleDef(def)),
ScopeDef::MacroDef(def) => defs.push(Def::MacroDef(def)),
_ => continue,
}
}
Some(defs)
}
fn find_used_names(
ctx: &AssistContext,
defs_in_mod: Vec<Def>,
name_refs_in_source_file: Vec<ast::NameRef>,
) -> Vec<Name> {
let defs_in_source_file = name_refs_in_source_file
.iter()
.filter_map(|r| classify_name_ref(&ctx.sema, r))
.filter_map(|rc| match rc {
NameRefClass::Definition(Definition::ModuleDef(def)) => Some(Def::ModuleDef(def)),
NameRefClass::Definition(Definition::Macro(def)) => Some(Def::MacroDef(def)),
_ => None,
})
.collect::<Vec<Def>>();
defs_in_mod
.iter()
.filter(|def| {
if let Def::ModuleDef(ModuleDef::Trait(tr)) = def {
for item in tr.items(ctx.db()) {
if let AssocItem::Function(f) = item {
if defs_in_source_file.contains(&Def::ModuleDef(ModuleDef::Function(f))) {
return true;
}
}
}
}
defs_in_source_file.contains(def)
})
.filter_map(|d| d.name(ctx.db()))
.collect()
}
fn replace_ast(
builder: &mut AssistBuilder,
node: &SyntaxNode,
path: ast::Path,
used_names: Vec<Name>,
) {
let replacement: Either<ast::UseTree, ast::UseTreeList> = match used_names.as_slice() {
[name] => Either::Left(ast::make::use_tree(
ast::make::path_from_text(&format!("{}::{}", path, name)),
None,
None,
false,
)),
names => Either::Right(ast::make::use_tree_list(names.iter().map(|n| {
ast::make::use_tree(ast::make::path_from_text(&n.to_string()), None, None, false)
}))),
};
let mut replace_node = |replacement: Either<ast::UseTree, ast::UseTreeList>| {
algo::diff(node, &replacement.either(|u| u.syntax().clone(), |ut| ut.syntax().clone()))
.into_text_edit(builder.text_edit_builder());
};
match_ast! {
match node {
ast::UseTree(use_tree) => {
replace_node(replacement);
},
ast::UseTreeList(use_tree_list) => {
replace_node(replacement);
},
ast::Use(use_item) => {
builder.replace_ast(use_item, ast::make::use_(replacement.left_or_else(|ut| ast::make::use_tree(path, Some(ut), None, false))));
},
_ => {},
}
}
}
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
use super::*;
#[test]
fn expanding_glob_import() {
check_assist(
expand_glob_import,
r"
mod foo {
pub struct Bar;
pub struct Baz;
pub struct Qux;
pub fn f() {}
}
use foo::*<|>;
fn qux(bar: Bar, baz: Baz) {
f();
}
",
r"
mod foo {
pub struct Bar;
pub struct Baz;
pub struct Qux;
pub fn f() {}
}
use foo::{Baz, Bar, f};
fn qux(bar: Bar, baz: Baz) {
f();
}
",
)
}
#[test]
fn expanding_glob_import_with_existing_explicit_names() {
check_assist(
expand_glob_import,
r"
mod foo {
pub struct Bar;
pub struct Baz;
pub struct Qux;
pub fn f() {}
}
use foo::{*<|>, f};
fn qux(bar: Bar, baz: Baz) {
f();
}
",
r"
mod foo {
pub struct Bar;
pub struct Baz;
pub struct Qux;
pub fn f() {}
}
use foo::{Baz, Bar, f};
fn qux(bar: Bar, baz: Baz) {
f();
}
",
)
}
#[test]
fn expanding_nested_glob_import() {
check_assist(
expand_glob_import,
r"
mod foo {
mod bar {
pub struct Bar;
pub struct Baz;
pub struct Qux;
pub fn f() {}
}
mod baz {
pub fn g() {}
}
}
use foo::{bar::{*<|>, f}, baz::*};
fn qux(bar: Bar, baz: Baz) {
f();
g();
}
",
r"
mod foo {
mod bar {
pub struct Bar;
pub struct Baz;
pub struct Qux;
pub fn f() {}
}
mod baz {
pub fn g() {}
}
}
use foo::{bar::{Baz, Bar, f}, baz::*};
fn qux(bar: Bar, baz: Baz) {
f();
g();
}
",
)
}
#[test]
fn expanding_glob_import_with_macro_defs() {
check_assist(
expand_glob_import,
r"
//- /lib.rs crate:foo
#[macro_export]
macro_rules! bar {
() => ()
}
pub fn baz() {}
//- /main.rs crate:main deps:foo
use foo::*<|>;
fn main() {
bar!();
baz();
}
",
r"
use foo::{bar, baz};
fn main() {
bar!();
baz();
}
",
)
}
#[test]
fn expanding_glob_import_with_trait_method_uses() {
check_assist(
expand_glob_import,
r"
//- /lib.rs crate:foo
pub trait Tr {
fn method(&self) {}
}
impl Tr for () {}
//- /main.rs crate:main deps:foo
use foo::*<|>;
fn main() {
().method();
}
",
r"
use foo::Tr;
fn main() {
().method();
}
",
)
}
#[test]
fn expanding_is_not_applicable_if_cursor_is_not_in_star_token() {
check_assist_not_applicable(
expand_glob_import,
r"
mod foo {
pub struct Bar;
pub struct Baz;
pub struct Qux;
}
use foo::Bar<|>;
fn qux(bar: Bar, baz: Baz) {}
",
)
}
}

View file

@ -1,13 +1,12 @@
use base_db::FileId;
use hir::{EnumVariant, Module, ModuleDef, Name};
use ra_db::FileId;
use ra_fmt::leading_indent;
use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
use ra_syntax::{
algo::find_node_at_offset,
ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
SourceFile, SyntaxNode, TextRange, TextSize,
};
use ide_db::{defs::Definition, search::Reference, RootDatabase};
use rustc_hash::FxHashSet;
use syntax::{
algo::find_node_at_offset,
ast::{self, edit::IndentLevel, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
SourceFile, TextRange, TextSize,
};
use crate::{
assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
@ -54,7 +53,7 @@ pub(crate) fn extract_struct_from_enum_variant(
target,
|builder| {
let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
let res = definition.find_usages(&ctx.sema, None);
let res = definition.usages(&ctx.sema).all();
let start_offset = variant.parent_enum().syntax().text_range().start();
let mut visited_modules_set = FxHashSet::default();
visited_modules_set.insert(current_module);
@ -72,7 +71,7 @@ pub(crate) fn extract_struct_from_enum_variant(
}
extract_struct_def(
builder,
enum_ast.syntax(),
&enum_ast,
&variant_name,
&field_list.to_string(),
start_offset,
@ -107,14 +106,20 @@ fn insert_import(
if let Some(mut mod_path) = mod_path {
mod_path.segments.pop();
mod_path.segments.push(variant_hir_name.clone());
insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
insert_use_statement(
path.syntax(),
&mod_path.to_string(),
ctx,
builder.text_edit_builder(),
);
}
Some(())
}
// FIXME: this should use strongly-typed `make`, rather than string manipulation.
fn extract_struct_def(
builder: &mut AssistBuilder,
enum_ast: &SyntaxNode,
enum_: &ast::Enum,
variant_name: &str,
variant_list: &str,
start_offset: TextSize,
@ -126,11 +131,7 @@ fn extract_struct_def(
} else {
"".to_string()
};
let indent = if let Some(indent) = leading_indent(enum_ast) {
indent.to_string()
} else {
"".to_string()
};
let indent = IndentLevel::from_node(enum_.syntax());
let struct_def = format!(
r#"{}struct {}{};

View file

@ -1,11 +1,11 @@
use ra_syntax::{
use stdx::format_to;
use syntax::{
ast::{self, AstNode},
SyntaxKind::{
BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR,
BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR,
},
SyntaxNode,
};
use stdx::format_to;
use test_utils::mark;
use crate::{AssistContext, AssistId, AssistKind, Assists};
@ -148,7 +148,7 @@ impl Anchor {
}
if let Some(parent) = node.parent() {
if parent.kind() == MATCH_ARM || parent.kind() == LAMBDA_EXPR {
if parent.kind() == MATCH_ARM || parent.kind() == CLOSURE_EXPR {
return Some(Anchor::WrapInBlock(node));
}
}

View file

@ -1,9 +1,9 @@
use std::iter;
use hir::{Adt, HasSource, ModuleDef, Semantics};
use ide_db::RootDatabase;
use itertools::Itertools;
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
use test_utils::mark;
use crate::{
@ -43,7 +43,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
if arms.len() == 1 {
if let Some(Pat::PlaceholderPat(..)) = arms[0].pat() {
if let Some(Pat::WildcardPat(..)) = arms[0].pat() {
arms.clear();
}
}
@ -116,17 +116,15 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
match (first_new_arm, ctx.config.snippet_cap) {
(Some(first_new_arm), Some(cap)) => {
let extend_lifetime;
let cursor = match first_new_arm
.syntax()
.descendants()
.find_map(ast::PlaceholderPat::cast)
{
Some(it) => {
extend_lifetime = it.syntax().clone();
Cursor::Replace(&extend_lifetime)
}
None => Cursor::Before(first_new_arm.syntax()),
};
let cursor =
match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast)
{
Some(it) => {
extend_lifetime = it.syntax().clone();
Cursor::Replace(&extend_lifetime)
}
None => Cursor::Before(first_new_arm.syntax()),
};
let snippet = render_snippet(cap, new_arm_list.syntax(), cursor);
builder.replace_snippet(cap, old_range, snippet);
}
@ -152,7 +150,7 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
let pat_head = match pat {
Pat::BindPat(bind_pat) => {
Pat::IdentPat(bind_pat) => {
if let Some(p) = bind_pat.pat() {
first_node_text(&p)
} else {
@ -199,12 +197,11 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
// FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
let pat: ast::Pat = match var.source(db).value.kind() {
ast::StructKind::Tuple(field_list) => {
let pats =
iter::repeat(make::placeholder_pat().into()).take(field_list.fields().count());
let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
make::tuple_struct_pat(path, pats).into()
}
ast::StructKind::Record(field_list) => {
let pats = field_list.fields().map(|f| make::bind_pat(f.name().unwrap()).into());
let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
make::record_pat(path, pats).into()
}
ast::StructKind::Unit => make::path_pat(path),

View file

@ -1,6 +1,6 @@
use base_db::FileId;
use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
use ra_db::FileId;
use ra_syntax::{ast, AstNode, TextRange, TextSize};
use syntax::{ast, AstNode, TextRange, TextSize};
use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
use ast::VisibilityOwner;
@ -121,7 +121,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
Some(cap) => match current_visibility {
Some(current_visibility) => builder.replace_snippet(
cap,
dbg!(current_visibility.syntax()).text_range(),
current_visibility.syntax().text_range(),
format!("$0{}", missing_visibility),
),
None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),

View file

@ -1,4 +1,4 @@
use ra_syntax::ast::{AstNode, BinExpr, BinOp};
use syntax::ast::{AstNode, BinExpr, BinOp};
use crate::{AssistContext, AssistId, AssistKind, Assists};

View file

@ -1,4 +1,4 @@
use ra_syntax::{algo::non_trivia_sibling, Direction, T};
use syntax::{algo::non_trivia_sibling, Direction, T};
use crate::{AssistContext, AssistId, AssistKind, Assists};

View file

@ -1,4 +1,4 @@
use ra_syntax::{
use syntax::{
algo::non_trivia_sibling,
ast::{self, AstNode},
Direction, T,

View file

@ -1,4 +1,4 @@
use ra_syntax::{
use syntax::{
ast::{self, AstNode, AttrsOwner},
SyntaxKind::{COMMENT, WHITESPACE},
TextSize,

View file

@ -1,5 +1,5 @@
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode, NameOwner};
use ide_db::RootDatabase;
use syntax::ast::{self, AstNode, NameOwner};
use test_utils::mark;
use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists};
@ -34,7 +34,7 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
}
let field_type = field_list.fields().next()?.ty()?;
let path = match field_type {
ast::TypeRef::PathType(it) => it,
ast::Type::PathType(it) => it,
_ => return None,
};

View file

@ -1,6 +1,7 @@
use base_db::FileId;
use hir::HirDisplay;
use ra_db::FileId;
use ra_syntax::{
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
@ -8,7 +9,6 @@ use ra_syntax::{
},
SyntaxKind, SyntaxNode, TextSize,
};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
assist_config::SnippetCap,
@ -142,7 +142,7 @@ impl FunctionBuilder {
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);
make::fn_(visibility, self.fn_name, self.type_params, self.params, fn_body);
let leading_ws;
let trailing_ws;

View file

@ -1,5 +1,6 @@
use ra_syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner};
use stdx::{format_to, SepBy};
use itertools::Itertools;
use stdx::format_to;
use syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@ -50,7 +51,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
.filter_map(|it| it.name())
.map(|it| it.text().clone());
let generic_params = lifetime_params.chain(type_params).sep_by(", ");
let generic_params = lifetime_params.chain(type_params).format(", ");
format_to!(buf, "<{}>", generic_params)
}
match ctx.config.snippet_cap {

View file

@ -1,9 +1,10 @@
use hir::Adt;
use ra_syntax::{
use itertools::Itertools;
use stdx::format_to;
use syntax::{
ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
T,
};
use stdx::{format_to, SepBy};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@ -52,8 +53,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
let params = field_list
.fields()
.filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax())))
.sep_by(", ");
let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
.format(", ");
let fields = field_list.fields().filter_map(|f| f.name()).format(", ");
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
@ -102,7 +103,7 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
.map(|it| it.text().clone());
let type_params =
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
format_to!(buf, "<{}>", lifetime_params.chain(type_params).sep_by(", "))
format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
}
format_to!(buf, " {{\n{}\n}}\n", code);

View file

@ -1,5 +1,5 @@
use ra_ide_db::defs::Definition;
use ra_syntax::{
use ide_db::defs::Definition;
use syntax::{
ast::{self, AstNode, AstToken},
TextRange,
};
@ -29,7 +29,7 @@ use crate::{
pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
let bind_pat = match let_stmt.pat()? {
ast::Pat::BindPat(pat) => pat,
ast::Pat::IdentPat(pat) => pat,
_ => return None,
};
if bind_pat.mut_token().is_some() {
@ -44,7 +44,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
let def = ctx.sema.to_def(&bind_pat)?;
let def = Definition::Local(def);
let refs = def.find_usages(&ctx.sema, None);
let refs = def.usages(&ctx.sema).all();
if refs.is_empty() {
mark::hit!(test_not_applicable_if_variable_unused);
return None;

View file

@ -1,8 +1,8 @@
use ra_syntax::{
use rustc_hash::FxHashSet;
use syntax::{
ast::{self, GenericParamsOwner, NameOwner},
AstNode, SyntaxKind, TextRange, TextSize,
};
use rustc_hash::FxHashSet;
use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
@ -68,7 +68,7 @@ fn generate_fn_def_assist(
let fn_params_without_lifetime: Vec<_> = param_list
.params()
.filter_map(|param| match param.ty() {
Some(ast::TypeRef::ReferenceType(ascribed_type))
Some(ast::Type::RefType(ascribed_type))
if ascribed_type.lifetime_token() == None =>
{
Some(ascribed_type.amp_token()?.text_range().end())

View file

@ -1,4 +1,4 @@
use ra_syntax::{
use syntax::{
ast::{self, AstNode},
T,
};

View file

@ -1,6 +1,6 @@
use std::iter::successors;
use ra_syntax::{
use syntax::{
algo::{neighbor, skip_trivia_token, SyntaxRewriter},
ast::{self, edit::AstNodeEdit, make},
AstNode, Direction, InsertPosition, SyntaxElement, T,
@ -8,6 +8,7 @@ use ra_syntax::{
use crate::{
assist_context::{AssistContext, Assists},
utils::next_prev,
AssistId, AssistKind,
};
@ -66,10 +67,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
)
}
fn next_prev() -> impl Iterator<Item = Direction> {
[Direction::Next, Direction::Prev].iter().copied()
}
fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTree> {
let lhs_path = old.path()?;
let rhs_path = new.path()?;
@ -164,6 +161,33 @@ use std::fmt::{Display, Debug};
);
}
#[test]
fn merge_self1() {
check_assist(
merge_imports,
r"
use std::fmt<|>;
use std::fmt::Display;
",
r"
use std::fmt::{self, Display};
",
);
}
#[test]
fn merge_self2() {
check_assist(
merge_imports,
r"
use std::{fmt, <|>fmt::Display};
",
r"
use std::{fmt::{Display, self}};
",
);
}
#[test]
fn test_merge_nested() {
check_assist(

View file

@ -1,6 +1,6 @@
use std::iter::successors;
use ra_syntax::{
use syntax::{
algo::neighbor,
ast::{self, AstNode},
Direction,
@ -86,7 +86,7 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
}
fn contains_placeholder(a: &ast::MatchArm) -> bool {
matches!(a.pat(), Some(ast::Pat::PlaceholderPat(..)))
matches!(a.pat(), Some(ast::Pat::WildcardPat(..)))
}
#[cfg(test)]

View file

@ -1,4 +1,4 @@
use ra_syntax::{
use syntax::{
ast::{self, edit::AstNodeEdit, make, AstNode, NameOwner, TypeBoundsOwner},
match_ast,
SyntaxKind::*,

View file

@ -1,5 +1,5 @@
use ra_syntax::{
ast::{AstNode, IfExpr, MatchArm},
use syntax::{
ast::{edit::AstNodeEdit, make, AstNode, IfExpr, MatchArm},
SyntaxKind::WHITESPACE,
};
@ -25,7 +25,9 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
//
// fn handle(action: Action) {
// match action {
// Action::Move { distance } => if distance > 10 { foo() },
// Action::Move { distance } => if distance > 10 {
// foo()
// },
// _ => (),
// }
// }
@ -35,9 +37,13 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
let guard = match_arm.guard()?;
let space_before_guard = guard.syntax().prev_sibling_or_token();
let guard_conditions = guard.expr()?;
let guard_condition = guard.expr()?;
let arm_expr = match_arm.expr()?;
let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
let if_expr = make::expr_if(
make::condition(guard_condition, None),
make::block_expr(None, Some(arm_expr.clone())),
)
.indent(arm_expr.indent_level());
let target = guard.syntax().text_range();
acc.add(
@ -53,7 +59,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
};
edit.delete(guard.syntax().text_range());
edit.replace_node_and_indent(arm_expr.syntax(), buf);
edit.replace_ast(arm_expr, if_expr);
},
)
}
@ -134,16 +140,14 @@ mod tests {
check_assist_target(
move_guard_to_arm_body,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' <|>if chars.clone().next() == Some('\n') => false,
_ => true
}
}
"#,
r#"if chars.clone().next() == Some('\n')"#,
fn main() {
match 92 {
x <|>if x > 10 => false,
_ => true
}
}
"#,
r#"if x > 10"#,
);
}
@ -152,25 +156,23 @@ mod tests {
check_assist(
move_guard_to_arm_body,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' <|>if chars.clone().next() == Some('\n') => false,
_ => true
}
}
"#,
fn main() {
match 92 {
x <|>if x > 10 => false,
_ => true
}
}
"#,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' => if chars.clone().next() == Some('\n') { false },
_ => true
}
}
"#,
fn main() {
match 92 {
x => if x > 10 {
false
},
_ => true
}
}
"#,
);
}
@ -179,21 +181,23 @@ mod tests {
check_assist(
move_guard_to_arm_body,
r#"
fn f() {
match x {
<|>y @ 4 | y @ 5 if y > 5 => true,
_ => false
}
}
"#,
fn main() {
match 92 {
<|>x @ 4 | x @ 5 if x > 5 => true,
_ => false
}
}
"#,
r#"
fn f() {
match x {
y @ 4 | y @ 5 => if y > 5 { true },
_ => false
}
}
"#,
fn main() {
match 92 {
x @ 4 | x @ 5 => if x > 5 {
true
},
_ => false
}
}
"#,
);
}
@ -202,25 +206,21 @@ mod tests {
check_assist(
move_arm_cond_to_match_guard,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' => if chars.clone().next() == Some('\n') { <|>false },
_ => true
}
}
"#,
fn main() {
match 92 {
x => if x > 10 { <|>false },
_ => true
}
}
"#,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' if chars.clone().next() == Some('\n') => false,
_ => true
}
}
"#,
fn main() {
match 92 {
x if x > 10 => false,
_ => true
}
}
"#,
);
}
@ -229,15 +229,13 @@ mod tests {
check_assist_not_applicable(
move_arm_cond_to_match_guard,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' => if let Some(_) = chars.clone().next() { <|>false },
_ => true
}
}
"#,
fn main() {
match 92 {
x => if let 62 = x { <|>false },
_ => true
}
}
"#,
);
}
@ -246,25 +244,21 @@ mod tests {
check_assist(
move_arm_cond_to_match_guard,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' => if chars.clone().next().is_some() { <|> },
_ => true
}
}
"#,
fn main() {
match 92 {
x => if x > 10 { <|> },
_ => true
}
}
"#,
r#"
fn f() {
let t = 'a';
let chars = "abcd";
match t {
'\r' if chars.clone().next().is_some() => { },
_ => true
}
}
"#,
fn main() {
match 92 {
x if x > 10 => { },
_ => true
}
}
"#,
);
}
@ -273,31 +267,27 @@ mod tests {
check_assist(
move_arm_cond_to_match_guard,
r#"
fn f() {
let mut t = 'a';
let chars = "abcd";
match t {
'\r' => if chars.clone().next().is_some() {
t = 'e';<|>
false
},
_ => true
}
}
"#,
fn main() {
match 92 {
x => if x > 10 {
92;<|>
false
},
_ => true
}
}
"#,
r#"
fn f() {
let mut t = 'a';
let chars = "abcd";
match t {
'\r' if chars.clone().next().is_some() => {
t = 'e';
false
},
_ => true
}
}
"#,
fn main() {
match 92 {
x if x > 10 => {
92;
false
},
_ => true
}
}
"#,
);
}
}

View file

@ -1,6 +1,6 @@
use std::borrow::Cow;
use ra_syntax::{
use syntax::{
ast::{self, HasQuotes, HasStringValue},
AstToken,
SyntaxKind::{RAW_STRING, STRING},
@ -173,7 +173,7 @@ fn test_required_hashes() {
}
#[cfg(test)]
mod test {
mod tests {
use test_utils::mark;
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

View file

@ -1,4 +1,4 @@
use ra_syntax::{
use syntax::{
ast::{self, AstNode},
TextRange, TextSize, T,
};
@ -82,9 +82,10 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
use super::*;
#[test]
fn test_remove_dbg() {
check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

View file

@ -1,4 +1,4 @@
use ra_syntax::{SyntaxKind, TextRange, T};
use syntax::{SyntaxKind, TextRange, T};
use crate::{AssistContext, AssistId, AssistKind, Assists};

View file

@ -0,0 +1,131 @@
use ide_db::{defs::Definition, search::Reference};
use syntax::{
algo::find_node_at_range,
ast::{self, ArgListOwner},
AstNode, SyntaxNode, TextRange, T,
};
use test_utils::mark;
use crate::{
assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
};
// Assist: remove_unused_param
//
// Removes unused function parameter.
//
// ```
// fn frobnicate(x: i32<|>) {}
//
// fn main() {
// frobnicate(92);
// }
// ```
// ->
// ```
// fn frobnicate() {}
//
// fn main() {
// frobnicate();
// }
// ```
pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let param: ast::Param = ctx.find_node_at_offset()?;
let ident_pat = match param.pat()? {
ast::Pat::IdentPat(it) => it,
_ => return None,
};
let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
let param_position = func.param_list()?.params().position(|it| it == param)?;
let fn_def = {
let func = ctx.sema.to_def(&func)?;
Definition::ModuleDef(func.into())
};
let param_def = {
let local = ctx.sema.to_def(&ident_pat)?;
Definition::Local(local)
};
if param_def.usages(&ctx.sema).at_least_one() {
mark::hit!(keep_used);
return None;
}
acc.add(
AssistId("remove_unused_param", AssistKind::Refactor),
"Remove unused parameter",
param.syntax().text_range(),
|builder| {
builder.delete(range_with_coma(param.syntax()));
for usage in fn_def.usages(&ctx.sema).all() {
process_usage(ctx, builder, usage, param_position);
}
},
)
}
fn process_usage(
ctx: &AssistContext,
builder: &mut AssistBuilder,
usage: Reference,
arg_to_remove: usize,
) -> Option<()> {
let source_file = ctx.sema.parse(usage.file_range.file_id);
let call_expr: ast::CallExpr =
find_node_at_range(source_file.syntax(), usage.file_range.range)?;
if call_expr.expr()?.syntax().text_range() != usage.file_range.range {
return None;
}
let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
builder.edit_file(usage.file_range.file_id);
builder.delete(range_with_coma(arg.syntax()));
Some(())
}
fn range_with_coma(node: &SyntaxNode) -> TextRange {
let up_to = next_prev().find_map(|dir| {
node.siblings_with_tokens(dir)
.filter_map(|it| it.into_token())
.find(|it| it.kind() == T![,])
});
let up_to = up_to.map_or(node.text_range(), |it| it.text_range());
node.text_range().cover(up_to)
}
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
use super::*;
#[test]
fn remove_unused() {
check_assist(
remove_unused_param,
r#"
fn a() { foo(9, 2) }
fn foo(x: i32, <|>y: i32) { x; }
fn b() { foo(9, 2,) }
"#,
r#"
fn a() { foo(9) }
fn foo(x: i32) { x; }
fn b() { foo(9, ) }
"#,
);
}
#[test]
fn keep_used() {
mark::check!(keep_used);
check_assist_not_applicable(
remove_unused_param,
r#"
fn foo(x: i32, <|>y: i32) { y; }
fn main() { foo(9, 2) }
"#,
);
}
}

View file

@ -2,8 +2,8 @@ use itertools::Itertools;
use rustc_hash::FxHashMap;
use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
use ra_ide_db::RootDatabase;
use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
use ide_db::RootDatabase;
use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@ -57,7 +57,7 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
match node.kind() {
RECORD_EXPR => vec![RECORD_EXPR_FIELD],
RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT],
RECORD_PAT => vec![RECORD_PAT_FIELD, IDENT_PAT],
_ => vec![],
}
}
@ -66,7 +66,7 @@ fn get_field_name(node: &SyntaxNode) -> String {
let res = match_ast! {
match node {
ast::RecordExprField(field) => field.field_name().map(|it| it.to_string()),
ast::RecordFieldPat(field) => field.field_name().map(|it| it.to_string()),
ast::RecordPatField(field) => field.field_name().map(|it| it.to_string()),
_ => None,
}
};

View file

@ -1,5 +1,4 @@
use ra_fmt::unwrap_trivial_block;
use ra_syntax::{
use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
@ -8,7 +7,10 @@ use ra_syntax::{
AstNode,
};
use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists};
use crate::{
utils::{unwrap_trivial_block, TryEnum},
AssistContext, AssistId, AssistKind, Assists,
};
// Assist: replace_if_let_with_match
//
@ -65,7 +67,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
.type_of_pat(&pat)
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
.map(|it| it.sad_pattern())
.unwrap_or_else(|| make::placeholder_pat().into());
.unwrap_or_else(|| make::wildcard_pat().into());
let else_expr = unwrap_trivial_block(else_block);
make::match_arm(vec![pattern], else_expr)
};

View file

@ -1,6 +1,6 @@
use std::iter::once;
use ra_syntax::{
use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
@ -50,10 +50,10 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
target,
|edit| {
let with_placeholder: ast::Pat = match happy_variant {
None => make::placeholder_pat().into(),
None => make::wildcard_pat().into(),
Some(var_name) => make::tuple_struct_pat(
make::path_unqualified(make::path_segment(make::name_ref(var_name))),
once(make::placeholder_pat().into()),
once(make::wildcard_pat().into()),
)
.into(),
};
@ -62,8 +62,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
let stmt = make::expr_stmt(if_);
let placeholder =
stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
let placeholder = stmt.syntax().descendants().find_map(ast::WildcardPat::cast).unwrap();
let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));

View file

@ -1,5 +1,5 @@
use hir;
use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode, TextRange};
use test_utils::mark;
use crate::{
utils::{find_insert_use_container, insert_use_statement},
@ -28,12 +28,19 @@ pub(crate) fn replace_qualified_name_with_use(
if path.syntax().ancestors().find_map(ast::Use::cast).is_some() {
return None;
}
let hir_path = ctx.sema.lower_path(&path)?;
let segments = collect_hir_path_segments(&hir_path)?;
if segments.len() < 2 {
if path.qualifier().is_none() {
mark::hit!(dont_import_trivial_paths);
return None;
}
let path_to_import = path.to_string().clone();
let path_to_import = match path.segment()?.generic_arg_list() {
Some(generic_args) => {
let generic_args_start =
generic_args.syntax().text_range().start() - path.syntax().text_range().start();
&path_to_import[TextRange::up_to(generic_args_start)]
}
None => path_to_import.as_str(),
};
let target = path.syntax().text_range();
acc.add(
@ -41,12 +48,16 @@ pub(crate) fn replace_qualified_name_with_use(
"Replace qualified path with use",
target,
|builder| {
let path_to_import = hir_path.mod_path().clone();
let container = match find_insert_use_container(path.syntax(), ctx) {
Some(c) => c,
None => return,
};
insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
insert_use_statement(
path.syntax(),
&path_to_import.to_string(),
ctx,
builder.text_edit_builder(),
);
// Now that we've brought the name into scope, re-qualify all paths that could be
// affected (that is, all paths inside the node we added the `use` to).
@ -58,26 +69,6 @@ pub(crate) fn replace_qualified_name_with_use(
)
}
fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
let mut ps = Vec::<SmolStr>::with_capacity(10);
match path.kind() {
hir::PathKind::Abs => ps.push("".into()),
hir::PathKind::Crate => ps.push("crate".into()),
hir::PathKind::Plain => {}
hir::PathKind::Super(0) => ps.push("self".into()),
hir::PathKind::Super(lvl) => {
let mut chain = "super".to_string();
for _ in 0..*lvl {
chain += "::super";
}
ps.push(chain.into());
}
hir::PathKind::DollarCrate(_) => return None,
}
ps.extend(path.segments().iter().map(|it| it.name.to_string().into()));
Some(ps)
}
/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
for child in node.children() {
@ -467,7 +458,8 @@ impl Debug for Foo {
}
#[test]
fn test_replace_not_applicable_one_segment() {
fn dont_import_trivial_paths() {
mark::check!(dont_import_trivial_paths);
check_assist_not_applicable(
replace_qualified_name_with_use,
r"
@ -639,6 +631,48 @@ use std::fmt::{self, Display};
fn main() {
fmt;
}
",
);
}
#[test]
fn does_not_replace_pub_use() {
check_assist(
replace_qualified_name_with_use,
r"
pub use std::fmt;
impl std::io<|> for Foo {
}
",
r"
use std::io;
pub use std::fmt;
impl io for Foo {
}
",
);
}
#[test]
fn does_not_replace_pub_crate_use() {
check_assist(
replace_qualified_name_with_use,
r"
pub(crate) use std::fmt;
impl std::io<|> for Foo {
}
",
r"
use std::io;
pub(crate) use std::fmt;
impl io for Foo {
}
",
);

View file

@ -1,6 +1,6 @@
use std::iter;
use ra_syntax::{
use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
@ -42,7 +42,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
if name.text() != "unwrap" {
return None;
}
let caller = method_call.expr()?;
let caller = method_call.receiver()?;
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();
@ -52,7 +52,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
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 it = make::ident_pat(make::name("a")).into();
let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
@ -60,7 +60,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
let unreachable_call = make::expr_unreachable();
let err_arm =
make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
make::match_arm(iter::once(make::wildcard_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)

View file

@ -1,6 +1,6 @@
use std::iter::successors;
use ra_syntax::{ast, AstNode, T};
use syntax::{ast, AstNode, T};
use crate::{AssistContext, AssistId, AssistKind, Assists};

View file

@ -1,5 +1,4 @@
use ra_fmt::unwrap_trivial_block;
use ra_syntax::{
use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
@ -7,7 +6,7 @@ use ra_syntax::{
AstNode, TextRange, T,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
// Assist: unwrap_block
//

View file

@ -1,4 +1,4 @@
//! `ra_assists` crate provides a bunch of code assists, also known as code
//! `assists` crate provides a bunch of code assists, also known as code
//! actions (in LSP) or intentions (in IntelliJ).
//!
//! An assist is a micro-refactoring, which is automatically activated in
@ -17,10 +17,10 @@ mod tests;
pub mod utils;
pub mod ast_transform;
use base_db::FileRange;
use hir::Semantics;
use ra_db::FileRange;
use ra_ide_db::{source_change::SourceChange, RootDatabase};
use ra_syntax::TextRange;
use ide_db::{label::Label, source_change::SourceChange, RootDatabase};
use syntax::TextRange;
pub(crate) use crate::assist_context::{AssistContext, Assists};
@ -68,7 +68,7 @@ pub struct GroupLabel(pub String);
pub struct Assist {
pub id: AssistId,
/// Short description of the assist, as shown in the UI.
pub label: String,
pub label: Label,
pub group: Option<GroupLabel>,
/// Target ranges are used to sort assists: the smaller the target range,
/// the more specific assist is, and so it should be sorted first.
@ -113,17 +113,6 @@ impl Assist {
});
acc.finish_resolved()
}
pub(crate) fn new(
id: AssistId,
label: String,
group: Option<GroupLabel>,
target: TextRange,
) -> Assist {
// FIXME: make fields private, so that this invariant can't be broken
assert!(label.starts_with(|c: char| c.is_uppercase()));
Assist { id, label, group, target }
}
}
mod handlers {
@ -140,6 +129,7 @@ mod handlers {
mod change_return_type_to_result;
mod change_visibility;
mod early_return;
mod expand_glob_import;
mod extract_struct_from_enum_variant;
mod extract_variable;
mod fill_match_arms;
@ -162,6 +152,7 @@ mod handlers {
mod raw_string;
mod remove_dbg;
mod remove_mut;
mod remove_unused_param;
mod reorder_fields;
mod replace_if_let_with_match;
mod replace_let_with_if_let;
@ -181,6 +172,7 @@ mod handlers {
change_return_type_to_result::change_return_type_to_result,
change_visibility::change_visibility,
early_return::convert_to_guarded_return,
expand_glob_import::expand_glob_import,
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
extract_variable::extract_variable,
fill_match_arms::fill_match_arms,
@ -207,6 +199,7 @@ mod handlers {
raw_string::remove_hash,
remove_dbg::remove_dbg,
remove_mut::remove_mut,
remove_unused_param::remove_unused_param,
reorder_fields::reorder_fields,
replace_if_let_with_match::replace_if_let_with_match,
replace_let_with_if_let::replace_let_with_if_let,

View file

@ -1,9 +1,9 @@
mod generated;
use base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
use hir::Semantics;
use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
use ra_ide_db::RootDatabase;
use ra_syntax::TextRange;
use ide_db::RootDatabase;
use syntax::TextRange;
use test_utils::{assert_eq_text, extract_offset, extract_range};
use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
@ -20,7 +20,7 @@ pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_
// FIXME: instead of having a separate function here, maybe use
// `extract_ranges` and mark the target as `<target> </target>` in the
// fixuture?
// fixture?
pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
check(assist, ra_fixture, ExpectedResult::Target(target));
}

View file

@ -82,8 +82,8 @@ trait Trait {
impl Trait for () {
Type X = ();
fn foo(&self) {}
$0fn bar(&self) {}
$0fn bar(&self) {}
}
"#####,
)
@ -115,7 +115,6 @@ impl Trait<u32> for () {
fn foo(&self) -> u32 {
${0:todo!()}
}
}
"#####,
)
@ -228,6 +227,33 @@ fn main() {
)
}
#[test]
fn doctest_expand_glob_import() {
check_doc_test(
"expand_glob_import",
r#####"
mod foo {
pub struct Bar;
pub struct Baz;
}
use foo::*<|>;
fn qux(bar: Bar, baz: Baz) {}
"#####,
r#####"
mod foo {
pub struct Bar;
pub struct Baz;
}
use foo::{Baz, Bar};
fn qux(bar: Bar, baz: Baz) {}
"#####,
)
}
#[test]
fn doctest_extract_struct_from_enum_variant() {
check_doc_test(
@ -663,7 +689,9 @@ enum Action { Move { distance: u32 }, Stop }
fn handle(action: Action) {
match action {
Action::Move { distance } => if distance > 10 { foo() },
Action::Move { distance } => if distance > 10 {
foo()
},
_ => (),
}
}
@ -722,6 +750,27 @@ impl Walrus {
)
}
#[test]
fn doctest_remove_unused_param() {
check_doc_test(
"remove_unused_param",
r#####"
fn frobnicate(x: i32<|>) {}
fn main() {
frobnicate(92);
}
"#####,
r#####"
fn frobnicate() {}
fn main() {
frobnicate();
}
"#####,
)
}
#[test]
fn doctest_reorder_fields() {
check_doc_test(

View file

@ -4,19 +4,57 @@ pub(crate) mod insert_use;
use std::{iter, ops};
use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type};
use ra_ide_db::RootDatabase;
use ra_syntax::{
use ide_db::RootDatabase;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use syntax::{
ast::{self, make, NameOwner},
AstNode,
AstNode, Direction,
SyntaxKind::*,
SyntaxNode, TextSize, T,
};
use rustc_hash::FxHashSet;
use crate::assist_config::SnippetCap;
pub(crate) use insert_use::{find_insert_use_container, insert_use_statement};
pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
extract_trivial_expression(&block)
.filter(|expr| !expr.syntax().text().contains_char('\n'))
.unwrap_or_else(|| block.into())
}
pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
let has_anything_else = |thing: &SyntaxNode| -> bool {
let mut non_trivial_children =
block.syntax().children_with_tokens().filter(|it| match it.kind() {
WHITESPACE | T!['{'] | T!['}'] => false,
_ => it.as_node() != Some(thing),
});
non_trivial_children.next().is_some()
};
if let Some(expr) = block.expr() {
if has_anything_else(expr.syntax()) {
return None;
}
return Some(expr);
}
// Unwrap `{ continue; }`
let (stmt,) = block.statements().next_tuple()?;
if let ast::Stmt::ExprStmt(expr_stmt) = stmt {
if has_anything_else(expr_stmt.syntax()) {
return None;
}
let expr = expr_stmt.expr()?;
match expr.syntax().kind() {
CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr),
_ => (),
}
}
None
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum Cursor<'a> {
Replace(&'a SyntaxNode),
@ -111,11 +149,8 @@ pub(crate) fn resolve_target_trait(
sema: &Semantics<RootDatabase>,
impl_def: &ast::Impl,
) -> Option<hir::Trait> {
let ast_path = impl_def
.target_trait()
.map(|it| it.syntax().clone())
.and_then(ast::PathType::cast)?
.path()?;
let ast_path =
impl_def.trait_().map(|it| it.syntax().clone()).and_then(ast::PathType::cast)?.path()?;
match sema.resolve_path(&ast_path) {
Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
@ -184,10 +219,10 @@ impl TryEnum {
match self {
TryEnum::Result => make::tuple_struct_pat(
make::path_unqualified(make::path_segment(make::name_ref("Err"))),
iter::once(make::placeholder_pat().into()),
iter::once(make::wildcard_pat().into()),
)
.into(),
TryEnum::Option => make::bind_pat(make::name("None")).into(),
TryEnum::Option => make::ident_pat(make::name("None")).into(),
}
}
@ -260,7 +295,7 @@ pub use prelude::*;
.find(|dep| &dep.name.to_string() == std_crate)?
.krate;
let mut module = std_crate.root_module(db)?;
let mut module = std_crate.root_module(db);
for segment in path {
module = module.children(db).find_map(|child| {
let name = child.name(db)?;
@ -276,3 +311,7 @@ pub use prelude::*;
Some(def)
}
}
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
[Direction::Next, Direction::Prev].iter().copied()
}

View file

@ -2,17 +2,18 @@
// FIXME: rewrite according to the plan, outlined in
// https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553
use hir::{self, ModPath};
use ra_syntax::{
ast::{self, NameOwner},
AstNode, Direction, SmolStr,
use std::iter::successors;
use either::Either;
use syntax::{
ast::{self, NameOwner, VisibilityOwner},
AstNode, AstToken, Direction, SmolStr,
SyntaxKind::{PATH, PATH_SEGMENT},
SyntaxNode, T,
SyntaxNode, SyntaxToken, T,
};
use ra_text_edit::TextEditBuilder;
use text_edit::TextEditBuilder;
use crate::assist_context::AssistContext;
use either::Either;
/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
pub(crate) fn find_insert_use_container(
@ -33,11 +34,11 @@ pub(crate) fn find_insert_use_container(
pub(crate) fn insert_use_statement(
// Ideally the position of the cursor, used to
position: &SyntaxNode,
path_to_import: &ModPath,
path_to_import: &str,
ctx: &AssistContext,
builder: &mut TextEditBuilder,
) {
let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
let target = path_to_import.split("::").map(SmolStr::new).collect::<Vec<_>>();
let container = find_insert_use_container(position, ctx);
if let Some(container) = container {
@ -378,6 +379,7 @@ fn best_action_for_target(
let best_action = container
.children()
.filter_map(ast::Use::cast)
.filter(|u| u.visibility().is_none())
.filter_map(|it| it.use_tree())
.map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
.fold(None, |best, a| match best {
@ -441,7 +443,7 @@ fn make_assist_add_new_use(
edit: &mut TextEditBuilder,
) {
if let Some(anchor) = anchor {
let indent = ra_fmt::leading_indent(anchor);
let indent = leading_indent(anchor);
let mut buf = String::new();
if after {
buf.push_str("\n");
@ -523,3 +525,22 @@ fn make_assist_add_nested_import(
edit.insert(end, "}".to_string());
}
}
/// If the node is on the beginning of the line, calculate indent.
fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
for token in prev_tokens(node.first_token()?) {
if let Some(ws) = ast::Whitespace::cast(token.clone()) {
let ws_text = ws.text();
if let Some(pos) = ws_text.rfind('\n') {
return Some(ws_text[pos + 1..].into());
}
}
if token.text().contains('\n') {
break;
}
}
return None;
fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
successors(token.prev_token(), |token| token.prev_token())
}
}

View file

@ -1,21 +1,21 @@
[package]
edition = "2018"
name = "ra_db"
version = "0.1.0"
authors = ["rust-analyzer developers"]
name = "base_db"
version = "0.0.0"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false
[dependencies]
salsa = "0.15.0"
salsa = "0.15.2"
rustc-hash = "1.1.0"
ra_syntax = { path = "../ra_syntax" }
ra_cfg = { path = "../ra_cfg" }
ra_prof = { path = "../ra_prof" }
ra_tt = { path = "../ra_tt" }
syntax = { path = "../syntax" }
cfg = { path = "../cfg" }
profile = { path = "../profile" }
tt = { path = "../tt" }
test_utils = { path = "../test_utils" }
vfs = { path = "../vfs" }
stdx = { path = "../stdx" }

View file

@ -59,7 +59,7 @@
//! ```
use std::{str::FromStr, sync::Arc};
use ra_cfg::CfgOptions;
use cfg::CfgOptions;
use rustc_hash::FxHashMap;
use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER};
use vfs::{file_set::FileSet, VfsPath};

View file

@ -8,10 +8,10 @@
use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc};
use ra_cfg::CfgOptions;
use ra_syntax::SmolStr;
use ra_tt::TokenExpander;
use cfg::CfgOptions;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::SmolStr;
use tt::TokenExpander;
use vfs::file_set::FileSet;
pub use vfs::FileId;
@ -156,7 +156,7 @@ impl CrateGraph {
display_name: Option<String>,
cfg_options: CfgOptions,
env: Env,
proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>,
proc_macro: Vec<(SmolStr, Arc<dyn tt::TokenExpander>)>,
) -> CrateId {
let proc_macro =
proc_macro.into_iter().map(|(name, it)| ProcMacro { name, expander: it }).collect();

View file

@ -1,13 +1,12 @@
//! ra_db defines basic database traits. The concrete DB is defined by ra_ide.
//! base_db defines basic database traits. The concrete DB is defined by ide.
mod cancellation;
mod input;
pub mod fixture;
use std::{panic, sync::Arc};
use ra_prof::profile;
use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
use rustc_hash::FxHashSet;
use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
pub use crate::{
cancellation::Canceled,
@ -113,7 +112,7 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug {
}
fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
let _p = profile("parse_query").detail(|| format!("{:?}", file_id));
let _p = profile::span("parse_query").detail(|| format!("{:?}", file_id));
let text = db.file_text(file_id);
SourceFile::parse(&*text)
}

View file

@ -1,9 +1,9 @@
[package]
edition = "2018"
name = "ra_cfg"
version = "0.1.0"
authors = ["rust-analyzer developers"]
name = "cfg"
version = "0.0.0"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false
@ -11,8 +11,8 @@ doctest = false
[dependencies]
rustc-hash = "1.1.0"
ra_syntax = { path = "../ra_syntax" }
tt = { path = "../ra_tt", package = "ra_tt" }
tt = { path = "../tt" }
[dev-dependencies]
mbe = { path = "../ra_mbe", package = "ra_mbe" }
mbe = { path = "../mbe" }
syntax = { path = "../syntax" }

View file

@ -4,7 +4,7 @@
use std::slice::Iter as SliceIter;
use ra_syntax::SmolStr;
use tt::SmolStr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CfgExpr {
@ -86,17 +86,15 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
mod tests {
use super::*;
use mbe::{ast_to_token_tree, TokenMap};
use ra_syntax::ast::{self, AstNode};
fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
let source_file = ast::SourceFile::parse(input).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
ast_to_token_tree(&tt).unwrap()
}
use mbe::ast_to_token_tree;
use syntax::ast::{self, AstNode};
fn assert_parse_result(input: &str, expected: CfgExpr) {
let (tt, _) = get_token_tree_generated(input);
let (tt, _) = {
let source_file = ast::SourceFile::parse(input).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
ast_to_token_tree(&tt).unwrap()
};
let cfg = CfgExpr::parse(&tt);
assert_eq!(cfg, expected);
}

View file

@ -1,9 +1,9 @@
//! ra_cfg defines conditional compiling options, `cfg` attibute parser and evaluator
//! cfg defines conditional compiling options, `cfg` attibute parser and evaluator
mod cfg_expr;
use ra_syntax::SmolStr;
use rustc_hash::FxHashSet;
use tt::SmolStr;
pub use cfg_expr::CfgExpr;

View file

@ -1,14 +0,0 @@
[package]
name = "expect"
version = "0.1.0"
authors = ["rust-analyzer developers"]
edition = "2018"
license = "MIT OR Apache-2.0"
[lib]
doctest = false
[dependencies]
once_cell = "1"
difference = "2"
stdx = { path = "../stdx" }

View file

@ -1,356 +0,0 @@
//! Snapshot testing library, see
//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
use std::{
collections::HashMap,
env, fmt, fs, mem,
ops::Range,
panic,
path::{Path, PathBuf},
sync::Mutex,
};
use difference::Changeset;
use once_cell::sync::Lazy;
use stdx::{lines_with_ends, trim_indent};
const HELP: &str = "
You can update all `expect![[]]` tests by running:
env UPDATE_EXPECT=1 cargo test
To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
";
fn update_expect() -> bool {
env::var("UPDATE_EXPECT").is_ok()
}
/// expect![[r#"inline snapshot"#]]
#[macro_export]
macro_rules! expect {
[[$data:literal]] => {$crate::Expect {
position: $crate::Position {
file: file!(),
line: line!(),
column: column!(),
},
data: $data,
}};
[[]] => { $crate::expect![[""]] };
}
/// expect_file!["/crates/foo/test_data/bar.html"]
#[macro_export]
macro_rules! expect_file {
[$path:expr] => {$crate::ExpectFile {
path: std::path::PathBuf::from($path)
}};
}
#[derive(Debug)]
pub struct Expect {
pub position: Position,
pub data: &'static str,
}
#[derive(Debug)]
pub struct ExpectFile {
pub path: PathBuf,
}
#[derive(Debug)]
pub struct Position {
pub file: &'static str,
pub line: u32,
pub column: u32,
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}:{}", self.file, self.line, self.column)
}
}
impl Expect {
pub fn assert_eq(&self, actual: &str) {
let trimmed = self.trimmed();
if &trimmed == actual {
return;
}
Runtime::fail_expect(self, &trimmed, actual);
}
pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
let actual = format!("{:#?}\n", actual);
self.assert_eq(&actual)
}
fn trimmed(&self) -> String {
if !self.data.contains('\n') {
return self.data.to_string();
}
trim_indent(self.data)
}
fn locate(&self, file: &str) -> Location {
let mut target_line = None;
let mut line_start = 0;
for (i, line) in lines_with_ends(file).enumerate() {
if i == self.position.line as usize - 1 {
let pat = "expect![[";
let offset = line.find(pat).unwrap();
let literal_start = line_start + offset + pat.len();
let indent = line.chars().take_while(|&it| it == ' ').count();
target_line = Some((literal_start, indent));
break;
}
line_start += line.len();
}
let (literal_start, line_indent) = target_line.unwrap();
let literal_length =
file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`.");
let literal_range = literal_start..literal_start + literal_length;
Location { line_indent, literal_range }
}
}
impl ExpectFile {
pub fn assert_eq(&self, actual: &str) {
let expected = self.read();
if actual == expected {
return;
}
Runtime::fail_file(self, &expected, actual);
}
pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
let actual = format!("{:#?}\n", actual);
self.assert_eq(&actual)
}
fn read(&self) -> String {
fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n")
}
fn write(&self, contents: &str) {
fs::write(self.abs_path(), contents).unwrap()
}
fn abs_path(&self) -> PathBuf {
WORKSPACE_ROOT.join(&self.path)
}
}
#[derive(Default)]
struct Runtime {
help_printed: bool,
per_file: HashMap<&'static str, FileRuntime>,
}
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);
impl Runtime {
fn fail_expect(expect: &Expect, expected: &str, actual: &str) {
let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
if update_expect() {
println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position);
rt.per_file
.entry(expect.position.file)
.or_insert_with(|| FileRuntime::new(expect))
.update(expect, actual);
return;
}
rt.panic(expect.position.to_string(), expected, actual);
}
fn fail_file(expect: &ExpectFile, expected: &str, actual: &str) {
let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
if update_expect() {
println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.path.display());
expect.write(actual);
return;
}
rt.panic(expect.path.display().to_string(), expected, actual);
}
fn panic(&mut self, position: String, expected: &str, actual: &str) {
let print_help = !mem::replace(&mut self.help_printed, true);
let help = if print_help { HELP } else { "" };
let diff = Changeset::new(actual, expected, "\n");
println!(
"\n
\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m
\x1b[1m\x1b[34m-->\x1b[0m {}
{}
\x1b[1mExpect\x1b[0m:
----
{}
----
\x1b[1mActual\x1b[0m:
----
{}
----
\x1b[1mDiff\x1b[0m:
----
{}
----
",
position, help, expected, actual, diff
);
// Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
panic::resume_unwind(Box::new(()));
}
}
struct FileRuntime {
path: PathBuf,
original_text: String,
patchwork: Patchwork,
}
impl FileRuntime {
fn new(expect: &Expect) -> FileRuntime {
let path = WORKSPACE_ROOT.join(expect.position.file);
let original_text = fs::read_to_string(&path).unwrap();
let patchwork = Patchwork::new(original_text.clone());
FileRuntime { path, original_text, patchwork }
}
fn update(&mut self, expect: &Expect, actual: &str) {
let loc = expect.locate(&self.original_text);
let patch = format_patch(loc.line_indent.clone(), actual);
self.patchwork.patch(loc.literal_range, &patch);
fs::write(&self.path, &self.patchwork.text).unwrap()
}
}
#[derive(Debug)]
struct Location {
line_indent: usize,
literal_range: Range<usize>,
}
#[derive(Debug)]
struct Patchwork {
text: String,
indels: Vec<(Range<usize>, usize)>,
}
impl Patchwork {
fn new(text: String) -> Patchwork {
Patchwork { text, indels: Vec::new() }
}
fn patch(&mut self, mut range: Range<usize>, patch: &str) {
self.indels.push((range.clone(), patch.len()));
self.indels.sort_by_key(|(delete, _insert)| delete.start);
let (delete, insert) = self
.indels
.iter()
.take_while(|(delete, _)| delete.start < range.start)
.map(|(delete, insert)| (delete.end - delete.start, insert))
.fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));
for pos in &mut [&mut range.start, &mut range.end] {
**pos -= delete;
**pos += insert;
}
self.text.replace_range(range, &patch);
}
}
fn format_patch(line_indent: usize, patch: &str) -> String {
let mut max_hashes = 0;
let mut cur_hashes = 0;
for byte in patch.bytes() {
if byte != b'#' {
cur_hashes = 0;
continue;
}
cur_hashes += 1;
max_hashes = max_hashes.max(cur_hashes);
}
let hashes = &"#".repeat(max_hashes + 1);
let indent = &" ".repeat(line_indent);
let is_multiline = patch.contains('\n');
let mut buf = String::new();
buf.push('r');
buf.push_str(hashes);
buf.push('"');
if is_multiline {
buf.push('\n');
}
let mut final_newline = false;
for line in lines_with_ends(patch) {
if is_multiline && !line.trim().is_empty() {
buf.push_str(indent);
buf.push_str(" ");
}
buf.push_str(line);
final_newline = line.ends_with('\n');
}
if final_newline {
buf.push_str(indent);
}
buf.push('"');
buf.push_str(hashes);
buf
}
static WORKSPACE_ROOT: Lazy<PathBuf> = Lazy::new(|| {
let my_manifest =
env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned());
// Heuristic, see https://github.com/rust-lang/cargo/issues/3946
Path::new(&my_manifest)
.ancestors()
.filter(|it| it.join("Cargo.toml").exists())
.last()
.unwrap()
.to_path_buf()
});
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_patch() {
let patch = format_patch(0, "hello\nworld\n");
expect![[r##"
r#"
hello
world
"#"##]]
.assert_eq(&patch);
let patch = format_patch(4, "single line");
expect![[r##"r#"single line"#"##]].assert_eq(&patch);
}
#[test]
fn test_patchwork() {
let mut patchwork = Patchwork::new("one two three".to_string());
patchwork.patch(4..7, "zwei");
patchwork.patch(0..3, "один");
patchwork.patch(8..13, "3");
expect![[r#"
Patchwork {
text: "один zwei 3",
indels: [
(
0..3,
8,
),
(
4..7,
4,
),
(
8..13,
1,
),
],
}
"#]]
.assert_debug_eq(&patchwork);
}
}

View file

@ -1,9 +1,9 @@
[package]
edition = "2018"
name = "flycheck"
version = "0.1.0"
authors = ["rust-analyzer developers"]
version = "0.0.0"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false
@ -14,4 +14,5 @@ log = "0.4.8"
cargo_metadata = "0.11.1"
serde_json = "1.0.48"
jod-thread = "0.1.1"
ra_toolchain = { path = "../ra_toolchain" }
toolchain = { path = "../toolchain" }

View file

@ -1,4 +1,4 @@
//! cargo_check provides the functionality needed to run `cargo check` or
//! Flycheck provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
@ -147,6 +147,12 @@ impl FlycheckActor {
// avoid busy-waiting.
let cargo_handle = self.cargo_handle.take().unwrap();
let res = cargo_handle.join();
if res.is_err() {
log::error!(
"Flycheck failed to run the following command: {:?}",
self.check_command()
)
}
self.send(Message::Progress(Progress::DidFinish(res)));
}
Event::CheckEvent(Some(message)) => match message {
@ -187,7 +193,7 @@ impl FlycheckActor {
extra_args,
features,
} => {
let mut cmd = Command::new(ra_toolchain::cargo());
let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command);
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
.arg(self.workspace_root.join("Cargo.toml"));
@ -253,7 +259,7 @@ impl CargoHandle {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Cargo watcher failed,the command produced no valid metadata (exit code: {:?})",
"Cargo watcher failed, the command produced no valid metadata (exit code: {:?})",
exit_status
),
));

26
crates/hir/Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[package]
name = "hir"
version = "0.0.0"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false
[dependencies]
log = "0.4.8"
rustc-hash = "1.1.0"
either = "1.5.3"
arrayvec = "0.5.1"
itertools = "0.9.0"
url = "2.1.1"
stdx = { path = "../stdx" }
syntax = { path = "../syntax" }
base_db = { path = "../base_db" }
profile = { path = "../profile" }
hir_expand = { path = "../hir_expand" }
hir_def = { path = "../hir_def" }
hir_ty = { path = "../hir_ty" }
tt = { path = "../tt" }

View file

@ -2,14 +2,18 @@
use std::{iter, sync::Arc};
use arrayvec::ArrayVec;
use base_db::{CrateId, Edition, FileId};
use either::Either;
use hir_def::{
adt::ReprKind,
adt::StructKind,
adt::VariantData,
builtin_type::BuiltinType,
docs::Documentation,
expr::{BindingAnnotation, Pat, PatId},
import_map,
lang_item::LangItemTarget,
path::ModPath,
per_ns::PerNs,
resolver::{HasResolver, Resolver},
src::HasSource as _,
@ -29,15 +33,13 @@ use hir_ty::{
method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate,
InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
};
use ra_db::{CrateId, Edition, FileId};
use ra_prof::profile;
use ra_syntax::{
ast::{self, AttrsOwner, NameOwner},
AstNode,
};
use ra_tt::{Ident, Leaf, Literal, TokenTree};
use rustc_hash::FxHashSet;
use stdx::impl_from;
use syntax::{
ast::{self, AttrsOwner, NameOwner},
AstNode, SmolStr,
};
use tt::{Ident, Leaf, Literal, TokenTree};
use crate::{
db::{DefDatabase, HirDatabase},
@ -85,9 +87,9 @@ impl Crate {
.collect()
}
pub fn root_module(self, db: &dyn HirDatabase) -> Option<Module> {
pub fn root_module(self, db: &dyn HirDatabase) -> Module {
let module_id = db.crate_def_map(self.id).root;
Some(Module::new(self, module_id))
Module::new(self, module_id)
}
pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
@ -126,7 +128,7 @@ impl Crate {
/// Try to get the root URL of the documentation of a crate.
pub fn get_doc_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
// Look for #![doc(html_root_url = "...")]
let attrs = db.attrs(AttrDef::from(self.root_module(db)?).into());
let attrs = db.attrs(AttrDef::from(self.root_module(db)).into());
let doc_attr_q = attrs.by_key("doc");
let doc_url = if doc_attr_q.exists() {
@ -348,7 +350,7 @@ impl Module {
}
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
let _p = profile("Module::diagnostics");
let _p = profile::span("Module::diagnostics");
let crate_def_map = db.crate_def_map(self.id.krate);
crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
for decl in self.declarations(db) {
@ -389,11 +391,7 @@ impl Module {
/// Finds a path that can be used to refer to the given item from within
/// this module, if possible.
pub fn find_use_path(
self,
db: &dyn DefDatabase,
item: impl Into<ItemInNs>,
) -> Option<hir_def::path::ModPath> {
pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
hir_def::find_path::find_path(db, item.into(), self.into())
}
}
@ -476,6 +474,10 @@ impl Struct {
Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id)
}
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
db.struct_data(self.id).repr.clone()
}
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
db.struct_data(self.id).variant_data.clone()
}
@ -710,12 +712,20 @@ impl Function {
db.function_data(self.id).name.clone()
}
pub fn has_self_param(self, db: &dyn HirDatabase) -> bool {
db.function_data(self.id).has_self_param
pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
if !db.function_data(self.id).has_self_param {
return None;
}
Some(SelfParam { func: self.id })
}
pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> {
db.function_data(self.id).params.clone()
pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> {
db.function_data(self.id)
.params
.iter()
.skip(if self.self_param(db).is_some() { 1 } else { 0 })
.map(|_| Param { _ty: () })
.collect()
}
pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
@ -727,6 +737,45 @@ impl Function {
}
}
// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
pub enum Access {
Shared,
Exclusive,
Owned,
}
impl From<Mutability> for Access {
fn from(mutability: Mutability) -> Access {
match mutability {
Mutability::Shared => Access::Shared,
Mutability::Mut => Access::Exclusive,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SelfParam {
func: FunctionId,
}
pub struct Param {
_ty: (),
}
impl SelfParam {
pub fn access(self, db: &dyn HirDatabase) -> Access {
let func_data = db.function_data(self.func);
func_data
.params
.first()
.map(|param| match *param {
TypeRef::Reference(_, mutability) => mutability.into(),
_ => Access::Owned,
})
.unwrap_or(Access::Owned)
}
}
impl HasVisibility for Function {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
let function_data = db.function_data(self.id);
@ -856,7 +905,7 @@ pub struct MacroDef {
impl MacroDef {
/// FIXME: right now, this just returns the root module of the crate that
/// defines this macro. The reasons for this is that macros are expanded
/// early, in `ra_hir_expand`, where modules simply do not exist yet.
/// early, in `hir_expand`, where modules simply do not exist yet.
pub fn module(self, db: &dyn HirDatabase) -> Option<Module> {
let krate = self.id.krate?;
let module_id = db.crate_def_map(krate).root;
@ -924,6 +973,13 @@ where
}
impl AssocItem {
pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
match self {
AssocItem::Function(it) => Some(it.name(db)),
AssocItem::Const(it) => it.name(db),
AssocItem::TypeAlias(it) => Some(it.name(db)),
}
}
pub fn module(self, db: &dyn HirDatabase) -> Module {
match self {
AssocItem::Function(f) => f.module(db),
@ -1047,7 +1103,7 @@ impl Local {
Type::new(db, krate, def, ty)
}
pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::BindPat, ast::SelfParam>> {
pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
let (_body, source_map) = db.body_with_source_map(self.parent.into());
let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
let root = src.file_syntax(db.upcast());
@ -1160,7 +1216,7 @@ impl ImplDef {
.value
.attrs()
.filter_map(|it| {
let path = hir_def::path::ModPath::from_src(it.path()?, &hygenic)?;
let path = ModPath::from_src(it.path()?, &hygenic)?;
if path.as_ident()?.to_string() == "derive" {
Some(it)
} else {
@ -1277,6 +1333,15 @@ impl Type {
db.trait_solve(self.krate, goal).is_some()
}
pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
let copy_trait = match lang_item {
Some(LangItemTarget::TraitId(it)) => it,
_ => return false,
};
self.impls_trait(db, copy_trait.into(), &[])
}
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
let def = match self.ty.value {
Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(def), parameters: _ }) => Some(def),
@ -1298,6 +1363,19 @@ impl Type {
)
}
pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
let adt_id = match self.ty.value {
Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_id), .. }) => adt_id,
_ => return false,
};
let adt = adt_id.into();
match adt {
Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
_ => false,
}
}
pub fn is_raw_ptr(&self) -> bool {
matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
}

View file

@ -13,14 +13,7 @@ pub use hir_expand::db::{
AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery,
};
pub use hir_ty::db::{
AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery,
GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase,
HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, InferQueryQuery,
InherentImplsInCrateQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery,
TraitDatumQuery, TraitImplsInCrateQuery, TraitImplsInDepsQuery, TraitSolveQuery, TyQuery,
ValueTyQuery,
};
pub use hir_ty::db::*;
#[test]
fn hir_database_is_object_safe() {

View file

@ -1,8 +1,6 @@
//! FIXME: write short doc here
pub use hir_def::diagnostics::UnresolvedModule;
pub use hir_expand::diagnostics::{
AstDiagnostic, Diagnostic, DiagnosticSink, DiagnosticSinkBuilder,
};
pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
pub use hir_ty::diagnostics::{
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
};

View file

@ -29,7 +29,7 @@ macro_rules! from_id {
}
from_id![
(ra_db::CrateId, crate::Crate),
(base_db::CrateId, crate::Crate),
(hir_def::ModuleId, crate::Module),
(hir_def::StructId, crate::Struct),
(hir_def::UnionId, crate::Union),

View file

@ -1,4 +1,4 @@
//! FIXME: write short doc here
//! Provides set of implementation for hir's objects that allows get back location in file.
use either::Either;
use hir_def::{
@ -6,7 +6,7 @@ use hir_def::{
src::{HasChildSource, HasSource as _},
Lookup, VariantId,
};
use ra_syntax::ast;
use syntax::ast;
use crate::{
db::HirDatabase, Const, Enum, EnumVariant, Field, FieldSource, Function, ImplDef, MacroDef,

View file

@ -9,11 +9,11 @@
//! It is written in "OO" style. Each type is self contained (as in, it knows it's
//! parents and full context). It should be "clean code".
//!
//! `ra_hir_*` crates are the implementation of the compiler logic.
//! `hir_*` crates are the implementation of the compiler logic.
//! They are written in "ECS" style, with relatively little abstractions.
//! Many types are not self-contained, and explicitly use local indexes, arenas, etc.
//!
//! `ra_hir` is what insulates the "we don't know how to actually write an incremental compiler"
//! `hir` is what insulates the "we don't know how to actually write an incremental compiler"
//! from the ide with completions, hovers, etc. It is a (soft, internal) boundary:
//! https://www.tedinski.com/2018/02/06/system-boundaries.html.
@ -33,10 +33,10 @@ mod has_source;
pub use crate::{
code_model::{
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind, Const,
Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function,
GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef,
Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind,
Const, Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource,
Function, GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef,
ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
},
has_source::HasSource,
link_rewrite::resolve_doc_link,
@ -51,13 +51,19 @@ pub use hir_def::{
docs::Documentation,
item_scope::ItemInNs,
nameres::ModuleSource,
path::{ModPath, Path, PathKind},
type_ref::Mutability,
path::ModPath,
type_ref::{Mutability, TypeRef},
};
pub use hir_expand::{
hygiene::Hygiene,
name::{AsName, Name},
HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, /* FIXME */
MacroFile, Origin,
name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
/* FIXME */ MacroDefId, MacroFile, Origin,
};
pub use hir_ty::display::HirDisplay;
// These are negative re-exports: pub using these names is forbidden, they
// should remain private to hir internals.
#[allow(unused)]
use {
hir_def::path::{Path, PathKind},
hir_expand::hygiene::Hygiene,
};

View file

@ -7,7 +7,7 @@ use url::Url;
use crate::{db::HirDatabase, Adt, AsName, Crate, Hygiene, ItemInNs, ModPath, ModuleDef};
use hir_def::{db::DefDatabase, resolver::Resolver};
use ra_syntax::ast::Path;
use syntax::ast::Path;
pub fn resolve_doc_link<T: Resolvable + Clone, D: DefDatabase + HirDatabase>(
db: &D,

View file

@ -4,30 +4,29 @@ mod source_to_def;
use std::{cell::RefCell, fmt, iter::successors};
use base_db::{FileId, FileRange};
use hir_def::{
resolver::{self, HasResolver, Resolver},
resolver::{self, HasResolver, Resolver, TypeNs},
AsMacroCall, FunctionId, TraitId, VariantId,
};
use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo};
use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
use hir_ty::associated_type_shorthand_candidates;
use itertools::Itertools;
use ra_db::{FileId, FileRange};
use ra_prof::profile;
use ra_syntax::{
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{
algo::{find_node_at_offset, skip_trivia_token},
ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
code_model::Access,
db::HirDatabase,
diagnostics::Diagnostic,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
AssocItem, Callable, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module,
ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
source_analyzer::{resolve_hir_path, SourceAnalyzer},
AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
};
use resolver::TypeNs;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathResolution {
@ -109,24 +108,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.parse(file_id)
}
pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST {
let file_id = d.source().file_id;
let root = self.db.parse_or_expand(file_id).unwrap();
self.imp.cache(root, file_id);
d.ast(self.db.upcast())
}
pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
self.imp.expand(macro_call)
}
pub fn expand_hypothetical(
pub fn speculative_expand(
&self,
actual_macro_call: &ast::MacroCall,
hypothetical_args: &ast::TokenTree,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> {
self.imp.expand_hypothetical(actual_macro_call, hypothetical_args, token_to_map)
self.imp.speculative_expand(actual_macro_call, hypothetical_args, token_to_map)
}
pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
@ -145,8 +136,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.original_range(node)
}
pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
self.imp.diagnostics_range(diagnostics)
pub fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
self.imp.diagnostics_display_range(diagnostics)
}
pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
@ -216,7 +207,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.resolve_record_field(field)
}
pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> {
pub fn resolve_record_field_pat(&self, field: &ast::RecordPatField) -> Option<Field> {
self.imp.resolve_record_field_pat(field)
}
@ -228,15 +219,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.resolve_path(path)
}
pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
self.imp.resolve_extern_crate(extern_crate)
}
pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
self.imp.resolve_variant(record_lit).map(VariantDef::from)
}
pub fn lower_path(&self, path: &ast::Path) -> Option<Path> {
self.imp.lower_path(path)
}
pub fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> {
pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
self.imp.resolve_bind_pat_to_const(pat)
}
@ -275,6 +266,18 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn assert_contains_node(&self, node: &SyntaxNode) {
self.imp.assert_contains_node(node)
}
pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
self.imp.is_unsafe_method_call(method_call_expr)
}
pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
self.imp.is_unsafe_ref_expr(ref_expr)
}
pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
self.imp.is_unsafe_ident_pat(ident_pat)
}
}
impl<'db> SemanticsImpl<'db> {
@ -302,7 +305,7 @@ impl<'db> SemanticsImpl<'db> {
Some(node)
}
fn expand_hypothetical(
fn speculative_expand(
&self,
actual_macro_call: &ast::MacroCall,
hypothetical_args: &ast::TokenTree,
@ -324,7 +327,7 @@ impl<'db> SemanticsImpl<'db> {
}
fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
let _p = profile("descend_into_macros");
let _p = profile::span("descend_into_macros");
let parent = token.parent();
let parent = self.find_file(parent);
let sa = self.analyze2(parent.as_ref(), None);
@ -372,10 +375,11 @@ impl<'db> SemanticsImpl<'db> {
original_range(self.db, node.as_ref())
}
fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
let src = diagnostics.source();
fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
let src = diagnostics.display_source();
let root = self.db.parse_or_expand(src.file_id).unwrap();
let node = src.value.to_node(&root);
self.cache(root, src.file_id);
original_range(self.db, src.with_value(&node))
}
@ -429,7 +433,7 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(field.syntax()).resolve_record_field(self.db, field)
}
fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> {
fn resolve_record_field_pat(&self, field: &ast::RecordPatField) -> Option<Field> {
self.analyze(field.syntax()).resolve_record_field_pat(self.db, field)
}
@ -443,16 +447,22 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(path.syntax()).resolve_path(self.db, path)
}
fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
let krate = self.scope(extern_crate.syntax()).krate()?;
krate.dependencies(self.db).into_iter().find_map(|dep| {
if dep.name == extern_crate.name_ref()?.as_name() {
Some(dep.krate)
} else {
None
}
})
}
fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> {
self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit)
}
fn lower_path(&self, path: &ast::Path) -> Option<Path> {
let src = self.find_file(path.syntax().clone());
Path::from_src(path.clone(), &Hygiene::new(self.db.upcast(), src.file_id.into()))
}
fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> {
fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
}
@ -481,18 +491,19 @@ impl<'db> SemanticsImpl<'db> {
fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
let node = self.find_file(node.clone());
let resolver = self.analyze2(node.as_ref(), None).resolver;
SemanticsScope { db: self.db, resolver }
SemanticsScope { db: self.db, file_id: node.file_id, resolver }
}
fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
let node = self.find_file(node.clone());
let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver;
SemanticsScope { db: self.db, resolver }
SemanticsScope { db: self.db, file_id: node.file_id, resolver }
}
fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
let file_id = self.db.lookup_intern_trait(def.id).id.file_id;
let resolver = def.id.resolver(self.db.upcast());
SemanticsScope { db: self.db, resolver }
SemanticsScope { db: self.db, file_id, resolver }
}
fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer {
@ -501,7 +512,7 @@ impl<'db> SemanticsImpl<'db> {
}
fn analyze2(&self, src: InFile<&SyntaxNode>, offset: Option<TextSize>) -> SourceAnalyzer {
let _p = profile("Semantics::analyze2");
let _p = profile::span("Semantics::analyze2");
let container = match self.with_ctx(|ctx| ctx.find_container(src)) {
Some(it) => it,
@ -559,6 +570,91 @@ impl<'db> SemanticsImpl<'db> {
});
InFile::new(file_id, node)
}
fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
method_call_expr
.receiver()
.and_then(|expr| {
let field_expr = match expr {
ast::Expr::FieldExpr(field_expr) => field_expr,
_ => return None,
};
let ty = self.type_of_expr(&field_expr.expr()?)?;
if !ty.is_packed(self.db) {
return None;
}
let func = self.resolve_method_call(&method_call_expr).map(Function::from)?;
let res = match func.self_param(self.db)?.access(self.db) {
Access::Shared | Access::Exclusive => true,
Access::Owned => false,
};
Some(res)
})
.unwrap_or(false)
}
fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
ref_expr
.expr()
.and_then(|expr| {
let field_expr = match expr {
ast::Expr::FieldExpr(field_expr) => field_expr,
_ => return None,
};
let expr = field_expr.expr()?;
self.type_of_expr(&expr)
})
// Binding a reference to a packed type is possibly unsafe.
.map(|ty| ty.is_packed(self.db))
.unwrap_or(false)
// FIXME This needs layout computation to be correct. It will highlight
// more than it should with the current implementation.
}
fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
if !ident_pat.ref_token().is_some() {
return false;
}
ident_pat
.syntax()
.parent()
.and_then(|parent| {
// `IdentPat` can live under `RecordPat` directly under `RecordPatField` or
// `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`,
// so this tries to lookup the `IdentPat` anywhere along that structure to the
// `RecordPat` so we can get the containing type.
let record_pat = ast::RecordPatField::cast(parent.clone())
.and_then(|record_pat| record_pat.syntax().parent())
.or_else(|| Some(parent.clone()))
.and_then(|parent| {
ast::RecordPatFieldList::cast(parent)?
.syntax()
.parent()
.and_then(ast::RecordPat::cast)
});
// If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
// this is initialized from a `FieldExpr`.
if let Some(record_pat) = record_pat {
self.type_of_pat(&ast::Pat::RecordPat(record_pat))
} else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
let field_expr = match let_stmt.initializer()? {
ast::Expr::FieldExpr(field_expr) => field_expr,
_ => return None,
};
self.type_of_expr(&field_expr.expr()?)
} else {
None
}
})
// Binding a reference to a packed type is possibly unsafe.
.map(|ty| ty.is_packed(self.db))
.unwrap_or(false)
}
}
pub trait ToDef: AstNode + Clone {
@ -594,7 +690,7 @@ to_def_impls![
(crate::EnumVariant, ast::Variant, enum_variant_to_def),
(crate::TypeParam, ast::TypeParam, type_param_to_def),
(crate::MacroDef, ast::MacroCall, macro_call_to_def), // this one is dubious, not all calls are macros
(crate::Local, ast::BindPat, bind_pat_to_def),
(crate::Local, ast::IdentPat, bind_pat_to_def),
];
fn find_root(node: &SyntaxNode) -> SyntaxNode {
@ -604,6 +700,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
#[derive(Debug)]
pub struct SemanticsScope<'a> {
pub db: &'a dyn HirDatabase,
file_id: HirFileId,
resolver: Resolver,
}
@ -612,6 +709,10 @@ impl<'a> SemanticsScope<'a> {
Some(Module { id: self.resolver.module()? })
}
pub fn krate(&self) -> Option<Crate> {
Some(Crate { id: self.resolver.krate()? })
}
/// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
// FIXME: rename to visible_traits to not repeat scope?
pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
@ -643,25 +744,12 @@ impl<'a> SemanticsScope<'a> {
})
}
pub fn resolve_hir_path(&self, path: &Path) -> Option<PathResolution> {
resolve_hir_path(self.db, &self.resolver, path)
}
/// Resolves a path where we know it is a qualifier of another path.
///
/// For example, if we have:
/// ```
/// mod my {
/// pub mod foo {
/// struct Bar;
/// }
///
/// pub fn foo() {}
/// }
/// ```
/// then we know that `foo` in `my::foo::Bar` refers to the module, not the function.
pub fn resolve_hir_path_qualifier(&self, path: &Path) -> Option<PathResolution> {
resolve_hir_path_qualifier(self.db, &self.resolver, path)
/// Resolve a path as-if it was written at the given scope. This is
/// necessary a heuristic, as it doesn't take hygiene into account.
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
let hygiene = Hygiene::new(self.db.upcast(), self.file_id);
let path = Path::from_src(path.clone(), &hygiene)?;
resolve_hir_path(self.db, &self.resolver, &path)
}
}

View file

@ -1,5 +1,6 @@
//! Maps *syntax* of various definitions to their semantic ids.
use base_db::FileId;
use hir_def::{
child_by_source::ChildBySource,
dyn_map::DynMap,
@ -9,14 +10,12 @@ use hir_def::{
ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
};
use hir_expand::{name::AsName, AstId, MacroDefKind};
use ra_db::FileId;
use ra_prof::profile;
use ra_syntax::{
use rustc_hash::FxHashMap;
use stdx::impl_from;
use syntax::{
ast::{self, NameOwner},
match_ast, AstNode, SyntaxNode,
};
use rustc_hash::FxHashMap;
use stdx::impl_from;
use crate::{db::HirDatabase, InFile, MacroDefId};
@ -29,7 +28,7 @@ pub(super) struct SourceToDefCtx<'a, 'b> {
impl SourceToDefCtx<'_, '_> {
pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> {
let _p = profile("SourceBinder::to_module_def");
let _p = profile::span("SourceBinder::to_module_def");
let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| {
let crate_def_map = self.db.crate_def_map(crate_id);
let local_id = crate_def_map.modules_for_file(file).next()?;
@ -39,7 +38,7 @@ impl SourceToDefCtx<'_, '_> {
}
pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
let _p = profile("module_to_def");
let _p = profile::span("module_to_def");
let parent_declaration = src
.as_ref()
.map(|it| it.syntax())
@ -106,7 +105,7 @@ impl SourceToDefCtx<'_, '_> {
}
pub(super) fn bind_pat_to_def(
&mut self,
src: InFile<ast::BindPat>,
src: InFile<ast::IdentPat>,
) -> Option<(DefWithBodyId, PatId)> {
let container = self.find_pat_container(src.as_ref().map(|it| it.syntax()))?;
let (_body, source_map) = self.db.body_with_source_map(container);

View file

@ -13,6 +13,7 @@ use hir_def::{
Body, BodySourceMap,
},
expr::{ExprId, Pat, PatId},
path::{ModPath, Path, PathKind},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, VariantId,
};
@ -21,17 +22,16 @@ use hir_ty::{
diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
InferenceResult, Substs, Ty,
};
use ra_syntax::{
use syntax::{
ast::{self, AstNode},
SyntaxNode, TextRange, TextSize,
};
use crate::{
db::HirDatabase, semantics::PathResolution, Adt, Const, EnumVariant, Field, Function, Local,
MacroDef, ModPath, ModuleDef, Path, PathKind, Static, Struct, Trait, Type, TypeAlias,
TypeParam,
MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam,
};
use ra_db::CrateId;
use base_db::CrateId;
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
/// original source files. It should not be used inside the HIR itself.
@ -182,7 +182,7 @@ impl SourceAnalyzer {
pub(crate) fn resolve_record_field_pat(
&self,
_db: &dyn HirDatabase,
field: &ast::RecordFieldPat,
field: &ast::RecordPatField,
) -> Option<Field> {
let pat_id = self.pat_id(&field.pat()?)?;
let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?;
@ -202,7 +202,7 @@ impl SourceAnalyzer {
pub(crate) fn resolve_bind_pat_to_const(
&self,
db: &dyn HirDatabase,
pat: &ast::BindPat,
pat: &ast::IdentPat,
) -> Option<ModuleDef> {
let pat_id = self.pat_id(&pat.clone().into())?;
let body = self.body.as_ref()?;
@ -265,8 +265,7 @@ impl SourceAnalyzer {
}
// This must be a normal source file rather than macro file.
let hir_path =
crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?;
let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?;
// Case where path is a qualifier of another path, e.g. foo::bar::Baz where we
// trying to resolve foo::bar.
@ -451,7 +450,7 @@ fn adjust(
pub(crate) fn resolve_hir_path(
db: &dyn HirDatabase,
resolver: &Resolver,
path: &crate::Path,
path: &Path,
) -> Option<PathResolution> {
let types =
resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty {
@ -509,10 +508,10 @@ pub(crate) fn resolve_hir_path(
/// }
/// ```
/// then we know that `foo` in `my::foo::Bar` refers to the module, not the function.
pub(crate) fn resolve_hir_path_qualifier(
fn resolve_hir_path_qualifier(
db: &dyn HirDatabase,
resolver: &Resolver,
path: &crate::Path,
path: &Path,
) -> Option<PathResolution> {
let items = resolver
.resolve_module_path_in_items(db.upcast(), path.mod_path())

View file

@ -1,9 +1,9 @@
[package]
edition = "2018"
name = "ra_hir_def"
version = "0.1.0"
authors = ["rust-analyzer developers"]
name = "hir_def"
version = "0.0.0"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false
@ -21,16 +21,15 @@ indexmap = "1.4.0"
smallvec = "1.4.0"
stdx = { path = "../stdx" }
ra_arena = { path = "../ra_arena" }
ra_db = { path = "../ra_db" }
ra_syntax = { path = "../ra_syntax" }
ra_prof = { path = "../ra_prof" }
hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
arena = { path = "../arena" }
base_db = { path = "../base_db" }
syntax = { path = "../syntax" }
profile = { path = "../profile" }
hir_expand = { path = "../hir_expand" }
test_utils = { path = "../test_utils" }
mbe = { path = "../ra_mbe", package = "ra_mbe" }
ra_cfg = { path = "../ra_cfg" }
tt = { path = "../ra_tt", package = "ra_tt" }
mbe = { path = "../mbe" }
cfg = { path = "../cfg" }
tt = { path = "../tt" }
[dev-dependencies]
expect = { path = "../expect" }
expect-test = "0.1"

View file

@ -2,18 +2,19 @@
use std::sync::Arc;
use arena::{map::ArenaMap, Arena};
use either::Either;
use hir_expand::{
name::{AsName, Name},
InFile,
};
use ra_arena::{map::ArenaMap, Arena};
use ra_syntax::ast::{self, NameOwner, VisibilityOwner};
use syntax::ast::{self, NameOwner, VisibilityOwner};
use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
use crate::{
body::{CfgExpander, LowerCtx},
db::DefDatabase,
item_tree::{Field, Fields, ItemTree},
item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
src::HasChildSource,
src::HasSource,
trace::Trace,
@ -22,13 +23,14 @@ use crate::{
EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId,
VariantId,
};
use ra_cfg::CfgOptions;
use cfg::CfgOptions;
/// Note that we use `StructData` for unions as well!
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructData {
pub name: Name,
pub variant_data: Arc<VariantData>,
pub repr: Option<ReprKind>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -58,26 +60,58 @@ pub struct FieldData {
pub visibility: RawVisibility,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReprKind {
Packed,
Other,
}
fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt)
}
fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
match tt.delimiter {
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
_ => return None,
}
let mut it = tt.token_trees.iter();
match it.next()? {
TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
_ => Some(ReprKind::Other),
}
}
impl StructData {
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
let loc = id.lookup(db);
let item_tree = db.item_tree(loc.id.file_id);
let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
let strukt = &item_tree[loc.id.value];
let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields);
Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) })
Arc::new(StructData {
name: strukt.name.clone(),
variant_data: Arc::new(variant_data),
repr,
})
}
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
let loc = id.lookup(db);
let item_tree = db.item_tree(loc.id.file_id);
let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
let union = &item_tree[loc.id.value];
let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields);
Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) })
Arc::new(StructData {
name: union.name.clone(),
variant_data: Arc::new(variant_data),
repr,
})
}
}

View file

@ -2,11 +2,11 @@
use std::{ops, sync::Arc};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{hygiene::Hygiene, AstId, InFile};
use mbe::ast_to_token_tree;
use ra_cfg::{CfgExpr, CfgOptions};
use ra_syntax::{
use syntax::{
ast::{self, AstNode, AttrsOwner},
SmolStr,
};

View file

@ -5,15 +5,14 @@ pub mod scope;
use std::{mem, ops::Index, sync::Arc};
use arena::{map::ArenaMap, Arena};
use base_db::CrateId;
use cfg::CfgOptions;
use drop_bomb::DropBomb;
use either::Either;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
use ra_arena::{map::ArenaMap, Arena};
use ra_cfg::CfgOptions;
use ra_db::CrateId;
use ra_prof::profile;
use ra_syntax::{ast, AstNode, AstPtr};
use rustc_hash::FxHashMap;
use syntax::{ast, AstNode, AstPtr};
use test_utils::mark;
pub(crate) use lower::LowerCtx;
@ -228,7 +227,7 @@ impl Body {
db: &dyn DefDatabase,
def: DefWithBodyId,
) -> (Arc<Body>, Arc<BodySourceMap>) {
let _p = profile("body_with_source_map_query");
let _p = profile::span("body_with_source_map_query");
let mut params = None;
let (file_id, module, body) = match def {
@ -321,7 +320,7 @@ impl BodySourceMap {
#[cfg(test)]
mod tests {
use ra_db::{fixture::WithFixture, SourceDatabase};
use base_db::{fixture::WithFixture, SourceDatabase};
use test_utils::mark;
use crate::ModuleDefId;

View file

@ -1,16 +1,19 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
use std::{any::type_name, sync::Arc};
use arena::Arena;
use either::Either;
use hir_expand::{
hygiene::Hygiene,
name::{name, AsName, Name},
HirFileId, MacroDefId, MacroDefKind,
};
use ra_arena::Arena;
use ra_syntax::{
use rustc_hash::FxHashMap;
use syntax::{
ast::{
self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, ModuleItemOwner, NameOwner,
self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner,
SlicePatComponents,
},
AstNode, AstPtr,
@ -35,9 +38,6 @@ use crate::{
};
use super::{ExprSource, PatSource};
use ast::AstChildren;
use rustc_hash::FxHashMap;
use std::{any::type_name, sync::Arc};
pub(crate) struct LowerCtx {
hygiene: Hygiene,
@ -224,9 +224,22 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
}
// FIXME: we need to record these effects somewhere...
ast::Effect::Async(_) | ast::Effect::Label(_) => {
self.collect_block_opt(e.block_expr())
}
ast::Effect::Label(label) => match e.block_expr() {
Some(block) => {
let res = self.collect_block(block);
match &mut self.body.exprs[res] {
Expr::Block { label: block_label, .. } => {
*block_label =
label.lifetime_token().map(|t| Name::new_lifetime(&t))
}
_ => unreachable!(),
}
res
}
None => self.missing_expr(),
},
// FIXME: we need to record these effects somewhere...
ast::Effect::Async(_) => self.collect_block_opt(e.block_expr()),
},
ast::Expr::BlockExpr(e) => self.collect_block(e),
ast::Expr::LoopExpr(e) => {
@ -316,7 +329,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
}
ast::Expr::MethodCallExpr(e) => {
let receiver = self.collect_expr_opt(e.expr());
let receiver = self.collect_expr_opt(e.receiver());
let args = if let Some(arg_list) = e.arg_list() {
arg_list.args().map(|e| self.collect_expr(e)).collect()
} else {
@ -324,7 +337,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.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it));
self.alloc_expr(
Expr::MethodCall { receiver, method_name, args, generic_args },
syntax_ptr,
@ -460,7 +473,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Missing, syntax_ptr)
}
}
ast::Expr::LambdaExpr(e) => {
ast::Expr::ClosureExpr(e) => {
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
@ -483,7 +496,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
ast::Expr::TupleExpr(e) => {
let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect();
let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr)
}
ast::Expr::BoxExpr(e) => {
@ -556,9 +569,6 @@ impl ExprCollector<'_> {
}
}
}
// FIXME implement HIR for these:
ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
@ -601,26 +611,35 @@ impl ExprCollector<'_> {
self.collect_block_items(&block);
let statements = block
.statements()
.map(|s| match s {
ast::Stmt::LetStmt(stmt) => {
let pat = self.collect_pat_opt(stmt.pat());
let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
Statement::Let { pat, type_ref, initializer }
}
ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
.filter_map(|s| {
let stmt = match s {
ast::Stmt::LetStmt(stmt) => {
let pat = self.collect_pat_opt(stmt.pat());
let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
Statement::Let { pat, type_ref, initializer }
}
ast::Stmt::ExprStmt(stmt) => {
Statement::Expr(self.collect_expr_opt(stmt.expr()))
}
ast::Stmt::Item(_) => return None,
};
Some(stmt)
})
.collect();
let tail = block.expr().map(|e| self.collect_expr(e));
let label = block.label().and_then(|l| l.lifetime_token()).map(|t| Name::new_lifetime(&t));
self.alloc_expr(Expr::Block { statements, tail, label }, syntax_node_ptr)
self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr)
}
fn collect_block_items(&mut self, block: &ast::BlockExpr) {
let container = ContainerId::DefWithBodyId(self.def);
let items = block
.items()
.statements()
.filter_map(|stmt| match stmt {
ast::Stmt::Item(it) => Some(it),
ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None,
})
.filter_map(|item| {
let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
ast::Item::Fn(def) => {
@ -704,7 +723,7 @@ impl ExprCollector<'_> {
fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
let pattern = match &pat {
ast::Pat::BindPat(bp) => {
ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
@ -743,7 +762,7 @@ impl ExprCollector<'_> {
}
ast::Pat::TupleStructPat(p) => {
let path = p.path().and_then(|path| self.expander.parse_path(path));
let (args, ellipsis) = self.collect_tuple_pat(p.args());
let (args, ellipsis) = self.collect_tuple_pat(p.fields());
Pat::TupleStruct { path, args, ellipsis }
}
ast::Pat::RefPat(p) => {
@ -761,40 +780,36 @@ impl ExprCollector<'_> {
}
ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()),
ast::Pat::TuplePat(p) => {
let (args, ellipsis) = self.collect_tuple_pat(p.args());
let (args, ellipsis) = self.collect_tuple_pat(p.fields());
Pat::Tuple { args, ellipsis }
}
ast::Pat::PlaceholderPat(_) => Pat::Wild,
ast::Pat::WildcardPat(_) => Pat::Wild,
ast::Pat::RecordPat(p) => {
let path = p.path().and_then(|path| self.expander.parse_path(path));
let record_field_pat_list =
p.record_field_pat_list().expect("every struct should have a field list");
let mut fields: Vec<_> = record_field_pat_list
.bind_pats()
.filter_map(|bind_pat| {
let ast_pat =
ast::Pat::cast(bind_pat.syntax().clone()).expect("bind pat is a pat");
let args: Vec<_> = p
.record_pat_field_list()
.expect("every struct should have a field list")
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_pat);
let name = bind_pat.name()?.as_name();
let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat })
})
.collect();
let iter = record_field_pat_list.record_field_pats().filter_map(|f| {
let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_pat);
let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat })
});
fields.extend(iter);
let ellipsis = record_field_pat_list.dotdot_token().is_some();
let ellipsis = p
.record_pat_field_list()
.expect("every struct should have a field list")
.dotdot_token()
.is_some();
Pat::Record { path, args: fields, ellipsis }
Pat::Record { path, args, ellipsis }
}
ast::Pat::SlicePat(p) => {
let SlicePatComponents { prefix, slice, suffix } = p.components();
// FIXME properly handle `DotDotPat`
// FIXME properly handle `RestPat`
Pat::Slice {
prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(),
slice: slice.map(|p| self.collect_pat(p)),
@ -811,10 +826,10 @@ impl ExprCollector<'_> {
Pat::Missing
}
}
ast::Pat::DotDotPat(_) => {
// `DotDotPat` requires special handling and should not be mapped
ast::Pat::RestPat(_) => {
// `RestPat` requires special handling and should not be mapped
// to a Pat. Here we are using `Pat::Missing` as a fallback for
// when `DotDotPat` is mapped to `Pat`, which can easily happen
// when `RestPat` is mapped to `Pat`, which can easily happen
// when the source code being analyzed has a malformed pattern
// which includes `..` in a place where it isn't valid.
@ -838,10 +853,10 @@ impl ExprCollector<'_> {
fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
// Find the location of the `..`, if there is one. Note that we do not
// consider the possiblity of there being multiple `..` here.
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::DotDotPat(_)));
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
// We want to skip the `..` pattern here, since we account for it above.
let args = args
.filter(|p| !matches!(p, ast::Pat::DotDotPat(_)))
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
.map(|p| self.collect_pat(p))
.collect();

View file

@ -1,8 +1,8 @@
//! Name resolution for expressions.
use std::sync::Arc;
use arena::{Arena, Idx};
use hir_expand::name::Name;
use ra_arena::{Arena, Idx};
use rustc_hash::FxHashMap;
use crate::{
@ -169,9 +169,9 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
#[cfg(test)]
mod tests {
use base_db::{fixture::WithFixture, FileId, SourceDatabase};
use hir_expand::{name::AsName, InFile};
use ra_db::{fixture::WithFixture, FileId, SourceDatabase};
use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
use syntax::{algo::find_node_at_offset, ast, AstNode};
use test_utils::{assert_eq_text, extract_offset, mark};
use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};

View file

@ -3,8 +3,7 @@
use std::sync::Arc;
use hir_expand::{name::Name, InFile};
use ra_prof::profile;
use ra_syntax::ast;
use syntax::ast;
use crate::{
attr::Attrs,
@ -133,7 +132,7 @@ pub struct ImplData {
impl ImplData {
pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
let _p = profile("impl_data_query");
let _p = profile::span("impl_data_query");
let impl_loc = id.lookup(db);
let item_tree = db.item_tree(impl_loc.id.file_id);

View file

@ -1,10 +1,9 @@
//! Defines database & queries for name resolution.
use std::sync::Arc;
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
use hir_expand::{db::AstDatabase, HirFileId};
use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
use ra_prof::profile;
use ra_syntax::SmolStr;
use syntax::SmolStr;
use crate::{
adt::{EnumData, StructData},
@ -116,6 +115,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
}
fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
let _p = profile("crate_def_map:wait");
let _p = profile::span("crate_def_map:wait");
db.crate_def_map_query(krate)
}

View file

@ -2,8 +2,8 @@
use std::any::Any;
use hir_expand::diagnostics::Diagnostic;
use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
use syntax::{ast, AstPtr, SyntaxNodePtr};
use hir_expand::{HirFileId, InFile};
@ -15,10 +15,13 @@ pub struct UnresolvedModule {
}
impl Diagnostic for UnresolvedModule {
fn code(&self) -> DiagnosticCode {
DiagnosticCode("unresolved-module")
}
fn message(&self) -> String {
"unresolved module".to_string()
}
fn source(&self) -> InFile<SyntaxNodePtr> {
fn display_source(&self) -> InFile<SyntaxNodePtr> {
InFile::new(self.file, self.decl.clone().into())
}
fn as_any(&self) -> &(dyn Any + Send + 'static) {

View file

@ -6,7 +6,7 @@
use std::sync::Arc;
use either::Either;
use ra_syntax::ast;
use syntax::ast;
use crate::{
db::DefDatabase,

View file

@ -12,9 +12,9 @@
//!
//! See also a neighboring `body` module.
use arena::{Idx, RawId};
use hir_expand::name::Name;
use ra_arena::{Idx, RawId};
use ra_syntax::ast::RangeOp;
use syntax::ast::RangeOp;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt},
@ -197,7 +197,7 @@ pub enum ArithOp {
BitAnd,
}
pub use ra_syntax::ast::PrefixOp as UnaryOp;
pub use syntax::ast::PrefixOp as UnaryOp;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Array {
ElementList(Vec<ExprId>),

View file

@ -1,7 +1,6 @@
//! An algorithm to find a path to refer to a certain item.
use hir_expand::name::{known, AsName, Name};
use ra_prof::profile;
use rustc_hash::FxHashSet;
use test_utils::mark;
@ -18,7 +17,7 @@ use crate::{
/// 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");
let _p = profile::span("find_path");
find_path_inner(db, item, from, MAX_PATH_LEN)
}
@ -215,7 +214,7 @@ fn find_local_import_locations(
item: ItemInNs,
from: ModuleId,
) -> Vec<(ModuleId, Name)> {
let _p = profile("find_local_import_locations");
let _p = profile::span("find_local_import_locations");
// `from` can import anything below `from` with visibility of at least `from`, and anything
// above `from` with any visibility. That means we do not need to descend into private siblings
@ -293,9 +292,9 @@ fn find_local_import_locations(
#[cfg(test)]
mod tests {
use base_db::fixture::WithFixture;
use hir_expand::hygiene::Hygiene;
use ra_db::fixture::WithFixture;
use ra_syntax::ast::AstNode;
use syntax::ast::AstNode;
use test_utils::mark;
use crate::test_db::TestDB;
@ -308,12 +307,9 @@ mod tests {
fn check_found_path(ra_fixture: &str, path: &str) {
let (db, pos) = TestDB::with_position(ra_fixture);
let module = db.module_for_file(pos.file_id);
let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
let ast_path = parsed_path_file
.syntax_node()
.descendants()
.find_map(ra_syntax::ast::Path::cast)
.unwrap();
let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
let ast_path =
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
let crate_def_map = db.crate_def_map(module.krate);
@ -442,12 +438,12 @@ mod tests {
// already in scope.
check_found_path(
r#"
//- /main.rs crate:main deps:ra_syntax
//- /main.rs crate:main deps:syntax
use ra_syntax::ast;
use syntax::ast;
<|>
//- /lib.rs crate:ra_syntax
//- /lib.rs crate:syntax
pub mod ast {
pub enum ModuleItem {
A, B, C,
@ -459,18 +455,18 @@ mod tests {
check_found_path(
r#"
//- /main.rs crate:main deps:ra_syntax
//- /main.rs crate:main deps:syntax
<|>
//- /lib.rs crate:ra_syntax
//- /lib.rs crate:syntax
pub mod ast {
pub enum ModuleItem {
A, B, C,
}
}
"#,
"ra_syntax::ast::ModuleItem",
"syntax::ast::ModuleItem",
);
}

View file

@ -4,15 +4,14 @@
//! in rustc.
use std::sync::Arc;
use arena::{map::ArenaMap, Arena};
use base_db::FileId;
use either::Either;
use hir_expand::{
name::{name, AsName, Name},
InFile,
};
use ra_arena::{map::ArenaMap, Arena};
use ra_db::FileId;
use ra_prof::profile;
use ra_syntax::ast::{self, GenericParamsOwner, NameOwner, TypeBoundsOwner};
use syntax::ast::{self, GenericParamsOwner, NameOwner, TypeBoundsOwner};
use crate::{
body::LowerCtx,
@ -73,7 +72,7 @@ impl GenericParams {
db: &dyn DefDatabase,
def: GenericDefId,
) -> Arc<GenericParams> {
let _p = profile("generic_params_query");
let _p = profile::span("generic_params_query");
let generics = match def {
GenericDefId::FunctionId(id) => {
@ -253,7 +252,7 @@ impl GenericParams {
fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) {
for pred in where_clause.predicates() {
let type_ref = match pred.type_ref() {
let type_ref = match pred.ty() {
Some(type_ref) => type_ref,
None => continue,
};
@ -270,7 +269,7 @@ impl GenericParams {
bound: ast::TypeBound,
type_ref: TypeRef,
) {
if bound.question_token().is_some() {
if bound.question_mark_token().is_some() {
// FIXME: remove this bound
return;
}

View file

@ -2,12 +2,12 @@
use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
use base_db::CrateId;
use fst::{self, Streamer};
use indexmap::{map::Entry, IndexMap};
use ra_db::CrateId;
use ra_syntax::SmolStr;
use rustc_hash::{FxHashMap, FxHasher};
use smallvec::SmallVec;
use syntax::SmolStr;
use crate::{
db::DefDatabase,
@ -56,7 +56,7 @@ pub struct ImportMap {
impl ImportMap {
pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
let _p = ra_prof::profile("import_map_query");
let _p = profile::span("import_map_query");
let def_map = db.crate_def_map(krate);
let mut import_map = Self::default();
@ -254,7 +254,7 @@ pub fn search_dependencies<'a>(
krate: CrateId,
query: Query,
) -> Vec<ItemInNs> {
let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query));
let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query));
let graph = db.crate_graph();
let import_maps: Vec<_> =
@ -327,8 +327,8 @@ pub fn search_dependencies<'a>(
#[cfg(test)]
mod tests {
use expect::{expect, Expect};
use ra_db::{fixture::WithFixture, SourceDatabase, Upcast};
use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
use expect_test::{expect, Expect};
use crate::{test_db::TestDB, AssocContainerId, Lookup};

Some files were not shown because too many files have changed in this diff Show more