mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 20:42:04 +00:00
Merge #1737
1737: Report type mismatches in analysis-stats r=matklad a=flodiebold
Only the number usually; each one individually when running with `-v`.
Getting the file/line locations for the exprs was really annoying and I had to make some stuff public (that I didn't remember why it would be `pub(crate)`); maybe I missed some easier way? It would be nice to have some general way for mapping locations 🤔
This reports 1768 mismatches on RA currently; from skimming, this seems to be mostly various kinds of coercions, though there were also some other things.
Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
f39f72db57
7 changed files with 110 additions and 27 deletions
|
@ -1,7 +1,7 @@
|
||||||
use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};
|
use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};
|
||||||
|
|
||||||
use ra_db::SourceDatabase;
|
use ra_db::SourceDatabase;
|
||||||
use ra_hir::{Crate, HasSource, ImplItem, ModuleDef, Ty};
|
use ra_hir::{Crate, HasBodySource, HasSource, HirDisplay, ImplItem, ModuleDef, Ty};
|
||||||
use ra_syntax::AstNode;
|
use ra_syntax::AstNode;
|
||||||
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
@ -66,6 +66,7 @@ pub fn run(verbose: bool, memory_usage: bool, path: &Path, only: Option<&str>) -
|
||||||
let mut num_exprs = 0;
|
let mut num_exprs = 0;
|
||||||
let mut num_exprs_unknown = 0;
|
let mut num_exprs_unknown = 0;
|
||||||
let mut num_exprs_partially_unknown = 0;
|
let mut num_exprs_partially_unknown = 0;
|
||||||
|
let mut num_type_mismatches = 0;
|
||||||
for f in funcs {
|
for f in funcs {
|
||||||
let name = f.name(db);
|
let name = f.name(db);
|
||||||
let mut msg = format!("processing: {}", name);
|
let mut msg = format!("processing: {}", name);
|
||||||
|
@ -100,6 +101,39 @@ pub fn run(verbose: bool, memory_usage: bool, path: &Path, only: Option<&str>) -
|
||||||
num_exprs_partially_unknown += 1;
|
num_exprs_partially_unknown += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
|
||||||
|
num_type_mismatches += 1;
|
||||||
|
if verbose {
|
||||||
|
let src = f.expr_source(db, expr_id);
|
||||||
|
if let Some(src) = src {
|
||||||
|
// FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
|
||||||
|
let original_file = src.file_id.original_file(db);
|
||||||
|
let path = db.file_relative_path(original_file);
|
||||||
|
let line_index = host.analysis().file_line_index(original_file).unwrap();
|
||||||
|
let (start, end) = (
|
||||||
|
line_index.line_col(src.ast.syntax().text_range().start()),
|
||||||
|
line_index.line_col(src.ast.syntax().text_range().end()),
|
||||||
|
);
|
||||||
|
bar.println(format!(
|
||||||
|
"{} {}:{}-{}:{}: Expected {}, got {}",
|
||||||
|
path.display(),
|
||||||
|
start.line + 1,
|
||||||
|
start.col_utf16,
|
||||||
|
end.line + 1,
|
||||||
|
end.col_utf16,
|
||||||
|
mismatch.expected.display(db),
|
||||||
|
mismatch.actual.display(db)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
bar.println(format!(
|
||||||
|
"{}: Expected {}, got {}",
|
||||||
|
name,
|
||||||
|
mismatch.expected.display(db),
|
||||||
|
mismatch.actual.display(db)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bar.inc(1);
|
bar.inc(1);
|
||||||
}
|
}
|
||||||
|
@ -115,6 +149,7 @@ pub fn run(verbose: bool, memory_usage: bool, path: &Path, only: Option<&str>) -
|
||||||
num_exprs_partially_unknown,
|
num_exprs_partially_unknown,
|
||||||
(num_exprs_partially_unknown * 100 / num_exprs)
|
(num_exprs_partially_unknown * 100 / num_exprs)
|
||||||
);
|
);
|
||||||
|
println!("Type mismatches: {}", num_type_mismatches);
|
||||||
println!("Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
|
println!("Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
|
||||||
println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());
|
println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());
|
||||||
|
|
||||||
|
|
|
@ -510,18 +510,6 @@ pub enum DefWithBody {
|
||||||
impl_froms!(DefWithBody: Function, Const, Static);
|
impl_froms!(DefWithBody: Function, Const, Static);
|
||||||
|
|
||||||
impl DefWithBody {
|
impl DefWithBody {
|
||||||
pub fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
|
||||||
db.infer(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body(self, db: &impl HirDatabase) -> Arc<Body> {
|
|
||||||
db.body_hir(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body_source_map(self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
|
|
||||||
db.body_with_source_map(self).1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a resolver for code inside this item.
|
/// Builds a resolver for code inside this item.
|
||||||
pub(crate) fn resolver(self, db: &impl HirDatabase) -> Resolver {
|
pub(crate) fn resolver(self, db: &impl HirDatabase) -> Resolver {
|
||||||
match self {
|
match self {
|
||||||
|
@ -532,6 +520,43 @@ impl DefWithBody {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait HasBody: Copy {
|
||||||
|
fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult>;
|
||||||
|
fn body(self, db: &impl HirDatabase) -> Arc<Body>;
|
||||||
|
fn body_source_map(self, db: &impl HirDatabase) -> Arc<BodySourceMap>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> HasBody for T
|
||||||
|
where
|
||||||
|
T: Into<DefWithBody> + Copy + HasSource,
|
||||||
|
{
|
||||||
|
fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
||||||
|
db.infer(self.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body(self, db: &impl HirDatabase) -> Arc<Body> {
|
||||||
|
db.body_hir(self.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body_source_map(self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
|
||||||
|
db.body_with_source_map(self.into()).1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasBody for DefWithBody {
|
||||||
|
fn infer(self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
||||||
|
db.infer(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body(self, db: &impl HirDatabase) -> Arc<Body> {
|
||||||
|
db.body_hir(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body_source_map(self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
|
||||||
|
db.body_with_source_map(self).1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub(crate) id: FunctionId,
|
pub(crate) id: FunctionId,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use ra_syntax::ast;
|
use ra_syntax::ast::{self, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ids::AstItemDef, AstDatabase, Const, DefDatabase, Enum, EnumVariant, FieldSource, Function,
|
ids::AstItemDef, AstDatabase, Const, DefDatabase, Enum, EnumVariant, FieldSource, Function,
|
||||||
HirFileId, MacroDef, Module, ModuleSource, Static, Struct, StructField, Trait, TypeAlias,
|
HasBody, HirDatabase, HirFileId, MacroDef, Module, ModuleSource, Static, Struct, StructField,
|
||||||
Union,
|
Trait, TypeAlias, Union,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Source<T> {
|
pub struct Source<T> {
|
||||||
|
@ -108,3 +108,27 @@ impl HasSource for MacroDef {
|
||||||
Source { file_id: self.id.0.file_id(), ast: self.id.0.to_node(db) }
|
Source { file_id: self.id.0.file_id(), ast: self.id.0.to_node(db) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait HasBodySource: HasBody + HasSource
|
||||||
|
where
|
||||||
|
Self::Ast: AstNode,
|
||||||
|
{
|
||||||
|
fn expr_source(
|
||||||
|
self,
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
expr_id: crate::expr::ExprId,
|
||||||
|
) -> Option<Source<ast::Expr>> {
|
||||||
|
let source_map = self.body_source_map(db);
|
||||||
|
let expr_syntax = source_map.expr_syntax(expr_id)?;
|
||||||
|
let source = self.source(db);
|
||||||
|
let node = expr_syntax.to_node(&source.ast.syntax());
|
||||||
|
ast::Expr::cast(node).map(|ast| Source { file_id: source.file_id, ast })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> HasBodySource for T
|
||||||
|
where
|
||||||
|
T: HasBody + HasSource,
|
||||||
|
T::Ast: AstNode,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -75,8 +75,8 @@ pub use self::{
|
||||||
|
|
||||||
pub use self::code_model::{
|
pub use self::code_model::{
|
||||||
docs::{DocDef, Docs, Documentation},
|
docs::{DocDef, Docs, Documentation},
|
||||||
src::{HasSource, Source},
|
src::{HasBodySource, HasSource, Source},
|
||||||
BuiltinType, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
|
BuiltinType, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
|
||||||
EnumVariant, FieldSource, FnData, Function, MacroDef, Module, ModuleDef, ModuleSource, Static,
|
EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef, ModuleSource,
|
||||||
Struct, StructField, Trait, TypeAlias, Union,
|
Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,9 +27,9 @@ use crate::{
|
||||||
name,
|
name,
|
||||||
path::{PathKind, PathSegment},
|
path::{PathKind, PathSegment},
|
||||||
ty::method_resolution::implements_trait,
|
ty::method_resolution::implements_trait,
|
||||||
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
|
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HasBody, HirDatabase,
|
||||||
MacroDef, Module, ModuleDef, Name, Path, PerNs, Resolution, Resolver, Static, Struct, Trait,
|
HirFileId, MacroDef, Module, ModuleDef, Name, Path, PerNs, Resolution, Resolver, Static,
|
||||||
Ty,
|
Struct, Trait, Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Locates the module by `FileId`. Picks topmost module in the file.
|
/// Locates the module by `FileId`. Picks topmost module in the file.
|
||||||
|
|
|
@ -50,8 +50,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
ty::infer::diagnostics::InferenceDiagnostic,
|
ty::infer::diagnostics::InferenceDiagnostic,
|
||||||
type_ref::{Mutability, TypeRef},
|
type_ref::{Mutability, TypeRef},
|
||||||
AdtDef, ConstData, DefWithBody, FnData, Function, HirDatabase, ImplItem, ModuleDef, Name, Path,
|
AdtDef, ConstData, DefWithBody, FnData, Function, HasBody, HirDatabase, ImplItem, ModuleDef,
|
||||||
StructField,
|
Name, Path, StructField,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod unify;
|
mod unify;
|
||||||
|
|
|
@ -15,9 +15,8 @@ impl SyntaxNodePtr {
|
||||||
SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
|
SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_node(self, root: &SyntaxNode) -> SyntaxNode {
|
pub fn to_node(self, parent: &SyntaxNode) -> SyntaxNode {
|
||||||
assert!(root.parent().is_none());
|
successors(Some(parent.clone()), |node| {
|
||||||
successors(Some(root.clone()), |node| {
|
|
||||||
node.children().find(|it| self.range.is_subrange(&it.text_range()))
|
node.children().find(|it| self.range.is_subrange(&it.text_range()))
|
||||||
})
|
})
|
||||||
.find(|it| it.text_range() == self.range && it.kind() == self.kind)
|
.find(|it| it.text_range() == self.range && it.kind() == self.kind)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue