1734: Strip indents and empty lines in check_apply_diagnostic_fix_from_position r=matklad a=matklad



Co-authored-by: Phil Ellison <phil.j.ellison@gmail.com>
This commit is contained in:
bors[bot] 2019-08-25 09:58:54 +00:00 committed by GitHub
commit cd433ed194
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 319 additions and 5 deletions

View file

@ -143,3 +143,31 @@ impl AstDiagnostic for MissingFields {
ast::RecordFieldList::cast(node).unwrap()
}
}
#[derive(Debug)]
pub struct MissingOkInTailExpr {
pub file: HirFileId,
pub expr: AstPtr<ast::Expr>,
}
impl Diagnostic for MissingOkInTailExpr {
fn message(&self) -> String {
"wrap return expression in Ok".to_string()
}
fn source(&self) -> Source<SyntaxNodePtr> {
Source { file_id: self.file, ast: self.expr.into() }
}
fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
}
impl AstDiagnostic for MissingOkInTailExpr {
type AST = ast::Expr;
fn ast(&self, db: &impl HirDatabase) -> Self::AST {
let root = db.parse_or_expand(self.file).unwrap();
let node = self.source().ast.to_node(&root);
ast::Expr::cast(node).unwrap()
}
}

View file

@ -6,11 +6,14 @@ use ra_syntax::ast::{AstNode, RecordLit};
use super::{Expr, ExprId, RecordLitField};
use crate::{
adt::AdtDef,
diagnostics::{DiagnosticSink, MissingFields},
diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr},
expr::AstPtr,
ty::InferenceResult,
Function, HasSource, HirDatabase, Name, Path,
name,
path::{PathKind, PathSegment},
ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
Function, HasSource, HirDatabase, ModuleDef, Name, Path, PerNs, Resolution,
};
use ra_syntax::ast;
pub(crate) struct ExprValidator<'a, 'b: 'a> {
func: Function,
@ -29,11 +32,17 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) {
let body = self.func.body(db);
for e in body.exprs() {
if let (id, Expr::RecordLit { path, fields, spread }) = e {
self.validate_record_literal(id, path, fields, *spread, db);
}
}
let body_expr = &body[body.body_expr()];
if let Expr::Block { statements: _, tail: Some(t) } = body_expr {
self.validate_results_in_tail_expr(*t, db);
}
}
fn validate_record_literal(
@ -87,4 +96,42 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
})
}
}
fn validate_results_in_tail_expr(&mut self, id: ExprId, db: &impl HirDatabase) {
let mismatch = match self.infer.type_mismatch_for_expr(id) {
Some(m) => m,
None => return,
};
let std_result_path = Path {
kind: PathKind::Abs,
segments: vec![
PathSegment { name: name::STD, args_and_bindings: None },
PathSegment { name: name::RESULT_MOD, args_and_bindings: None },
PathSegment { name: name::RESULT_TYPE, args_and_bindings: None },
],
};
let resolver = self.func.resolver(db);
let std_result_enum =
match resolver.resolve_path_segments(db, &std_result_path).into_fully_resolved() {
PerNs { types: Some(Resolution::Def(ModuleDef::Enum(e))), .. } => e,
_ => return,
};
let std_result_ctor = TypeCtor::Adt(AdtDef::Enum(std_result_enum));
let params = match &mismatch.expected {
Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters,
_ => return,
};
if params.len() == 2 && &params[0] == &mismatch.actual {
let source_map = self.func.body_source_map(db);
let file_id = self.func.source(db).file_id;
if let Some(expr) = source_map.expr_syntax(id).and_then(|n| n.cast::<ast::Expr>()) {
self.sink.push(MissingOkInTailExpr { file: file_id, expr });
}
}
}
}

View file

@ -120,6 +120,8 @@ pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try")
pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future"));
pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future"));
pub(crate) const RESULT_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result"));
pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result"));
pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
fn resolve_name(text: &SmolStr) -> SmolStr {

View file

@ -106,6 +106,13 @@ impl Default for BindingMode {
}
}
/// A mismatch between an expected and an inferred type.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TypeMismatch {
pub expected: Ty,
pub actual: Ty,
}
/// The result of type inference: A mapping from expressions and patterns to types.
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct InferenceResult {
@ -120,6 +127,7 @@ pub struct InferenceResult {
diagnostics: Vec<InferenceDiagnostic>,
pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
pub(super) type_of_pat: ArenaMap<PatId, Ty>,
pub(super) type_mismatches: ArenaMap<ExprId, TypeMismatch>,
}
impl InferenceResult {
@ -141,6 +149,9 @@ impl InferenceResult {
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<ImplItem> {
self.assoc_resolutions.get(&id.into()).copied()
}
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
self.type_mismatches.get(expr)
}
pub(crate) fn add_diagnostics(
&self,
db: &impl HirDatabase,
@ -1345,9 +1356,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
};
// use a new type variable if we got Ty::Unknown here
let ty = self.insert_type_vars_shallow(ty);
self.unify(&ty, &expected.ty);
let could_unify = self.unify(&ty, &expected.ty);
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
self.write_expr_ty(tgt_expr, ty.clone());
if !could_unify {
self.result.type_mismatches.insert(
tgt_expr,
TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
);
}
ty
}