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:
bors[bot] 2019-09-02 14:07:17 +00:00 committed by GitHub
commit f39f72db57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 27 deletions

View File

@ -1,7 +1,7 @@
use std::{collections::HashSet, fmt::Write, path::Path, time::Instant};
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 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_unknown = 0;
let mut num_exprs_partially_unknown = 0;
let mut num_type_mismatches = 0;
for f in funcs {
let name = f.name(db);
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;
}
}
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);
}
@ -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 * 100 / num_exprs)
);
println!("Type mismatches: {}", num_type_mismatches);
println!("Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());

View File

@ -510,18 +510,6 @@ pub enum DefWithBody {
impl_froms!(DefWithBody: Function, Const, Static);
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.
pub(crate) fn resolver(self, db: &impl HirDatabase) -> Resolver {
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)]
pub struct Function {
pub(crate) id: FunctionId,

View File

@ -1,9 +1,9 @@
use ra_syntax::ast;
use ra_syntax::ast::{self, AstNode};
use crate::{
ids::AstItemDef, AstDatabase, Const, DefDatabase, Enum, EnumVariant, FieldSource, Function,
HirFileId, MacroDef, Module, ModuleSource, Static, Struct, StructField, Trait, TypeAlias,
Union,
HasBody, HirDatabase, HirFileId, MacroDef, Module, ModuleSource, Static, Struct, StructField,
Trait, TypeAlias, Union,
};
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) }
}
}
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,
{
}

View File

@ -75,8 +75,8 @@ pub use self::{
pub use self::code_model::{
docs::{DocDef, Docs, Documentation},
src::{HasSource, Source},
src::{HasBodySource, HasSource, Source},
BuiltinType, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
EnumVariant, FieldSource, FnData, Function, MacroDef, Module, ModuleDef, ModuleSource, Static,
Struct, StructField, Trait, TypeAlias, Union,
EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef, ModuleSource,
Static, Struct, StructField, Trait, TypeAlias, Union,
};

View File

@ -27,9 +27,9 @@ use crate::{
name,
path::{PathKind, PathSegment},
ty::method_resolution::implements_trait,
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId,
MacroDef, Module, ModuleDef, Name, Path, PerNs, Resolution, Resolver, Static, Struct, Trait,
Ty,
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HasBody, HirDatabase,
HirFileId, MacroDef, Module, ModuleDef, Name, Path, PerNs, Resolution, Resolver, Static,
Struct, Trait, Ty,
};
/// Locates the module by `FileId`. Picks topmost module in the file.

View File

@ -50,8 +50,8 @@ use crate::{
},
ty::infer::diagnostics::InferenceDiagnostic,
type_ref::{Mutability, TypeRef},
AdtDef, ConstData, DefWithBody, FnData, Function, HirDatabase, ImplItem, ModuleDef, Name, Path,
StructField,
AdtDef, ConstData, DefWithBody, FnData, Function, HasBody, HirDatabase, ImplItem, ModuleDef,
Name, Path, StructField,
};
mod unify;

View File

@ -15,9 +15,8 @@ impl SyntaxNodePtr {
SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
}
pub fn to_node(self, root: &SyntaxNode) -> SyntaxNode {
assert!(root.parent().is_none());
successors(Some(root.clone()), |node| {
pub fn to_node(self, parent: &SyntaxNode) -> SyntaxNode {
successors(Some(parent.clone()), |node| {
node.children().find(|it| self.range.is_subrange(&it.text_range()))
})
.find(|it| it.text_range() == self.range && it.kind() == self.kind)