From: Ariel Ben-Yehuda Date: Wed, 30 Aug 2017 09:35:48 +0000 (+0300) Subject: move the on_unimplemented logic to its own file X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=cf07ebd2a2d148667d2fa79e6fc683ee3b4c7e48;p=rust.git move the on_unimplemented logic to its own file --- diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 8b1b11ff940..afd1d87f598 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -15,6 +15,7 @@ Obligation, ObligationCause, ObligationCauseCode, + OnUnimplementedInfo, OutputTypeParameterMismatch, TraitNotObjectSafe, PredicateObligation, @@ -25,7 +26,6 @@ }; use errors::DiagnosticBuilder; -use fmt_macros::{Parser, Piece, Position}; use hir; use hir::def_id::DefId; use infer::{self, InferCtxt}; @@ -39,148 +39,9 @@ use ty::fold::TypeFolder; use ty::subst::Subst; use ty::SubtypePredicate; -use util::common::ErrorReported; use util::nodemap::{FxHashMap, FxHashSet}; use syntax_pos::{DUMMY_SP, Span}; -use syntax_pos::symbol::InternedString; - -pub struct OnUnimplementedFormatString(InternedString); -pub struct OnUnimplementedInfo { - note: OnUnimplementedFormatString -} - -impl<'a, 'gcx, 'tcx> OnUnimplementedInfo { - pub fn of_item(tcx: TyCtxt<'a, 'gcx, 'tcx>, - trait_def_id: DefId, - impl_def_id: DefId, - span: Span) - -> Result, ErrorReported> - { - let attrs = tcx.get_attrs(impl_def_id); - - let attr = if let Some(item) = - attrs.into_iter().find(|a| a.check_name("rustc_on_unimplemented")) - { - item - } else { - return Ok(None); - }; - - let span = attr.span.substitute_dummy(span); - if let Some(label) = attr.value_str() { - Ok(Some(OnUnimplementedInfo { - note: OnUnimplementedFormatString::try_parse( - tcx, trait_def_id, label.as_str(), span)? - })) - } else { - struct_span_err!( - tcx.sess, span, E0232, - "this attribute must have a value") - .span_label(attr.span, "attribute requires a value") - .note(&format!("eg `#[rustc_on_unimplemented = \"foo\"]`")) - .emit(); - Err(ErrorReported) - } - } -} - -impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString { - pub fn try_parse(tcx: TyCtxt<'a, 'gcx, 'tcx>, - trait_def_id: DefId, - from: InternedString, - err_sp: Span) - -> Result - { - let result = OnUnimplementedFormatString(from); - result.verify(tcx, trait_def_id, err_sp)?; - Ok(result) - } - - fn verify(&self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - trait_def_id: DefId, - span: Span) - -> Result<(), ErrorReported> - { - let name = tcx.item_name(trait_def_id).as_str(); - let generics = tcx.generics_of(trait_def_id); - let parser = Parser::new(&self.0); - let types = &generics.types; - let mut result = Ok(()); - for token in parser { - match token { - Piece::String(_) => (), // Normal string, no need to check it - Piece::NextArgument(a) => match a.position { - // `{Self}` is allowed - Position::ArgumentNamed(s) if s == "Self" => (), - // `{ThisTraitsName}` is allowed - Position::ArgumentNamed(s) if s == name => (), - // So is `{A}` if A is a type parameter - Position::ArgumentNamed(s) => match types.iter().find(|t| { - t.name == s - }) { - Some(_) => (), - None => { - span_err!(tcx.sess, span, E0230, - "there is no type parameter \ - {} on trait {}", - s, name); - result = Err(ErrorReported); - } - }, - // `{:1}` and `{}` are not to be used - Position::ArgumentIs(_) => { - span_err!(tcx.sess, span, E0231, - "only named substitution \ - parameters are allowed"); - result = Err(ErrorReported); - } - } - } - } - - result - } - - fn format(&self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - trait_ref: ty::TraitRef<'tcx>) - -> String - { - let name = tcx.item_name(trait_ref.def_id).as_str(); - let trait_str = tcx.item_path_str(trait_ref.def_id); - let generics = tcx.generics_of(trait_ref.def_id); - let generic_map = generics.types.iter().map(|param| { - (param.name.as_str().to_string(), - trait_ref.substs.type_for_def(param).to_string()) - }).collect::>(); - - let parser = Parser::new(&self.0); - parser.map(|p| { - match p { - Piece::String(s) => s, - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => match generic_map.get(s) { - Some(val) => val, - None if s == name => { - &trait_str - } - None => { - bug!("broken on_unimplemented {:?} for {:?}: \ - no argument matching {:?}", - self.0, trait_ref, s) - } - }, - _ => { - bug!("broken on_unimplemented {:?} - bad \ - format arg", self.0) - } - } - } - }).collect() - } -} impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn report_fulfillment_errors(&self, @@ -465,7 +326,7 @@ fn on_unimplemented_note(&self, match OnUnimplementedInfo::of_item( self.tcx, trait_ref.def_id, def_id, obligation.cause.span ) { - Ok(Some(info)) => Some(info.note.format(self.tcx, *trait_ref)), + Ok(Some(info)) => Some(info.label.format(self.tcx, *trait_ref)), _ => None } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index cbc0389f48e..65dd4347564 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -31,13 +31,13 @@ pub use self::coherence::orphan_check; pub use self::coherence::overlapping_impls; pub use self::coherence::OrphanCheckErr; -pub use self::error_reporting::OnUnimplementedInfo; pub use self::fulfill::{FulfillmentContext, RegionObligation}; pub use self::project::MismatchedProjectionTypes; pub use self::project::{normalize, normalize_projection_type, Normalized}; pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal}; pub use self::object_safety::ObjectSafetyViolation; pub use self::object_safety::MethodViolationCode; +pub use self::on_unimplemented::OnUnimplementedInfo; pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; pub use self::specialize::{OverlapError, specialization_graph, translate_substs}; pub use self::specialize::{SpecializesCache, find_associated_item}; @@ -53,6 +53,7 @@ mod fulfill; mod project; mod object_safety; +mod on_unimplemented; mod select; mod specialize; mod structural_impls; diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs new file mode 100644 index 00000000000..5d46809d9b0 --- /dev/null +++ b/src/librustc/traits/on_unimplemented.rs @@ -0,0 +1,156 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fmt_macros::{Parser, Piece, Position}; + +use hir::def_id::DefId; +use ty::{self, TyCtxt}; +use util::common::ErrorReported; +use util::nodemap::FxHashMap; + +use syntax_pos::Span; +use syntax_pos::symbol::InternedString; + +pub struct OnUnimplementedFormatString(InternedString); +pub struct OnUnimplementedInfo { + pub label: OnUnimplementedFormatString +} + +impl<'a, 'gcx, 'tcx> OnUnimplementedInfo { + pub fn of_item(tcx: TyCtxt<'a, 'gcx, 'tcx>, + trait_def_id: DefId, + impl_def_id: DefId, + span: Span) + -> Result, ErrorReported> + { + let attrs = tcx.get_attrs(impl_def_id); + + let attr = if let Some(item) = + attrs.into_iter().find(|a| a.check_name("rustc_on_unimplemented")) + { + item + } else { + return Ok(None); + }; + + let span = attr.span.substitute_dummy(span); + if let Some(label) = attr.value_str() { + Ok(Some(OnUnimplementedInfo { + label: OnUnimplementedFormatString::try_parse( + tcx, trait_def_id, label.as_str(), span)? + })) + } else { + struct_span_err!( + tcx.sess, span, E0232, + "this attribute must have a value") + .span_label(attr.span, "attribute requires a value") + .note(&format!("eg `#[rustc_on_unimplemented = \"foo\"]`")) + .emit(); + Err(ErrorReported) + } + } +} + +impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString { + pub fn try_parse(tcx: TyCtxt<'a, 'gcx, 'tcx>, + trait_def_id: DefId, + from: InternedString, + err_sp: Span) + -> Result + { + let result = OnUnimplementedFormatString(from); + result.verify(tcx, trait_def_id, err_sp)?; + Ok(result) + } + + fn verify(&self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + trait_def_id: DefId, + span: Span) + -> Result<(), ErrorReported> + { + let name = tcx.item_name(trait_def_id).as_str(); + let generics = tcx.generics_of(trait_def_id); + let parser = Parser::new(&self.0); + let types = &generics.types; + let mut result = Ok(()); + for token in parser { + match token { + Piece::String(_) => (), // Normal string, no need to check it + Piece::NextArgument(a) => match a.position { + // `{Self}` is allowed + Position::ArgumentNamed(s) if s == "Self" => (), + // `{ThisTraitsName}` is allowed + Position::ArgumentNamed(s) if s == name => (), + // So is `{A}` if A is a type parameter + Position::ArgumentNamed(s) => match types.iter().find(|t| { + t.name == s + }) { + Some(_) => (), + None => { + span_err!(tcx.sess, span, E0230, + "there is no type parameter \ + {} on trait {}", + s, name); + result = Err(ErrorReported); + } + }, + // `{:1}` and `{}` are not to be used + Position::ArgumentIs(_) => { + span_err!(tcx.sess, span, E0231, + "only named substitution \ + parameters are allowed"); + result = Err(ErrorReported); + } + } + } + } + + result + } + + pub fn format(&self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + trait_ref: ty::TraitRef<'tcx>) + -> String + { + let name = tcx.item_name(trait_ref.def_id).as_str(); + let trait_str = tcx.item_path_str(trait_ref.def_id); + let generics = tcx.generics_of(trait_ref.def_id); + let generic_map = generics.types.iter().map(|param| { + (param.name.as_str().to_string(), + trait_ref.substs.type_for_def(param).to_string()) + }).collect::>(); + + let parser = Parser::new(&self.0); + parser.map(|p| { + match p { + Piece::String(s) => s, + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(s) => match generic_map.get(s) { + Some(val) => val, + None if s == name => { + &trait_str + } + None => { + bug!("broken on_unimplemented {:?} for {:?}: \ + no argument matching {:?}", + self.0, trait_ref, s) + } + }, + _ => { + bug!("broken on_unimplemented {:?} - bad \ + format arg", self.0) + } + } + } + }).collect() + } +}