auto merge of #10990 : ktt3ja/rust/method-stability, r=huonw

If it's a trait method, this checks the stability attribute of the
method inside the trait definition. Otherwise, it checks the method
implementation itself.

Close #8961.
This commit is contained in:
bors 2013-12-17 02:31:55 -08:00
commit dc65762d79
5 changed files with 173 additions and 55 deletions

View file

@ -312,7 +312,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
&exported_items, reachable_map, crate));
time(time_passes, "lint checking", (), |_|
lint::check_crate(ty_cx, &exported_items, crate));
lint::check_crate(ty_cx, method_map, &exported_items, crate));
CrateAnalysis {
exp_map2: exp_map2,

View file

@ -812,6 +812,10 @@ fn encode_info_for_method(ecx: &EncodeContext,
encode_bounds_and_type(ebml_w, ecx, &tpt);
encode_path(ecx, ebml_w, impl_path, ast_map::path_name(m.ident));
match ast_method_opt {
Some(ast_method) => encode_attributes(ebml_w, ast_method.attrs),
None => ()
}
for ast_method in ast_method_opt.iter() {
let num_params = tpt.generics.type_param_defs.len();
@ -1205,11 +1209,13 @@ fn encode_info_for_item(ecx: &EncodeContext,
}
match ms[i] {
required(_) => {
required(ref tm) => {
encode_attributes(ebml_w, tm.attrs);
encode_method_sort(ebml_w, 'r');
}
provided(m) => {
encode_attributes(ebml_w, m.attrs);
// If this is a static method, we've already encoded
// this.
if method_ty.explicit_self != sty_static {

View file

@ -37,6 +37,7 @@ use driver::session;
use middle::privacy;
use middle::trans::adt; // for `adt::is_ffi_safe`
use middle::ty;
use middle::typeck;
use middle::pat_util;
use metadata::csearch;
use util::ppaux::{ty_to_str};
@ -359,6 +360,9 @@ struct Context<'a> {
cur: SmallIntMap<(level, LintSource)>,
// context we're checking in (used to access fields like sess)
tcx: ty::ctxt,
// maps from an expression id that corresponds to a method call to the
// details of the method to be invoked
method_map: typeck::method_map,
// Items exported by the crate; used by the missing_doc lint.
exported_items: &'a privacy::ExportedItems,
// The id of the current `ast::struct_def` being walked.
@ -1176,20 +1180,43 @@ fn check_missing_doc_variant(cx: &Context, v: &ast::variant) {
/// Checks for use of items with #[deprecated], #[experimental] and
/// #[unstable] (or none of them) attributes.
fn check_stability(cx: &Context, e: &ast::Expr) {
let def = match e.node {
ast::ExprMethodCall(..) |
ast::ExprPath(..) |
ast::ExprStruct(..) => {
let id = match e.node {
ast::ExprPath(..) | ast::ExprStruct(..) => {
match cx.tcx.def_map.find(&e.id) {
Some(&def) => def,
Some(&def) => ast_util::def_id_of_def(def),
None => return
}
}
ast::ExprMethodCall(..) => {
match cx.method_map.find(&e.id) {
Some(&typeck::method_map_entry { origin, .. }) => {
match origin {
typeck::method_static(def_id) => {
// If this implements a trait method, get def_id
// of the method inside trait definition.
// Otherwise, use the current def_id (which refers
// to the method inside impl).
ty::trait_method_of_method(
cx.tcx, def_id).unwrap_or(def_id)
}
typeck::method_param(typeck::method_param {
trait_id: trait_id,
method_num: index,
..
})
| typeck::method_object(typeck::method_object {
trait_id: trait_id,
method_num: index,
..
}) => ty::trait_method(cx.tcx, trait_id, index).def_id
}
}
None => return
}
}
_ => return
};
let id = ast_util::def_id_of_def(def);
let stability = if ast_util::is_local(id) {
// this crate
match cx.tcx.items.find(&id.node) {
@ -1208,7 +1235,8 @@ fn check_stability(cx: &Context, e: &ast::Expr) {
None => return
}
}
_ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
_ => cx.tcx.sess.span_bug(e.span,
format!("handle_def: {:?} not found", id))
}
} else {
// cross-crate
@ -1395,12 +1423,14 @@ impl<'a> IdVisitingOperation for Context<'a> {
}
pub fn check_crate(tcx: ty::ctxt,
method_map: typeck::method_map,
exported_items: &privacy::ExportedItems,
crate: &ast::Crate) {
let mut cx = Context {
dict: @get_lint_dict(),
cur: SmallIntMap::new(),
tcx: tcx,
method_map: method_map,
exported_items: exported_items,
cur_struct_def_id: -1,
is_doc_hidden: false,

View file

@ -4540,29 +4540,67 @@ pub fn populate_implementations_for_trait_if_necessary(
tcx.populated_external_traits.insert(trait_id);
}
/// If the given def ID describes a trait method, returns the ID of the trait
/// that the method belongs to. Otherwise, returns `None`.
/// Given the def_id of an impl, return the def_id of the trait it implements.
/// If it implements no trait, return `None`.
pub fn trait_id_of_impl(tcx: ctxt,
def_id: ast::DefId) -> Option<ast::DefId> {
let node = match tcx.items.find(&def_id.node) {
Some(node) => node,
None => return None
};
match node {
&ast_map::node_item(item, _) => {
match item.node {
ast::item_impl(_, Some(ref trait_ref), _, _) => {
Some(node_id_to_trait_ref(tcx, trait_ref.ref_id).def_id)
}
_ => None
}
}
_ => None
}
}
/// If the given def ID describes a method belonging to a trait (either a
/// default method or an implementation of a trait method), return the ID of
/// the trait that the method belongs to. Otherwise, return `None`.
pub fn trait_of_method(tcx: ctxt, def_id: ast::DefId)
-> Option<ast::DefId> {
if def_id.crate != LOCAL_CRATE {
return csearch::get_trait_of_method(tcx.cstore, def_id, tcx);
}
match tcx.methods.find(&def_id) {
Some(method_descriptor) => {
match method_descriptor.container {
TraitContainer(id) => return Some(id),
_ => {}
Some(method) => {
match method.container {
TraitContainer(def_id) => Some(def_id),
ImplContainer(def_id) => trait_id_of_impl(tcx, def_id),
}
}
None => {}
None => None
}
}
// If the method was in the local crate, then if we got here we know the
// answer is negative.
if def_id.crate == LOCAL_CRATE {
return None
/// If the given def ID describes a method belonging to a trait, (either a
/// default method or an implementation of a trait method), return the ID of
/// the method inside trait definition (this means that if the given def ID
/// is already that of the original trait method, then the return value is
/// the same).
/// Otherwise, return `None`.
pub fn trait_method_of_method(tcx: ctxt,
def_id: ast::DefId) -> Option<ast::DefId> {
let name = match tcx.methods.find(&def_id) {
Some(method) => method.ident.name,
None => return None
};
match trait_of_method(tcx, def_id) {
Some(trait_did) => {
let trait_methods = ty::trait_methods(tcx, trait_did);
trait_methods.iter()
.position(|m| m.ident.name == name)
.map(|idx| ty::trait_method(tcx, trait_did, idx).def_id)
}
None => None
}
let result = csearch::get_trait_of_method(tcx.cstore, def_id, tcx);
result
}
/// Creates a hash of the type `t` which will be the same no matter what crate

View file

@ -102,6 +102,28 @@ mod cross_crate {
let _ = FrozenVariant;
let _ = LockedVariant;
}
fn test_method_param<F: Trait>(foo: F) {
foo.trait_deprecated(); //~ ERROR use of deprecated item
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
foo.trait_experimental(); //~ ERROR use of experimental item
foo.trait_experimental_text(); //~ ERROR use of experimental item: text
foo.trait_unstable(); //~ ERROR use of unstable item
foo.trait_unstable_text(); //~ ERROR use of unstable item: text
foo.trait_unmarked(); //~ ERROR use of unmarked item
foo.trait_stable();
}
fn test_method_object(foo: &Trait) {
foo.trait_deprecated(); //~ ERROR use of deprecated item
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
foo.trait_experimental(); //~ ERROR use of experimental item
foo.trait_experimental_text(); //~ ERROR use of experimental item: text
foo.trait_unstable(); //~ ERROR use of unstable item
foo.trait_unstable_text(); //~ ERROR use of unstable item: text
foo.trait_unmarked(); //~ ERROR use of unmarked item
foo.trait_stable();
}
}
mod this_crate {
@ -335,6 +357,28 @@ mod this_crate {
let _ = FrozenVariant;
let _ = LockedVariant;
}
fn test_method_param<F: Trait>(foo: F) {
foo.trait_deprecated(); //~ ERROR use of deprecated item
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
foo.trait_experimental(); //~ ERROR use of experimental item
foo.trait_experimental_text(); //~ ERROR use of experimental item: text
foo.trait_unstable(); //~ ERROR use of unstable item
foo.trait_unstable_text(); //~ ERROR use of unstable item: text
foo.trait_unmarked(); //~ ERROR use of unmarked item
foo.trait_stable();
}
fn test_method_object(foo: &Trait) {
foo.trait_deprecated(); //~ ERROR use of deprecated item
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
foo.trait_experimental(); //~ ERROR use of experimental item
foo.trait_experimental_text(); //~ ERROR use of experimental item: text
foo.trait_unstable(); //~ ERROR use of unstable item
foo.trait_unstable_text(); //~ ERROR use of unstable item: text
foo.trait_unmarked(); //~ ERROR use of unmarked item
foo.trait_stable();
}
}
fn main() {}