compare-method lint

This commit is contained in:
Niko Matsakis 2016-10-12 16:38:58 -04:00
parent f652651275
commit ddabd509a8
24 changed files with 152 additions and 67 deletions

View file

@ -646,13 +646,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
};
if let SubregionOrigin::CompareImplMethodObligation {
span, item_name, impl_item_def_id, trait_item_def_id
span, item_name, impl_item_def_id, trait_item_def_id, lint_id
} = origin {
self.report_extra_impl_obligation(span,
item_name,
impl_item_def_id,
trait_item_def_id,
&format!("`{}: {}`", bound_kind, sub))
&format!("`{}: {}`", bound_kind, sub),
lint_id)
.emit();
return;
}
@ -977,12 +978,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
infer::CompareImplMethodObligation { span,
item_name,
impl_item_def_id,
trait_item_def_id } => {
trait_item_def_id,
lint_id } => {
self.report_extra_impl_obligation(span,
item_name,
impl_item_def_id,
trait_item_def_id,
&format!("`{}: {}`", sup, sub))
&format!("`{}: {}`", sup, sub),
lint_id)
}
}
}

View file

@ -368,6 +368,10 @@ pub enum SubregionOrigin<'tcx> {
item_name: ast::Name,
impl_item_def_id: DefId,
trait_item_def_id: DefId,
// this is `Some(_)` if this error arises from the bug fix for
// #18937. This is a temporary measure.
lint_id: Option<ast::NodeId>,
},
}
@ -1816,12 +1820,14 @@ impl<'tcx> SubregionOrigin<'tcx> {
traits::ObligationCauseCode::CompareImplMethodObligation { item_name,
impl_item_def_id,
trait_item_def_id } =>
trait_item_def_id,
lint_id } =>
SubregionOrigin::CompareImplMethodObligation {
span: cause.span,
item_name: item_name,
impl_item_def_id: impl_item_def_id,
trait_item_def_id: trait_item_def_id,
lint_id: lint_id,
},
_ => default(),

View file

@ -198,6 +198,12 @@ declare_lint! {
"patterns in functions without body were erroneously allowed"
}
declare_lint! {
pub EXTRA_REQUIREMENT_IN_IMPL,
Warn,
"detects extra requirements in impls that were erroneously allowed"
}
/// Does nothing as a lint pass, but registers some `Lint`s
/// which are used by other parts of the compiler.
#[derive(Copy, Clone)]
@ -235,7 +241,8 @@ impl LintPass for HardwiredLints {
HR_LIFETIME_IN_ASSOC_TYPE,
LIFETIME_UNDERSCORE,
SAFE_EXTERN_STATICS,
PATTERNS_IN_FNS_WITHOUT_BODY
PATTERNS_IN_FNS_WITHOUT_BODY,
EXTRA_REQUIREMENT_IN_IMPL
)
}
}

View file

@ -42,7 +42,7 @@ use std::fmt;
use syntax::attr;
use syntax::parse::token::InternedString;
use syntax::ast;
use syntax_pos::Span;
use syntax_pos::{MultiSpan, Span};
use errors::{self, Diagnostic, DiagnosticBuilder};
use hir;
use hir::intravisit as hir_visit;
@ -107,9 +107,12 @@ impl fmt::Debug for EarlyLint {
}
impl EarlyLint {
pub fn new(id: LintId, span: Span, msg: String) -> Self {
let mut diagnostic = Diagnostic::new(errors::Level::Warning, &msg);
diagnostic.set_span(span);
pub fn new<M: EarlyLintMessage>(id: LintId, span: Span, msg: M) -> Self {
let diagnostic = msg.into_diagnostic(span);
EarlyLint { id: id, span: span, diagnostic: diagnostic }
}
pub fn with_diagnostic(id: LintId, span: Span, diagnostic: Diagnostic) -> Self {
EarlyLint { id: id, span: span, diagnostic: diagnostic }
}
@ -120,7 +123,23 @@ impl EarlyLint {
}
}
pub trait EarlyLintMessage {
fn into_diagnostic(self, span: Span) -> Diagnostic;
}
impl EarlyLintMessage for String {
fn into_diagnostic(self, span: Span) -> Diagnostic {
let mut diagnostic = Diagnostic::new(errors::Level::Warning, &self);
diagnostic.set_span(span);
diagnostic
}
}
impl EarlyLintMessage for Diagnostic {
fn into_diagnostic(self, _span: Span) -> Diagnostic {
self
}
}
/// Extra information for a future incompatibility lint. See the call
/// to `register_future_incompatible` in `librustc_lint/lib.rs` for
@ -439,13 +458,15 @@ pub fn raw_emit_lint(sess: &Session,
raw_struct_lint(sess, lints, lint, lvlsrc, span, msg).emit();
}
pub fn raw_struct_lint<'a>(sess: &'a Session,
lints: &LintStore,
lint: &'static Lint,
lvlsrc: LevelSource,
span: Option<Span>,
msg: &str)
-> DiagnosticBuilder<'a> {
pub fn raw_struct_lint<'a, S>(sess: &'a Session,
lints: &LintStore,
lint: &'static Lint,
lvlsrc: LevelSource,
span: Option<S>,
msg: &str)
-> DiagnosticBuilder<'a>
where S: Into<MultiSpan>
{
let (mut level, source) = lvlsrc;
if level == Allow {
return sess.diagnostic().struct_dummy();

View file

@ -41,7 +41,7 @@ use hir;
pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore,
raw_emit_lint, check_crate, check_ast_crate, gather_attrs,
raw_struct_lint, FutureIncompatibleInfo, EarlyLint};
raw_struct_lint, FutureIncompatibleInfo, EarlyLint, EarlyLintMessage};
/// Specification of a single lint.
#[derive(Copy, Clone, Debug)]

View file

@ -258,11 +258,11 @@ impl Session {
pub fn unimpl(&self, msg: &str) -> ! {
self.diagnostic().unimpl(msg)
}
pub fn add_lint(&self,
lint: &'static lint::Lint,
id: ast::NodeId,
sp: Span,
msg: String) {
pub fn add_lint<M: lint::EarlyLintMessage>(&self,
lint: &'static lint::Lint,
id: ast::NodeId,
sp: Span,
msg: M) {
let lint_id = lint::LintId::of(lint);
let mut lints = self.lints.borrow_mut();
let early_lint = lint::EarlyLint::new(lint_id, sp, msg);

View file

@ -27,6 +27,7 @@ use super::{
use fmt_macros::{Parser, Piece, Position};
use hir::def_id::DefId;
use infer::{self, InferCtxt, TypeOrigin};
use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
use ty::error::ExpectedFound;
use ty::fast_reject;
@ -423,9 +424,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
item_name: ast::Name,
_impl_item_def_id: DefId,
trait_item_def_id: DefId,
requirement: &fmt::Display)
requirement: &fmt::Display,
lint_id: Option<ast::NodeId>) // (*)
-> DiagnosticBuilder<'tcx>
{
// (*) This parameter is temporary and used only for phasing
// in the bug fix to #18937. If it is `Some`, it has a kind of
// weird effect -- the diagnostic is reported as a lint, and
// the builder which is returned is marked as canceled.
let mut err =
struct_span_err!(self.tcx.sess,
error_span,
@ -441,6 +448,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
error_span,
&format!("impl has extra requirement {}", requirement));
if let Some(node_id) = lint_id {
let diagnostic = (*err).clone();
self.tcx.sess.add_lint(EXTRA_REQUIREMENT_IN_IMPL, node_id, error_span, diagnostic);
err.cancel();
}
err
}
@ -452,14 +465,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let mut err = match *error {
SelectionError::Unimplemented => {
if let ObligationCauseCode::CompareImplMethodObligation {
item_name, impl_item_def_id, trait_item_def_id
item_name, impl_item_def_id, trait_item_def_id, lint_id
} = obligation.cause.code {
self.report_extra_impl_obligation(
span,
item_name,
impl_item_def_id,
trait_item_def_id,
&format!("`{}`", obligation.predicate))
&format!("`{}`", obligation.predicate),
lint_id)
.emit();
return;
} else {

View file

@ -138,10 +138,12 @@ pub enum ObligationCauseCode<'tcx> {
ImplDerivedObligation(DerivedObligationCause<'tcx>),
// error derived when matching traits/impls; see ObligationCause for more details
CompareImplMethodObligation {
item_name: ast::Name,
impl_item_def_id: DefId,
trait_item_def_id: DefId
trait_item_def_id: DefId,
lint_id: Option<ast::NodeId>,
},
}

View file

@ -197,11 +197,13 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
}
super::CompareImplMethodObligation { item_name,
impl_item_def_id,
trait_item_def_id } => {
trait_item_def_id,
lint_id } => {
Some(super::CompareImplMethodObligation {
item_name: item_name,
impl_item_def_id: impl_item_def_id,
trait_item_def_id: trait_item_def_id,
lint_id: lint_id,
})
}
}