Merge branch 'master' of github.com:rust-analyzer/rust-analyzer
This commit is contained in:
commit
af3c19e85f
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -675,6 +675,16 @@ version = "2.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memmap"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi 0.3.8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
@ -1112,6 +1122,7 @@ dependencies = [
|
|||||||
"difference",
|
"difference",
|
||||||
"goblin",
|
"goblin",
|
||||||
"libloading",
|
"libloading",
|
||||||
|
"memmap",
|
||||||
"ra_mbe",
|
"ra_mbe",
|
||||||
"ra_proc_macro",
|
"ra_proc_macro",
|
||||||
"ra_tt",
|
"ra_tt",
|
||||||
|
@ -20,6 +20,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
|
diagnostics::Diagnostic,
|
||||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||||
AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name,
|
AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name,
|
||||||
@ -126,6 +127,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||||||
original_range(self.db, node.as_ref())
|
original_range(self.db, node.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
|
||||||
|
let src = diagnostics.source();
|
||||||
|
let root = self.db.parse_or_expand(src.file_id).unwrap();
|
||||||
|
let node = src.value.to_node(&root);
|
||||||
|
original_range(self.db, src.with_value(&node))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
|
pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
|
||||||
let node = self.find_file(node);
|
let node = self.find_file(node);
|
||||||
node.ancestors_with_macros(self.db).map(|it| it.value)
|
node.ancestors_with_macros(self.db).map(|it| it.value)
|
||||||
|
@ -23,7 +23,7 @@ use hir_ty::{
|
|||||||
};
|
};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
SyntaxNode, SyntaxNodePtr, TextUnit,
|
SyntaxNode, TextRange, TextUnit,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -56,7 +56,7 @@ impl SourceAnalyzer {
|
|||||||
let scopes = db.expr_scopes(def);
|
let scopes = db.expr_scopes(def);
|
||||||
let scope = match offset {
|
let scope = match offset {
|
||||||
None => scope_for(&scopes, &source_map, node),
|
None => scope_for(&scopes, &source_map, node),
|
||||||
Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)),
|
Some(offset) => scope_for_offset(db, &scopes, &source_map, node.with_value(offset)),
|
||||||
};
|
};
|
||||||
let resolver = resolver_for_scope(db.upcast(), def, scope);
|
let resolver = resolver_for_scope(db.upcast(), def, scope);
|
||||||
SourceAnalyzer {
|
SourceAnalyzer {
|
||||||
@ -304,6 +304,7 @@ fn scope_for(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scope_for_offset(
|
fn scope_for_offset(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
scopes: &ExprScopes,
|
scopes: &ExprScopes,
|
||||||
source_map: &BodySourceMap,
|
source_map: &BodySourceMap,
|
||||||
offset: InFile<TextUnit>,
|
offset: InFile<TextUnit>,
|
||||||
@ -317,21 +318,63 @@ fn scope_for_offset(
|
|||||||
if source.file_id != offset.file_id {
|
if source.file_id != offset.file_id {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let syntax_node_ptr = source.value.syntax_node_ptr();
|
let root = source.file_syntax(db.upcast());
|
||||||
Some((syntax_node_ptr, scope))
|
let node = source.value.to_node(&root);
|
||||||
|
Some((node.syntax().text_range(), scope))
|
||||||
})
|
})
|
||||||
// find containing scope
|
// find containing scope
|
||||||
.min_by_key(|(ptr, _scope)| {
|
.min_by_key(|(expr_range, _scope)| {
|
||||||
(
|
(
|
||||||
!(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()),
|
!(expr_range.start() <= offset.value && offset.value <= expr_range.end()),
|
||||||
ptr.range().len(),
|
expr_range.len(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(ptr, scope)| {
|
.map(|(expr_range, scope)| {
|
||||||
adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope)
|
adjust(db, scopes, source_map, expr_range, offset.file_id, offset.value)
|
||||||
|
.unwrap_or(*scope)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: during completion, cursor might be outside of any particular
|
||||||
|
// expression. Try to figure out the correct scope...
|
||||||
|
fn adjust(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
scopes: &ExprScopes,
|
||||||
|
source_map: &BodySourceMap,
|
||||||
|
expr_range: TextRange,
|
||||||
|
file_id: HirFileId,
|
||||||
|
offset: TextUnit,
|
||||||
|
) -> Option<ScopeId> {
|
||||||
|
let child_scopes = scopes
|
||||||
|
.scope_by_expr()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(id, scope)| {
|
||||||
|
let source = source_map.expr_syntax(*id).ok()?;
|
||||||
|
// FIXME: correctly handle macro expansion
|
||||||
|
if source.file_id != file_id {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let root = source.file_syntax(db.upcast());
|
||||||
|
let node = source.value.to_node(&root);
|
||||||
|
Some((node.syntax().text_range(), scope))
|
||||||
|
})
|
||||||
|
.filter(|(range, _)| {
|
||||||
|
range.start() <= offset && range.is_subrange(&expr_range) && *range != expr_range
|
||||||
|
});
|
||||||
|
|
||||||
|
child_scopes
|
||||||
|
.max_by(|(r1, _), (r2, _)| {
|
||||||
|
if r2.is_subrange(&r1) {
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
} else if r1.is_subrange(&r2) {
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
} else {
|
||||||
|
r1.start().cmp(&r2.start())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(_ptr, scope)| *scope)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_hir_path(
|
pub(crate) fn resolve_hir_path(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
@ -376,41 +419,3 @@ pub(crate) fn resolve_hir_path(
|
|||||||
.map(|def| PathResolution::Macro(def.into()))
|
.map(|def| PathResolution::Macro(def.into()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: during completion, cursor might be outside of any particular
|
|
||||||
// expression. Try to figure out the correct scope...
|
|
||||||
fn adjust(
|
|
||||||
scopes: &ExprScopes,
|
|
||||||
source_map: &BodySourceMap,
|
|
||||||
ptr: SyntaxNodePtr,
|
|
||||||
file_id: HirFileId,
|
|
||||||
offset: TextUnit,
|
|
||||||
) -> Option<ScopeId> {
|
|
||||||
let r = ptr.range();
|
|
||||||
let child_scopes = scopes
|
|
||||||
.scope_by_expr()
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(id, scope)| {
|
|
||||||
let source = source_map.expr_syntax(*id).ok()?;
|
|
||||||
// FIXME: correctly handle macro expansion
|
|
||||||
if source.file_id != file_id {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let syntax_node_ptr = source.value.syntax_node_ptr();
|
|
||||||
Some((syntax_node_ptr, scope))
|
|
||||||
})
|
|
||||||
.map(|(ptr, scope)| (ptr.range(), scope))
|
|
||||||
.filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
|
|
||||||
|
|
||||||
child_scopes
|
|
||||||
.max_by(|(r1, _), (r2, _)| {
|
|
||||||
if r2.is_subrange(&r1) {
|
|
||||||
std::cmp::Ordering::Greater
|
|
||||||
} else if r1.is_subrange(&r2) {
|
|
||||||
std::cmp::Ordering::Less
|
|
||||||
} else {
|
|
||||||
r1.start().cmp(&r2.start())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|(_ptr, scope)| *scope)
|
|
||||||
}
|
|
||||||
|
@ -210,7 +210,7 @@ pub struct BodySourceMap {
|
|||||||
expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
|
expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
|
||||||
pat_map: FxHashMap<PatSource, PatId>,
|
pat_map: FxHashMap<PatSource, PatId>,
|
||||||
pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
|
pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
|
||||||
field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>,
|
field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordField>>>,
|
||||||
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
|
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +303,7 @@ impl BodySourceMap {
|
|||||||
self.pat_map.get(&src).cloned()
|
self.pat_map.get(&src).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::RecordField> {
|
pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> {
|
||||||
self.field_map[&(expr, field)].clone()
|
self.field_map[&(expr, field)].clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,7 +320,8 @@ impl ExprCollector<'_> {
|
|||||||
|
|
||||||
let res = self.alloc_expr(record_lit, syntax_ptr);
|
let res = self.alloc_expr(record_lit, syntax_ptr);
|
||||||
for (i, ptr) in field_ptrs.into_iter().enumerate() {
|
for (i, ptr) in field_ptrs.into_iter().enumerate() {
|
||||||
self.source_map.field_map.insert((res, i), ptr);
|
let src = self.expander.to_source(ptr);
|
||||||
|
self.source_map.field_map.insert((res, i), src);
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@ -650,6 +651,7 @@ impl ExprCollector<'_> {
|
|||||||
ast::Pat::SlicePat(p) => {
|
ast::Pat::SlicePat(p) => {
|
||||||
let SlicePatComponents { prefix, slice, suffix } = p.components();
|
let SlicePatComponents { prefix, slice, suffix } = p.components();
|
||||||
|
|
||||||
|
// FIXME properly handle `DotDotPat`
|
||||||
Pat::Slice {
|
Pat::Slice {
|
||||||
prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(),
|
prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(),
|
||||||
slice: slice.map(|p| self.collect_pat(p)),
|
slice: slice.map(|p| self.collect_pat(p)),
|
||||||
@ -666,9 +668,15 @@ impl ExprCollector<'_> {
|
|||||||
Pat::Missing
|
Pat::Missing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Pat::DotDotPat(_) => unreachable!(
|
ast::Pat::DotDotPat(_) => {
|
||||||
"`DotDotPat` requires special handling and should not be mapped to a Pat."
|
// `DotDotPat` 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 the source code being analyzed has a malformed pattern
|
||||||
|
// which includes `..` in a place where it isn't valid.
|
||||||
|
|
||||||
|
Pat::Missing
|
||||||
|
}
|
||||||
// FIXME: implement
|
// FIXME: implement
|
||||||
ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
|
ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use hir_expand::name::{name, Name};
|
use hir_expand::name::{name, AsName, Name};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum Signedness {
|
pub enum Signedness {
|
||||||
@ -75,33 +75,39 @@ impl BuiltinType {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for BuiltinType {
|
impl AsName for BuiltinType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn as_name(&self) -> Name {
|
||||||
let type_name = match self {
|
match self {
|
||||||
BuiltinType::Char => "char",
|
BuiltinType::Char => name![char],
|
||||||
BuiltinType::Bool => "bool",
|
BuiltinType::Bool => name![bool],
|
||||||
BuiltinType::Str => "str",
|
BuiltinType::Str => name![str],
|
||||||
BuiltinType::Int(BuiltinInt { signedness, bitness }) => match (signedness, bitness) {
|
BuiltinType::Int(BuiltinInt { signedness, bitness }) => match (signedness, bitness) {
|
||||||
(Signedness::Signed, IntBitness::Xsize) => "isize",
|
(Signedness::Signed, IntBitness::Xsize) => name![isize],
|
||||||
(Signedness::Signed, IntBitness::X8) => "i8",
|
(Signedness::Signed, IntBitness::X8) => name![i8],
|
||||||
(Signedness::Signed, IntBitness::X16) => "i16",
|
(Signedness::Signed, IntBitness::X16) => name![i16],
|
||||||
(Signedness::Signed, IntBitness::X32) => "i32",
|
(Signedness::Signed, IntBitness::X32) => name![i32],
|
||||||
(Signedness::Signed, IntBitness::X64) => "i64",
|
(Signedness::Signed, IntBitness::X64) => name![i64],
|
||||||
(Signedness::Signed, IntBitness::X128) => "i128",
|
(Signedness::Signed, IntBitness::X128) => name![i128],
|
||||||
|
|
||||||
(Signedness::Unsigned, IntBitness::Xsize) => "usize",
|
(Signedness::Unsigned, IntBitness::Xsize) => name![usize],
|
||||||
(Signedness::Unsigned, IntBitness::X8) => "u8",
|
(Signedness::Unsigned, IntBitness::X8) => name![u8],
|
||||||
(Signedness::Unsigned, IntBitness::X16) => "u16",
|
(Signedness::Unsigned, IntBitness::X16) => name![u16],
|
||||||
(Signedness::Unsigned, IntBitness::X32) => "u32",
|
(Signedness::Unsigned, IntBitness::X32) => name![u32],
|
||||||
(Signedness::Unsigned, IntBitness::X64) => "u64",
|
(Signedness::Unsigned, IntBitness::X64) => name![u64],
|
||||||
(Signedness::Unsigned, IntBitness::X128) => "u128",
|
(Signedness::Unsigned, IntBitness::X128) => name![u128],
|
||||||
},
|
},
|
||||||
BuiltinType::Float(BuiltinFloat { bitness }) => match bitness {
|
BuiltinType::Float(BuiltinFloat { bitness }) => match bitness {
|
||||||
FloatBitness::X32 => "f32",
|
FloatBitness::X32 => name![f32],
|
||||||
FloatBitness::X64 => "f64",
|
FloatBitness::X64 => name![f64],
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
f.write_str(type_name)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BuiltinType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let type_name = self.as_name();
|
||||||
|
type_name.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ impl Diagnostic for UnresolvedModule {
|
|||||||
"unresolved module".to_string()
|
"unresolved module".to_string()
|
||||||
}
|
}
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
InFile { file_id: self.file, value: self.decl.clone().into() }
|
InFile::new(self.file, self.decl.clone().into())
|
||||||
}
|
}
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
self
|
self
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
visibility::Visibility,
|
visibility::Visibility,
|
||||||
CrateId, ModuleDefId, ModuleId,
|
CrateId, ModuleDefId, ModuleId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::{known, Name};
|
use hir_expand::name::{known, AsName, Name};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
const MAX_PATH_LEN: usize = 15;
|
const MAX_PATH_LEN: usize = 15;
|
||||||
@ -113,6 +113,11 @@ fn find_path_inner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - if the item is a builtin, it's in scope
|
||||||
|
if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
|
||||||
|
return Some(ModPath::from_segments(PathKind::Plain, vec![builtin.as_name()]));
|
||||||
|
}
|
||||||
|
|
||||||
// Recursive case:
|
// Recursive case:
|
||||||
// - if the item is an enum variant, refer to it via the enum
|
// - if the item is an enum variant, refer to it via the enum
|
||||||
if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
|
if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
|
||||||
@ -523,4 +528,18 @@ mod tests {
|
|||||||
"#;
|
"#;
|
||||||
check_found_path(code, "megaalloc::Arc");
|
check_found_path(code, "megaalloc::Arc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builtins_are_in_scope() {
|
||||||
|
let code = r#"
|
||||||
|
//- /main.rs
|
||||||
|
<|>
|
||||||
|
|
||||||
|
pub mod primitive {
|
||||||
|
pub use u8;
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
check_found_path(code, "u8");
|
||||||
|
check_found_path(code, "u16");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,13 @@
|
|||||||
|
|
||||||
use std::{any::Any, fmt};
|
use std::{any::Any, fmt};
|
||||||
|
|
||||||
use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange};
|
use ra_syntax::{SyntaxNode, SyntaxNodePtr};
|
||||||
|
|
||||||
use crate::{db::AstDatabase, InFile};
|
use crate::{db::AstDatabase, InFile};
|
||||||
|
|
||||||
pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
|
pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
|
||||||
fn message(&self) -> String;
|
fn message(&self) -> String;
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr>;
|
fn source(&self) -> InFile<SyntaxNodePtr>;
|
||||||
fn highlight_range(&self) -> TextRange {
|
|
||||||
self.source().value.range()
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static);
|
fn as_any(&self) -> &(dyn Any + Send + 'static);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ impl Diagnostic for NoSuchField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn source(&self) -> InFile<SyntaxNodePtr> {
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
InFile { file_id: self.file, value: self.field.clone().into() }
|
InFile::new(self.file, self.field.clone().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
@ -682,10 +682,10 @@ mod diagnostics {
|
|||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
InferenceDiagnostic::NoSuchField { expr, field } => {
|
InferenceDiagnostic::NoSuchField { expr, field } => {
|
||||||
let file = owner.lookup(db.upcast()).source(db.upcast()).file_id;
|
let source = owner.lookup(db.upcast()).source(db.upcast());
|
||||||
let (_, source_map) = db.body_with_source_map(owner.into());
|
let (_, source_map) = db.body_with_source_map(owner.into());
|
||||||
let field = source_map.field_syntax(*expr, *field);
|
let field = source_map.field_syntax(*expr, *field);
|
||||||
sink.push(NoSuchField { file, field })
|
sink.push(NoSuchField { file: source.file_id, field: field.value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
// Trivial cases, this should go after `never` check to
|
// Trivial cases, this should go after `never` check to
|
||||||
// avoid infer result type to be never
|
// avoid infer result type to be never
|
||||||
_ => {
|
_ => {
|
||||||
if self.table.unify_inner_trivial(&from_ty, &to_ty) {
|
if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
return self.table.unify_substs(st1, st2, 0);
|
return self.table.unify_substs(st1, st2, 0);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if self.table.unify_inner_trivial(&derefed_ty, &to_ty) {
|
if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ use test_utils::tested_by;
|
|||||||
|
|
||||||
use super::{InferenceContext, Obligation};
|
use super::{InferenceContext, Obligation};
|
||||||
use crate::{
|
use crate::{
|
||||||
BoundVar, Canonical, DebruijnIndex, InEnvironment, InferTy, Substs, Ty, TypeCtor, TypeWalk,
|
BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty,
|
||||||
|
TypeCtor, TypeWalk,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
@ -226,16 +227,26 @@ impl InferenceTable {
|
|||||||
(Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
|
(Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
|
||||||
self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
|
self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
|
||||||
}
|
}
|
||||||
_ => self.unify_inner_trivial(&ty1, &ty2),
|
|
||||||
|
_ => self.unify_inner_trivial(&ty1, &ty2, depth),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
|
||||||
match (ty1, ty2) {
|
match (ty1, ty2) {
|
||||||
(Ty::Unknown, _) | (_, Ty::Unknown) => true,
|
(Ty::Unknown, _) | (_, Ty::Unknown) => true,
|
||||||
|
|
||||||
(Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true,
|
(Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true,
|
||||||
|
|
||||||
|
(Ty::Dyn(dyn1), Ty::Dyn(dyn2)) if dyn1.len() == dyn2.len() => {
|
||||||
|
for (pred1, pred2) in dyn1.iter().zip(dyn2.iter()) {
|
||||||
|
if !self.unify_preds(pred1, pred2, depth + 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
(Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
|
(Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
|
||||||
| (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
|
| (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
|
||||||
| (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
|
| (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
|
||||||
@ -268,6 +279,31 @@ impl InferenceTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unify_preds(
|
||||||
|
&mut self,
|
||||||
|
pred1: &GenericPredicate,
|
||||||
|
pred2: &GenericPredicate,
|
||||||
|
depth: usize,
|
||||||
|
) -> bool {
|
||||||
|
match (pred1, pred2) {
|
||||||
|
(GenericPredicate::Implemented(tr1), GenericPredicate::Implemented(tr2))
|
||||||
|
if tr1.trait_ == tr2.trait_ =>
|
||||||
|
{
|
||||||
|
self.unify_substs(&tr1.substs, &tr2.substs, depth + 1)
|
||||||
|
}
|
||||||
|
(GenericPredicate::Projection(proj1), GenericPredicate::Projection(proj2))
|
||||||
|
if proj1.projection_ty.associated_ty == proj2.projection_ty.associated_ty =>
|
||||||
|
{
|
||||||
|
self.unify_substs(
|
||||||
|
&proj1.projection_ty.parameters,
|
||||||
|
&proj2.projection_ty.parameters,
|
||||||
|
depth + 1,
|
||||||
|
) && self.unify_inner(&proj1.ty, &proj2.ty, depth + 1)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// If `ty` is a type variable with known type, returns that type;
|
/// If `ty` is a type variable with known type, returns that type;
|
||||||
/// otherwise, return ty.
|
/// otherwise, return ty.
|
||||||
pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
|
pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
|
||||||
|
@ -396,12 +396,12 @@ impl Substs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return Substs that replace each parameter by a bound variable.
|
/// Return Substs that replace each parameter by a bound variable.
|
||||||
pub(crate) fn bound_vars(generic_params: &Generics) -> Substs {
|
pub(crate) fn bound_vars(generic_params: &Generics, debruijn: DebruijnIndex) -> Substs {
|
||||||
Substs(
|
Substs(
|
||||||
generic_params
|
generic_params
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, _)| Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx)))
|
.map(|(idx, _)| Ty::Bound(BoundVar::new(debruijn, idx)))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ use crate::{
|
|||||||
pub struct TyLoweringContext<'a> {
|
pub struct TyLoweringContext<'a> {
|
||||||
pub db: &'a dyn HirDatabase,
|
pub db: &'a dyn HirDatabase,
|
||||||
pub resolver: &'a Resolver,
|
pub resolver: &'a Resolver,
|
||||||
|
in_binders: DebruijnIndex,
|
||||||
/// Note: Conceptually, it's thinkable that we could be in a location where
|
/// Note: Conceptually, it's thinkable that we could be in a location where
|
||||||
/// some type params should be represented as placeholders, and others
|
/// some type params should be represented as placeholders, and others
|
||||||
/// should be converted to variables. I think in practice, this isn't
|
/// should be converted to variables. I think in practice, this isn't
|
||||||
@ -53,7 +54,27 @@ impl<'a> TyLoweringContext<'a> {
|
|||||||
let impl_trait_counter = std::cell::Cell::new(0);
|
let impl_trait_counter = std::cell::Cell::new(0);
|
||||||
let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
|
let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
|
||||||
let type_param_mode = TypeParamLoweringMode::Placeholder;
|
let type_param_mode = TypeParamLoweringMode::Placeholder;
|
||||||
Self { db, resolver, impl_trait_mode, impl_trait_counter, type_param_mode }
|
let in_binders = DebruijnIndex::INNERMOST;
|
||||||
|
Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_shifted_in<T>(
|
||||||
|
&self,
|
||||||
|
debruijn: DebruijnIndex,
|
||||||
|
f: impl FnOnce(&TyLoweringContext) -> T,
|
||||||
|
) -> T {
|
||||||
|
let new_ctx = Self {
|
||||||
|
in_binders: self.in_binders.shifted_in_from(debruijn),
|
||||||
|
impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
|
||||||
|
..*self
|
||||||
|
};
|
||||||
|
let result = f(&new_ctx);
|
||||||
|
self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self {
|
||||||
|
Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
|
pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
|
||||||
@ -134,22 +155,26 @@ impl Ty {
|
|||||||
}
|
}
|
||||||
TypeRef::DynTrait(bounds) => {
|
TypeRef::DynTrait(bounds) => {
|
||||||
let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
|
let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
|
||||||
let predicates = bounds
|
let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
|
||||||
|
bounds
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
|
.flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
|
||||||
.collect();
|
.collect()
|
||||||
|
});
|
||||||
Ty::Dyn(predicates)
|
Ty::Dyn(predicates)
|
||||||
}
|
}
|
||||||
TypeRef::ImplTrait(bounds) => {
|
TypeRef::ImplTrait(bounds) => {
|
||||||
match ctx.impl_trait_mode {
|
match ctx.impl_trait_mode {
|
||||||
ImplTraitLoweringMode::Opaque => {
|
ImplTraitLoweringMode::Opaque => {
|
||||||
let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
|
let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
|
||||||
let predicates = bounds
|
let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
|
||||||
|
bounds
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|b| {
|
.flat_map(|b| {
|
||||||
GenericPredicate::from_type_bound(ctx, b, self_ty.clone())
|
GenericPredicate::from_type_bound(ctx, b, self_ty.clone())
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
|
});
|
||||||
Ty::Opaque(predicates)
|
Ty::Opaque(predicates)
|
||||||
}
|
}
|
||||||
ImplTraitLoweringMode::Param => {
|
ImplTraitLoweringMode::Param => {
|
||||||
@ -180,7 +205,7 @@ impl Ty {
|
|||||||
(0, 0, 0, 0)
|
(0, 0, 0, 0)
|
||||||
};
|
};
|
||||||
Ty::Bound(BoundVar::new(
|
Ty::Bound(BoundVar::new(
|
||||||
DebruijnIndex::INNERMOST,
|
ctx.in_binders,
|
||||||
idx as usize + parent_params + self_params + list_params,
|
idx as usize + parent_params + self_params + list_params,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -293,7 +318,7 @@ impl Ty {
|
|||||||
TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id),
|
TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id),
|
||||||
TypeParamLoweringMode::Variable => {
|
TypeParamLoweringMode::Variable => {
|
||||||
let idx = generics.param_idx(param_id).expect("matching generics");
|
let idx = generics.param_idx(param_id).expect("matching generics");
|
||||||
Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx))
|
Ty::Bound(BoundVar::new(ctx.in_binders, idx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,7 +328,9 @@ impl Ty {
|
|||||||
TypeParamLoweringMode::Placeholder => {
|
TypeParamLoweringMode::Placeholder => {
|
||||||
Substs::type_params_for_generics(&generics)
|
Substs::type_params_for_generics(&generics)
|
||||||
}
|
}
|
||||||
TypeParamLoweringMode::Variable => Substs::bound_vars(&generics),
|
TypeParamLoweringMode::Variable => {
|
||||||
|
Substs::bound_vars(&generics, ctx.in_binders)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
ctx.db.impl_self_ty(impl_id).subst(&substs)
|
ctx.db.impl_self_ty(impl_id).subst(&substs)
|
||||||
}
|
}
|
||||||
@ -313,7 +340,9 @@ impl Ty {
|
|||||||
TypeParamLoweringMode::Placeholder => {
|
TypeParamLoweringMode::Placeholder => {
|
||||||
Substs::type_params_for_generics(&generics)
|
Substs::type_params_for_generics(&generics)
|
||||||
}
|
}
|
||||||
TypeParamLoweringMode::Variable => Substs::bound_vars(&generics),
|
TypeParamLoweringMode::Variable => {
|
||||||
|
Substs::bound_vars(&generics, ctx.in_binders)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
ctx.db.ty(adt.into()).subst(&substs)
|
ctx.db.ty(adt.into()).subst(&substs)
|
||||||
}
|
}
|
||||||
@ -797,7 +826,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
|||||||
/// function body.
|
/// function body.
|
||||||
fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
|
fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
|
||||||
let generics = generics(db.upcast(), def.into());
|
let generics = generics(db.upcast(), def.into());
|
||||||
let substs = Substs::bound_vars(&generics);
|
let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
|
||||||
Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
|
Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,7 +880,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
|
|||||||
return type_for_adt(db, def.into());
|
return type_for_adt(db, def.into());
|
||||||
}
|
}
|
||||||
let generics = generics(db.upcast(), def.into());
|
let generics = generics(db.upcast(), def.into());
|
||||||
let substs = Substs::bound_vars(&generics);
|
let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
|
||||||
Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
|
Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -876,13 +905,13 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -
|
|||||||
return type_for_adt(db, def.parent.into());
|
return type_for_adt(db, def.parent.into());
|
||||||
}
|
}
|
||||||
let generics = generics(db.upcast(), def.parent.into());
|
let generics = generics(db.upcast(), def.parent.into());
|
||||||
let substs = Substs::bound_vars(&generics);
|
let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
|
||||||
Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
|
Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
|
fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
|
||||||
let generics = generics(db.upcast(), adt.into());
|
let generics = generics(db.upcast(), adt.into());
|
||||||
let substs = Substs::bound_vars(&generics);
|
let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
|
||||||
Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs))
|
Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,7 +921,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
|
|||||||
let ctx =
|
let ctx =
|
||||||
TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
|
TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
|
||||||
let type_ref = &db.type_alias_data(t).type_ref;
|
let type_ref = &db.type_alias_data(t).type_ref;
|
||||||
let substs = Substs::bound_vars(&generics);
|
let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
|
||||||
let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error));
|
let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error));
|
||||||
Binders::new(substs.len(), inner)
|
Binders::new(substs.len(), inner)
|
||||||
}
|
}
|
||||||
|
@ -484,3 +484,52 @@ fn main() {
|
|||||||
|
|
||||||
assert_eq!("()", super::type_at_pos(&db, pos));
|
assert_eq!("()", super::type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue_3999_slice() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
fn foo(params: &[usize]) {
|
||||||
|
match params {
|
||||||
|
[ps @ .., _] => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[8; 14) 'params': &[usize]
|
||||||
|
[26; 81) '{ ... } }': ()
|
||||||
|
[32; 79) 'match ... }': ()
|
||||||
|
[38; 44) 'params': &[usize]
|
||||||
|
[55; 67) '[ps @ .., _]': [usize]
|
||||||
|
[65; 66) '_': usize
|
||||||
|
[71; 73) '{}': ()
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue_3999_struct() {
|
||||||
|
// rust-analyzer should not panic on seeing this malformed
|
||||||
|
// record pattern.
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
struct Bar {
|
||||||
|
a: bool,
|
||||||
|
}
|
||||||
|
fn foo(b: Bar) {
|
||||||
|
match b {
|
||||||
|
Bar { a: .. } => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[36; 37) 'b': Bar
|
||||||
|
[44; 96) '{ ... } }': ()
|
||||||
|
[50; 94) 'match ... }': ()
|
||||||
|
[56; 57) 'b': Bar
|
||||||
|
[68; 81) 'Bar { a: .. }': Bar
|
||||||
|
[77; 79) '..': bool
|
||||||
|
[85; 87) '{}': ()
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1210,6 +1210,42 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dyn_trait_in_impl() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
trait Trait<T, U> {
|
||||||
|
fn foo(&self) -> (T, U);
|
||||||
|
}
|
||||||
|
struct S<T, U> {}
|
||||||
|
impl<T, U> S<T, U> {
|
||||||
|
fn bar(&self) -> &dyn Trait<T, U> { loop {} }
|
||||||
|
}
|
||||||
|
trait Trait2<T, U> {
|
||||||
|
fn baz(&self) -> (T, U);
|
||||||
|
}
|
||||||
|
impl<T, U> Trait2<T, U> for dyn Trait<T, U> { }
|
||||||
|
|
||||||
|
fn test(s: S<u32, i32>) {
|
||||||
|
s.bar().baz();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[33; 37) 'self': &Self
|
||||||
|
[103; 107) 'self': &S<T, U>
|
||||||
|
[129; 140) '{ loop {} }': &dyn Trait<T, U>
|
||||||
|
[131; 138) 'loop {}': !
|
||||||
|
[136; 138) '{}': ()
|
||||||
|
[176; 180) 'self': &Self
|
||||||
|
[252; 253) 's': S<u32, i32>
|
||||||
|
[268; 290) '{ ...z(); }': ()
|
||||||
|
[274; 275) 's': S<u32, i32>
|
||||||
|
[274; 281) 's.bar()': &dyn Trait<u32, i32>
|
||||||
|
[274; 287) 's.bar().baz()': (u32, i32)
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dyn_trait_bare() {
|
fn dyn_trait_bare() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
@ -2204,3 +2240,201 @@ fn test(x: Box<dyn Trait>) {
|
|||||||
);
|
);
|
||||||
assert_eq!(t, "()");
|
assert_eq!(t, "()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_to_owned() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct String {}
|
||||||
|
pub trait ToOwned {
|
||||||
|
type Owned;
|
||||||
|
fn to_owned(&self) -> Self::Owned;
|
||||||
|
}
|
||||||
|
impl ToOwned for str {
|
||||||
|
type Owned = String;
|
||||||
|
}
|
||||||
|
fn test() {
|
||||||
|
"foo".to_owned()<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "String");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iterator_chain() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
//- /main.rs
|
||||||
|
#[lang = "fn_once"]
|
||||||
|
trait FnOnce<Args> {
|
||||||
|
type Output;
|
||||||
|
}
|
||||||
|
#[lang = "fn_mut"]
|
||||||
|
trait FnMut<Args>: FnOnce<Args> { }
|
||||||
|
|
||||||
|
enum Option<T> { Some(T), None }
|
||||||
|
use Option::*;
|
||||||
|
|
||||||
|
pub trait Iterator {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
|
||||||
|
where
|
||||||
|
F: FnMut(Self::Item) -> Option<B>,
|
||||||
|
{ loop {} }
|
||||||
|
|
||||||
|
fn for_each<F>(self, f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Self::Item),
|
||||||
|
{ loop {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IntoIterator {
|
||||||
|
type Item;
|
||||||
|
type IntoIter: Iterator<Item = Self::Item>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FilterMap<I, F> { }
|
||||||
|
impl<B, I: Iterator, F> Iterator for FilterMap<I, F>
|
||||||
|
where
|
||||||
|
F: FnMut(I::Item) -> Option<B>,
|
||||||
|
{
|
||||||
|
type Item = B;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
impl<I: Iterator> IntoIterator for I {
|
||||||
|
type Item = I::Item;
|
||||||
|
type IntoIter = I;
|
||||||
|
|
||||||
|
fn into_iter(self) -> I {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Vec<T> {}
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
fn new() -> Self { loop {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoIterator for Vec<T> {
|
||||||
|
type Item = T;
|
||||||
|
type IntoIter = IntoIter<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IntoIter<T> { }
|
||||||
|
impl<T> Iterator for IntoIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Vec::<i32>::new().into_iter()
|
||||||
|
.filter_map(|x| if x > 0 { Some(x as u32) } else { None })
|
||||||
|
.for_each(|y| { y; });
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[240; 244) 'self': Self
|
||||||
|
[246; 247) 'f': F
|
||||||
|
[331; 342) '{ loop {} }': FilterMap<Self, F>
|
||||||
|
[333; 340) 'loop {}': !
|
||||||
|
[338; 340) '{}': ()
|
||||||
|
[363; 367) 'self': Self
|
||||||
|
[369; 370) 'f': F
|
||||||
|
[419; 430) '{ loop {} }': ()
|
||||||
|
[421; 428) 'loop {}': !
|
||||||
|
[426; 428) '{}': ()
|
||||||
|
[539; 543) 'self': Self
|
||||||
|
[868; 872) 'self': I
|
||||||
|
[879; 899) '{ ... }': I
|
||||||
|
[889; 893) 'self': I
|
||||||
|
[958; 969) '{ loop {} }': Vec<T>
|
||||||
|
[960; 967) 'loop {}': !
|
||||||
|
[965; 967) '{}': ()
|
||||||
|
[1156; 1287) '{ ... }); }': ()
|
||||||
|
[1162; 1177) 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
|
||||||
|
[1162; 1179) 'Vec::<...:new()': Vec<i32>
|
||||||
|
[1162; 1191) 'Vec::<...iter()': IntoIter<i32>
|
||||||
|
[1162; 1256) 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
|
||||||
|
[1162; 1284) 'Vec::<... y; })': ()
|
||||||
|
[1210; 1255) '|x| if...None }': |i32| -> Option<u32>
|
||||||
|
[1211; 1212) 'x': i32
|
||||||
|
[1214; 1255) 'if x >...None }': Option<u32>
|
||||||
|
[1217; 1218) 'x': i32
|
||||||
|
[1217; 1222) 'x > 0': bool
|
||||||
|
[1221; 1222) '0': i32
|
||||||
|
[1223; 1241) '{ Some...u32) }': Option<u32>
|
||||||
|
[1225; 1229) 'Some': Some<u32>(u32) -> Option<u32>
|
||||||
|
[1225; 1239) 'Some(x as u32)': Option<u32>
|
||||||
|
[1230; 1231) 'x': i32
|
||||||
|
[1230; 1238) 'x as u32': u32
|
||||||
|
[1247; 1255) '{ None }': Option<u32>
|
||||||
|
[1249; 1253) 'None': Option<u32>
|
||||||
|
[1273; 1283) '|y| { y; }': |u32| -> ()
|
||||||
|
[1274; 1275) 'y': u32
|
||||||
|
[1277; 1283) '{ y; }': ()
|
||||||
|
[1279; 1280) 'y': u32
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_assoc() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
struct Bar;
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
trait A {
|
||||||
|
type OutputA;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl A for Bar {
|
||||||
|
type OutputA = Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait B {
|
||||||
|
type Output;
|
||||||
|
fn foo() -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:A> B for T {
|
||||||
|
type Output = T::OutputA;
|
||||||
|
fn foo() -> Self::Output { loop {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Bar::foo()<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "Foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trait_object_no_coercion() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer_with_mismatches(r#"
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
fn foo(x: &dyn Foo) {}
|
||||||
|
|
||||||
|
fn test(x: &dyn Foo) {
|
||||||
|
foo(x);
|
||||||
|
}
|
||||||
|
"#, true),
|
||||||
|
@r###"
|
||||||
|
[22; 23) 'x': &dyn Foo
|
||||||
|
[35; 37) '{}': ()
|
||||||
|
[47; 48) 'x': &dyn Foo
|
||||||
|
[60; 75) '{ foo(x); }': ()
|
||||||
|
[66; 69) 'foo': fn foo(&dyn Foo)
|
||||||
|
[66; 72) 'foo(x)': ()
|
||||||
|
[70; 71) 'x': &dyn Foo
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ use ra_db::{
|
|||||||
use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
|
use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
|
db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
|
||||||
ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
|
ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) mod tls;
|
pub(super) mod tls;
|
||||||
@ -815,7 +815,7 @@ pub(crate) fn associated_ty_data_query(
|
|||||||
// Lower bounds -- we could/should maybe move this to a separate query in `lower`
|
// Lower bounds -- we could/should maybe move this to a separate query in `lower`
|
||||||
let type_alias_data = db.type_alias_data(type_alias);
|
let type_alias_data = db.type_alias_data(type_alias);
|
||||||
let generic_params = generics(db.upcast(), type_alias.into());
|
let generic_params = generics(db.upcast(), type_alias.into());
|
||||||
let bound_vars = Substs::bound_vars(&generic_params);
|
let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
|
||||||
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
|
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
|
||||||
let ctx = crate::TyLoweringContext::new(db, &resolver)
|
let ctx = crate::TyLoweringContext::new(db, &resolver)
|
||||||
.with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
|
.with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
|
||||||
@ -849,7 +849,7 @@ pub(crate) fn trait_datum_query(
|
|||||||
let trait_data = db.trait_data(trait_);
|
let trait_data = db.trait_data(trait_);
|
||||||
debug!("trait {:?} = {:?}", trait_id, trait_data.name);
|
debug!("trait {:?} = {:?}", trait_id, trait_data.name);
|
||||||
let generic_params = generics(db.upcast(), trait_.into());
|
let generic_params = generics(db.upcast(), trait_.into());
|
||||||
let bound_vars = Substs::bound_vars(&generic_params);
|
let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
|
||||||
let flags = chalk_rust_ir::TraitFlags {
|
let flags = chalk_rust_ir::TraitFlags {
|
||||||
auto: trait_data.auto,
|
auto: trait_data.auto,
|
||||||
upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate,
|
upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate,
|
||||||
@ -888,7 +888,7 @@ pub(crate) fn struct_datum_query(
|
|||||||
.as_generic_def()
|
.as_generic_def()
|
||||||
.map(|generic_def| {
|
.map(|generic_def| {
|
||||||
let generic_params = generics(db.upcast(), generic_def);
|
let generic_params = generics(db.upcast(), generic_def);
|
||||||
let bound_vars = Substs::bound_vars(&generic_params);
|
let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
|
||||||
convert_where_clauses(db, generic_def, &bound_vars)
|
convert_where_clauses(db, generic_def, &bound_vars)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(Vec::new);
|
.unwrap_or_else(Vec::new);
|
||||||
@ -934,7 +934,7 @@ fn impl_def_datum(
|
|||||||
let impl_data = db.impl_data(impl_id);
|
let impl_data = db.impl_data(impl_id);
|
||||||
|
|
||||||
let generic_params = generics(db.upcast(), impl_id.into());
|
let generic_params = generics(db.upcast(), impl_id.into());
|
||||||
let bound_vars = Substs::bound_vars(&generic_params);
|
let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
|
||||||
let trait_ = trait_ref.trait_;
|
let trait_ = trait_ref.trait_;
|
||||||
let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate {
|
let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate {
|
||||||
chalk_rust_ir::ImplType::Local
|
chalk_rust_ir::ImplType::Local
|
||||||
|
@ -142,11 +142,11 @@ fn add_function_impl(
|
|||||||
CompletionItemKind::Function
|
CompletionItemKind::Function
|
||||||
};
|
};
|
||||||
|
|
||||||
let snippet = format!("{} {{}}", display);
|
let snippet = format!("{} {{\n $0\n}}", display);
|
||||||
|
|
||||||
let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
|
let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
|
||||||
|
|
||||||
builder.text_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc);
|
builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_type_alias_impl(
|
fn add_type_alias_impl(
|
||||||
@ -217,9 +217,10 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
|
|
||||||
use insta::assert_debug_snapshot;
|
use insta::assert_debug_snapshot;
|
||||||
|
|
||||||
|
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
|
||||||
|
|
||||||
fn complete(code: &str) -> Vec<CompletionItem> {
|
fn complete(code: &str) -> Vec<CompletionItem> {
|
||||||
do_completion(code, CompletionKind::Magic)
|
do_completion(code, CompletionKind::Magic)
|
||||||
}
|
}
|
||||||
@ -255,7 +256,7 @@ mod tests {
|
|||||||
label: "fn test()",
|
label: "fn test()",
|
||||||
source_range: [209; 210),
|
source_range: [209; 210),
|
||||||
delete: [209; 210),
|
delete: [209; 210),
|
||||||
insert: "fn test() {}",
|
insert: "fn test() {\n $0\n}",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
lookup: "test",
|
lookup: "test",
|
||||||
},
|
},
|
||||||
@ -313,7 +314,7 @@ mod tests {
|
|||||||
label: "fn test()",
|
label: "fn test()",
|
||||||
source_range: [139; 140),
|
source_range: [139; 140),
|
||||||
delete: [139; 140),
|
delete: [139; 140),
|
||||||
insert: "fn test() {}",
|
insert: "fn test() {\n $0\n}",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
lookup: "test",
|
lookup: "test",
|
||||||
},
|
},
|
||||||
@ -342,7 +343,7 @@ mod tests {
|
|||||||
label: "fn foo()",
|
label: "fn foo()",
|
||||||
source_range: [141; 142),
|
source_range: [141; 142),
|
||||||
delete: [138; 142),
|
delete: [138; 142),
|
||||||
insert: "fn foo() {}",
|
insert: "fn foo() {\n $0\n}",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
lookup: "foo",
|
lookup: "foo",
|
||||||
},
|
},
|
||||||
@ -374,7 +375,7 @@ mod tests {
|
|||||||
label: "fn foo_bar()",
|
label: "fn foo_bar()",
|
||||||
source_range: [200; 201),
|
source_range: [200; 201),
|
||||||
delete: [197; 201),
|
delete: [197; 201),
|
||||||
insert: "fn foo_bar() {}",
|
insert: "fn foo_bar() {\n $0\n}",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
lookup: "foo_bar",
|
lookup: "foo_bar",
|
||||||
},
|
},
|
||||||
@ -425,7 +426,7 @@ mod tests {
|
|||||||
label: "fn foo()",
|
label: "fn foo()",
|
||||||
source_range: [144; 145),
|
source_range: [144; 145),
|
||||||
delete: [141; 145),
|
delete: [141; 145),
|
||||||
insert: "fn foo<T>() {}",
|
insert: "fn foo<T>() {\n $0\n}",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
lookup: "foo",
|
lookup: "foo",
|
||||||
},
|
},
|
||||||
@ -454,7 +455,7 @@ mod tests {
|
|||||||
label: "fn foo()",
|
label: "fn foo()",
|
||||||
source_range: [166; 167),
|
source_range: [166; 167),
|
||||||
delete: [163; 167),
|
delete: [163; 167),
|
||||||
insert: "fn foo<T>()\nwhere T: Into<String> {}",
|
insert: "fn foo<T>()\nwhere T: Into<String> {\n $0\n}",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
lookup: "foo",
|
lookup: "foo",
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
//! Completion of names from the current scope, e.g. locals and imported items.
|
//! Completion of names from the current scope, e.g. locals and imported items.
|
||||||
|
|
||||||
|
use hir::ScopeDef;
|
||||||
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::completion::{CompletionContext, Completions};
|
use crate::completion::{CompletionContext, Completions};
|
||||||
|
use ra_syntax::AstNode;
|
||||||
|
|
||||||
pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !ctx.is_trivial_path {
|
if !ctx.is_trivial_path {
|
||||||
@ -14,12 +18,23 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res));
|
ctx.scope().process_all_names(&mut |name, res| {
|
||||||
|
if ctx.use_item_syntax.is_some() {
|
||||||
|
if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
|
||||||
|
if name_ref.syntax().text() == name.to_string().as_str() {
|
||||||
|
tested_by!(self_fulfilling_completion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc.add_resolution(ctx, name.to_string(), &res)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use insta::assert_debug_snapshot;
|
use insta::assert_debug_snapshot;
|
||||||
|
use test_utils::covers;
|
||||||
|
|
||||||
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
|
use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
|
||||||
|
|
||||||
@ -27,6 +42,29 @@ mod tests {
|
|||||||
do_completion(ra_fixture, CompletionKind::Reference)
|
do_completion(ra_fixture, CompletionKind::Reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn self_fulfilling_completion() {
|
||||||
|
covers!(self_fulfilling_completion);
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
r#"
|
||||||
|
use foo<|>
|
||||||
|
use std::collections;
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "collections",
|
||||||
|
source_range: [21; 24),
|
||||||
|
delete: [21; 24),
|
||||||
|
insert: "collections",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bind_pat_and_path_ignore_at() {
|
fn bind_pat_and_path_ignore_at() {
|
||||||
assert_debug_snapshot!(
|
assert_debug_snapshot!(
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
//! FIXME: write short doc here
|
//! Collects diagnostics & fixits for a single file.
|
||||||
|
//!
|
||||||
|
//! The tricky bit here is that diagnostics are produced by hir in terms of
|
||||||
|
//! macro-expanded files, but we need to present them to the users in terms of
|
||||||
|
//! original files. So we need to map the ranges.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
@ -46,7 +50,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
let mut sink = DiagnosticSink::new(|d| {
|
let mut sink = DiagnosticSink::new(|d| {
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: None,
|
fix: None,
|
||||||
})
|
})
|
||||||
@ -62,7 +66,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
let create_file = FileSystemEdit::CreateFile { source_root, path };
|
let create_file = FileSystemEdit::CreateFile { source_root, path };
|
||||||
let fix = SourceChange::file_system_edit("create module", create_file);
|
let fix = SourceChange::file_system_edit("create module", create_file);
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: Some(fix),
|
fix: Some(fix),
|
||||||
@ -95,7 +99,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
};
|
};
|
||||||
|
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix,
|
fix,
|
||||||
@ -103,7 +107,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
})
|
})
|
||||||
.on::<hir::diagnostics::MissingMatchArms, _>(|d| {
|
.on::<hir::diagnostics::MissingMatchArms, _>(|d| {
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: None,
|
fix: None,
|
||||||
@ -115,7 +119,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
let edit = TextEdit::replace(node.syntax().text_range(), replacement);
|
||||||
let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit);
|
let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit);
|
||||||
res.borrow_mut().push(Diagnostic {
|
res.borrow_mut().push(Diagnostic {
|
||||||
range: d.highlight_range(),
|
range: sema.diagnostics_range(d).range,
|
||||||
message: d.message(),
|
message: d.message(),
|
||||||
severity: Severity::Error,
|
severity: Severity::Error,
|
||||||
fix: Some(fix),
|
fix: Some(fix),
|
||||||
@ -621,6 +625,62 @@ mod tests {
|
|||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range_mapping_out_of_macros() {
|
||||||
|
let (analysis, file_id) = single_file(
|
||||||
|
r"
|
||||||
|
fn some() {}
|
||||||
|
fn items() {}
|
||||||
|
fn here() {}
|
||||||
|
|
||||||
|
macro_rules! id {
|
||||||
|
($($tt:tt)*) => { $($tt)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _x = id![Foo { a: 42 }];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Foo {
|
||||||
|
pub a: i32,
|
||||||
|
pub b: i32,
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let diagnostics = analysis.diagnostics(file_id).unwrap();
|
||||||
|
assert_debug_snapshot!(diagnostics, @r###"
|
||||||
|
[
|
||||||
|
Diagnostic {
|
||||||
|
message: "Missing structure fields:\n- b",
|
||||||
|
range: [224; 233),
|
||||||
|
fix: Some(
|
||||||
|
SourceChange {
|
||||||
|
label: "fill struct fields",
|
||||||
|
source_file_edits: [
|
||||||
|
SourceFileEdit {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
edit: TextEdit {
|
||||||
|
atoms: [
|
||||||
|
AtomTextEdit {
|
||||||
|
delete: [3; 9),
|
||||||
|
insert: "{a:42, b: ()}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
file_system_edits: [],
|
||||||
|
cursor_position: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
severity: Error,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_unnecessary_braces_in_use_statement() {
|
fn test_check_unnecessary_braces_in_use_statement() {
|
||||||
check_not_applicable(
|
check_not_applicable(
|
||||||
|
@ -237,7 +237,8 @@ fn should_show_param_hint(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
if param_name.is_empty()
|
if param_name.is_empty()
|
||||||
|| is_argument_similar_to_param(argument, param_name)
|
|| is_argument_similar_to_param(argument, param_name)
|
||||||
|| Some(param_name) == fn_signature.name.as_ref().map(String::as_str)
|
|| Some(param_name.trim_start_matches('_'))
|
||||||
|
== fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -255,6 +256,8 @@ fn should_show_param_hint(
|
|||||||
|
|
||||||
fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool {
|
fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool {
|
||||||
let argument_string = remove_ref(argument.clone()).syntax().to_string();
|
let argument_string = remove_ref(argument.clone()).syntax().to_string();
|
||||||
|
let param_name = param_name.trim_start_matches('_');
|
||||||
|
let argument_string = argument_string.trim_start_matches('_');
|
||||||
argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name)
|
argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1094,8 +1097,10 @@ struct Param {}
|
|||||||
|
|
||||||
fn different_order(param: &Param) {}
|
fn different_order(param: &Param) {}
|
||||||
fn different_order_mut(param: &mut Param) {}
|
fn different_order_mut(param: &mut Param) {}
|
||||||
|
fn has_underscore(_param: bool) {}
|
||||||
|
|
||||||
fn twiddle(twiddle: bool) {}
|
fn twiddle(twiddle: bool) {}
|
||||||
|
fn doo(_doo: bool) {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let container: TestVarContainer = TestVarContainer { test_var: 42 };
|
let container: TestVarContainer = TestVarContainer { test_var: 42 };
|
||||||
@ -1112,11 +1117,15 @@ fn main() {
|
|||||||
test_processed.frob(false);
|
test_processed.frob(false);
|
||||||
|
|
||||||
twiddle(true);
|
twiddle(true);
|
||||||
|
doo(true);
|
||||||
|
|
||||||
let param_begin: Param = Param {};
|
let param_begin: Param = Param {};
|
||||||
different_order(¶m_begin);
|
different_order(¶m_begin);
|
||||||
different_order(&mut param_begin);
|
different_order(&mut param_begin);
|
||||||
|
|
||||||
|
let param: bool = true;
|
||||||
|
has_underscore(param);
|
||||||
|
|
||||||
let a: f64 = 7.0;
|
let a: f64 = 7.0;
|
||||||
let b: f64 = 4.0;
|
let b: f64 = 4.0;
|
||||||
let _: f64 = a.div_euclid(b);
|
let _: f64 = a.div_euclid(b);
|
||||||
|
@ -8,4 +8,5 @@ test_utils::marks!(
|
|||||||
test_resolve_parent_module_on_module_decl
|
test_resolve_parent_module_on_module_decl
|
||||||
search_filters_by_range
|
search_filters_by_range
|
||||||
dont_insert_macro_call_parens_unncessary
|
dont_insert_macro_call_parens_unncessary
|
||||||
|
self_fulfilling_completion
|
||||||
);
|
);
|
||||||
|
@ -174,7 +174,8 @@ pub(crate) fn highlight(
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element");
|
assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element");
|
||||||
let res = res.pop().unwrap();
|
let mut res = res.pop().unwrap();
|
||||||
|
res.sort_by_key(|range| range.range.start());
|
||||||
// Check that ranges are sorted and disjoint
|
// Check that ranges are sorted and disjoint
|
||||||
assert!(res
|
assert!(res
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -156,3 +156,15 @@ fn main() {
|
|||||||
fs::write(dst_file, &actual_html).unwrap();
|
fs::write(dst_file, &actual_html).unwrap();
|
||||||
assert_eq_text!(expected_html, actual_html);
|
assert_eq_text!(expected_html, actual_html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ranges_sorted() {
|
||||||
|
let (analysis, file_id) = single_file(
|
||||||
|
r#"
|
||||||
|
#[foo(bar = "bar")]
|
||||||
|
macro_rules! test {}
|
||||||
|
}"#
|
||||||
|
.trim(),
|
||||||
|
);
|
||||||
|
let _ = analysis.highlight(file_id).unwrap();
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ ra_mbe = { path = "../ra_mbe" }
|
|||||||
ra_proc_macro = { path = "../ra_proc_macro" }
|
ra_proc_macro = { path = "../ra_proc_macro" }
|
||||||
goblin = "0.2.1"
|
goblin = "0.2.1"
|
||||||
libloading = "0.6.0"
|
libloading = "0.6.0"
|
||||||
|
memmap = "0.7"
|
||||||
test_utils = { path = "../test_utils" }
|
test_utils = { path = "../test_utils" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
//! Handles dynamic library loading for proc macro
|
//! Handles dynamic library loading for proc macro
|
||||||
|
|
||||||
use crate::{proc_macro::bridge, rustc_server::TokenStream};
|
use crate::{proc_macro::bridge, rustc_server::TokenStream};
|
||||||
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use goblin::{mach::Mach, Object};
|
use goblin::{mach::Mach, Object};
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
|
use memmap::Mmap;
|
||||||
use ra_proc_macro::ProcMacroKind;
|
use ra_proc_macro::ProcMacroKind;
|
||||||
|
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
@ -16,55 +18,54 @@ fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> I
|
|||||||
IoError::new(IoErrorKind::InvalidData, e)
|
IoError::new(IoErrorKind::InvalidData, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> {
|
fn is_derive_registrar_symbol(symbol: &str) -> bool {
|
||||||
let buffer = std::fs::read(file)?;
|
symbol.contains(NEW_REGISTRAR_SYMBOL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
|
||||||
|
let file = File::open(file)?;
|
||||||
|
let buffer = unsafe { Mmap::map(&file)? };
|
||||||
let object = Object::parse(&buffer).map_err(invalid_data_err)?;
|
let object = Object::parse(&buffer).map_err(invalid_data_err)?;
|
||||||
|
|
||||||
match object {
|
match object {
|
||||||
Object::Elf(elf) => {
|
Object::Elf(elf) => {
|
||||||
let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
|
let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
|
||||||
let names = symbols.iter().map(|s| s.to_string()).collect();
|
let name =
|
||||||
Ok(names)
|
symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string());
|
||||||
|
Ok(name)
|
||||||
}
|
}
|
||||||
Object::PE(pe) => {
|
Object::PE(pe) => {
|
||||||
let symbol_names =
|
let name = pe
|
||||||
pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect();
|
.exports
|
||||||
Ok(symbol_names)
|
.iter()
|
||||||
|
.flat_map(|s| s.name)
|
||||||
|
.find(|s| is_derive_registrar_symbol(s))
|
||||||
|
.map(|s| s.to_string());
|
||||||
|
Ok(name)
|
||||||
}
|
}
|
||||||
Object::Mach(mach) => match mach {
|
Object::Mach(Mach::Binary(binary)) => {
|
||||||
Mach::Binary(binary) => {
|
|
||||||
let exports = binary.exports().map_err(invalid_data_err)?;
|
let exports = binary.exports().map_err(invalid_data_err)?;
|
||||||
let names = exports
|
let name = exports
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
// In macos doc:
|
// In macos doc:
|
||||||
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
|
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
|
||||||
// Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
|
// Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
|
||||||
// prepended with an underscore.
|
// prepended with an underscore.
|
||||||
if s.name.starts_with("_") {
|
if s.name.starts_with("_") {
|
||||||
s.name[1..].to_string()
|
&s.name[1..]
|
||||||
} else {
|
} else {
|
||||||
s.name
|
&s.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.find(|s| is_derive_registrar_symbol(s))
|
||||||
Ok(names)
|
.map(|s| s.to_string());
|
||||||
|
Ok(name)
|
||||||
}
|
}
|
||||||
Mach::Fat(_) => Ok(vec![]),
|
_ => Ok(None),
|
||||||
},
|
|
||||||
Object::Archive(_) | Object::Unknown(_) => Ok(vec![]),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_derive_registrar_symbol(symbol: &str) -> bool {
|
|
||||||
symbol.contains(NEW_REGISTRAR_SYMBOL)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
|
|
||||||
let symbols = get_symbols_from_lib(file)?;
|
|
||||||
Ok(symbols.into_iter().find(|s| is_derive_registrar_symbol(s)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads dynamic library in platform dependent manner.
|
/// Loads dynamic library in platform dependent manner.
|
||||||
///
|
///
|
||||||
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
|
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
|
||||||
|
@ -75,9 +75,7 @@ pub(crate) fn load_cargo(
|
|||||||
let proc_macro_client = if !with_proc_macro {
|
let proc_macro_client = if !with_proc_macro {
|
||||||
ProcMacroClient::dummy()
|
ProcMacroClient::dummy()
|
||||||
} else {
|
} else {
|
||||||
let mut path = std::env::current_exe()?;
|
let path = std::env::current_exe()?;
|
||||||
path.pop();
|
|
||||||
path.push("rust-analyzer");
|
|
||||||
ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap()
|
ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap()
|
||||||
};
|
};
|
||||||
let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
|
let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
|
||||||
|
@ -134,9 +134,7 @@ impl Config {
|
|||||||
|
|
||||||
match get::<bool>(value, "/procMacro/enabled") {
|
match get::<bool>(value, "/procMacro/enabled") {
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
if let Ok(mut path) = std::env::current_exe() {
|
if let Ok(path) = std::env::current_exe() {
|
||||||
path.pop();
|
|
||||||
path.push("rust-analyzer");
|
|
||||||
self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()]));
|
self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ fn check_todo(path: &Path, text: &str) {
|
|||||||
}
|
}
|
||||||
if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
|
if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
|
||||||
panic!(
|
panic!(
|
||||||
"\nTODO markers should not be committed to the master branch,\n\
|
"\nTODO markers or todo! macros should not be committed to the master branch,\n\
|
||||||
use FIXME instead\n\
|
use FIXME instead\n\
|
||||||
{}\n",
|
{}\n",
|
||||||
path.display(),
|
path.display(),
|
||||||
@ -47,9 +47,9 @@ fn check_trailing_ws(path: &Path, text: &str) {
|
|||||||
if is_exclude_dir(path, &["test_data"]) {
|
if is_exclude_dir(path, &["test_data"]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for line in text.lines() {
|
for (line_number, line) in text.lines().enumerate() {
|
||||||
if line.chars().last().map(char::is_whitespace) == Some(true) {
|
if line.chars().last().map(char::is_whitespace) == Some(true) {
|
||||||
panic!("Trailing whitespace in {}", path.display())
|
panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user