|
30 | 30 |
|
31 | 31 | use rustc::hir::def::Def; |
32 | 32 | use rustc::hir::def_id::DefId; |
33 | | -use rustc::cfg; |
34 | | -use rustc::ty::subst::Substs; |
35 | 33 | use rustc::ty::{self, Ty}; |
36 | | -use rustc::traits; |
37 | 34 | use hir::Node; |
38 | 35 | use util::nodemap::NodeSet; |
39 | 36 | use lint::{LateContext, LintContext, LintArray}; |
@@ -844,279 +841,6 @@ impl EarlyLintPass for UnusedDocComment { |
844 | 841 | } |
845 | 842 | } |
846 | 843 |
|
847 | | -declare_lint! { |
848 | | - pub UNCONDITIONAL_RECURSION, |
849 | | - Warn, |
850 | | - "functions that cannot return without calling themselves" |
851 | | -} |
852 | | - |
853 | | -#[derive(Copy, Clone)] |
854 | | -pub struct UnconditionalRecursion; |
855 | | - |
856 | | - |
857 | | -impl LintPass for UnconditionalRecursion { |
858 | | - fn get_lints(&self) -> LintArray { |
859 | | - lint_array![UNCONDITIONAL_RECURSION] |
860 | | - } |
861 | | -} |
862 | | - |
863 | | -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion { |
864 | | - fn check_fn(&mut self, |
865 | | - cx: &LateContext, |
866 | | - fn_kind: FnKind, |
867 | | - _: &hir::FnDecl, |
868 | | - body: &hir::Body, |
869 | | - sp: Span, |
870 | | - id: ast::NodeId) { |
871 | | - let method = match fn_kind { |
872 | | - FnKind::ItemFn(..) => None, |
873 | | - FnKind::Method(..) => { |
874 | | - Some(cx.tcx.associated_item(cx.tcx.hir.local_def_id(id))) |
875 | | - } |
876 | | - // closures can't recur, so they don't matter. |
877 | | - FnKind::Closure(_) => return, |
878 | | - }; |
879 | | - |
880 | | - // Walk through this function (say `f`) looking to see if |
881 | | - // every possible path references itself, i.e. the function is |
882 | | - // called recursively unconditionally. This is done by trying |
883 | | - // to find a path from the entry node to the exit node that |
884 | | - // *doesn't* call `f` by traversing from the entry while |
885 | | - // pretending that calls of `f` are sinks (i.e. ignoring any |
886 | | - // exit edges from them). |
887 | | - // |
888 | | - // NB. this has an edge case with non-returning statements, |
889 | | - // like `loop {}` or `panic!()`: control flow never reaches |
890 | | - // the exit node through these, so one can have a function |
891 | | - // that never actually calls itself but is still picked up by |
892 | | - // this lint: |
893 | | - // |
894 | | - // fn f(cond: bool) { |
895 | | - // if !cond { panic!() } // could come from `assert!(cond)` |
896 | | - // f(false) |
897 | | - // } |
898 | | - // |
899 | | - // In general, functions of that form may be able to call |
900 | | - // itself a finite number of times and then diverge. The lint |
901 | | - // considers this to be an error for two reasons, (a) it is |
902 | | - // easier to implement, and (b) it seems rare to actually want |
903 | | - // to have behaviour like the above, rather than |
904 | | - // e.g. accidentally recursing after an assert. |
905 | | - |
906 | | - let cfg = cfg::CFG::new(cx.tcx, &body); |
907 | | - |
908 | | - let mut work_queue = vec![cfg.entry]; |
909 | | - let mut reached_exit_without_self_call = false; |
910 | | - let mut self_call_spans = vec![]; |
911 | | - let mut visited = FxHashSet::default(); |
912 | | - |
913 | | - while let Some(idx) = work_queue.pop() { |
914 | | - if idx == cfg.exit { |
915 | | - // found a path! |
916 | | - reached_exit_without_self_call = true; |
917 | | - break; |
918 | | - } |
919 | | - |
920 | | - let cfg_id = idx.node_id(); |
921 | | - if visited.contains(&cfg_id) { |
922 | | - // already done |
923 | | - continue; |
924 | | - } |
925 | | - visited.insert(cfg_id); |
926 | | - |
927 | | - // is this a recursive call? |
928 | | - let local_id = cfg.graph.node_data(idx).id(); |
929 | | - if local_id != hir::DUMMY_ITEM_LOCAL_ID { |
930 | | - let node_id = cx.tcx.hir.hir_to_node_id(hir::HirId { |
931 | | - owner: body.value.hir_id.owner, |
932 | | - local_id |
933 | | - }); |
934 | | - let self_recursive = match method { |
935 | | - Some(ref method) => expr_refers_to_this_method(cx, method, node_id), |
936 | | - None => expr_refers_to_this_fn(cx, id, node_id), |
937 | | - }; |
938 | | - if self_recursive { |
939 | | - self_call_spans.push(cx.tcx.hir.span(node_id)); |
940 | | - // this is a self call, so we shouldn't explore past |
941 | | - // this node in the CFG. |
942 | | - continue; |
943 | | - } |
944 | | - } |
945 | | - |
946 | | - // add the successors of this node to explore the graph further. |
947 | | - for (_, edge) in cfg.graph.outgoing_edges(idx) { |
948 | | - let target_idx = edge.target(); |
949 | | - let target_cfg_id = target_idx.node_id(); |
950 | | - if !visited.contains(&target_cfg_id) { |
951 | | - work_queue.push(target_idx) |
952 | | - } |
953 | | - } |
954 | | - } |
955 | | - |
956 | | - // Check the number of self calls because a function that |
957 | | - // doesn't return (e.g. calls a `-> !` function or `loop { /* |
958 | | - // no break */ }`) shouldn't be linted unless it actually |
959 | | - // recurs. |
960 | | - if !reached_exit_without_self_call && !self_call_spans.is_empty() { |
961 | | - let sp = cx.tcx.sess.source_map().def_span(sp); |
962 | | - let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION, |
963 | | - sp, |
964 | | - "function cannot return without recursing"); |
965 | | - db.span_label(sp, "cannot return without recursing"); |
966 | | - // offer some help to the programmer. |
967 | | - for call in &self_call_spans { |
968 | | - db.span_label(*call, "recursive call site"); |
969 | | - } |
970 | | - db.help("a `loop` may express intention better if this is on purpose"); |
971 | | - db.emit(); |
972 | | - } |
973 | | - |
974 | | - // all done |
975 | | - return; |
976 | | - |
977 | | - // Functions for identifying if the given Expr NodeId `id` |
978 | | - // represents a call to the function `fn_id`/method `method`. |
979 | | - |
980 | | - fn expr_refers_to_this_fn(cx: &LateContext, fn_id: ast::NodeId, id: ast::NodeId) -> bool { |
981 | | - match cx.tcx.hir.get(id) { |
982 | | - Node::Expr(&hir::Expr { node: hir::ExprKind::Call(ref callee, _), .. }) => { |
983 | | - let def = if let hir::ExprKind::Path(ref qpath) = callee.node { |
984 | | - cx.tables.qpath_def(qpath, callee.hir_id) |
985 | | - } else { |
986 | | - return false; |
987 | | - }; |
988 | | - match def { |
989 | | - Def::Local(..) | Def::Upvar(..) => false, |
990 | | - _ => def.def_id() == cx.tcx.hir.local_def_id(fn_id) |
991 | | - } |
992 | | - } |
993 | | - _ => false, |
994 | | - } |
995 | | - } |
996 | | - |
997 | | - // Check if the expression `id` performs a call to `method`. |
998 | | - fn expr_refers_to_this_method(cx: &LateContext, |
999 | | - method: &ty::AssociatedItem, |
1000 | | - id: ast::NodeId) |
1001 | | - -> bool { |
1002 | | - use rustc::ty::adjustment::*; |
1003 | | - |
1004 | | - // Ignore non-expressions. |
1005 | | - let expr = if let Node::Expr(e) = cx.tcx.hir.get(id) { |
1006 | | - e |
1007 | | - } else { |
1008 | | - return false; |
1009 | | - }; |
1010 | | - |
1011 | | - // Check for overloaded autoderef method calls. |
1012 | | - let mut source = cx.tables.expr_ty(expr); |
1013 | | - for adjustment in cx.tables.expr_adjustments(expr) { |
1014 | | - if let Adjust::Deref(Some(deref)) = adjustment.kind { |
1015 | | - let (def_id, substs) = deref.method_call(cx.tcx, source); |
1016 | | - if method_call_refers_to_method(cx, method, def_id, substs, id) { |
1017 | | - return true; |
1018 | | - } |
1019 | | - } |
1020 | | - source = adjustment.target; |
1021 | | - } |
1022 | | - |
1023 | | - // Check for method calls and overloaded operators. |
1024 | | - if cx.tables.is_method_call(expr) { |
1025 | | - let hir_id = cx.tcx.hir.definitions().node_to_hir_id(id); |
1026 | | - if let Some(def) = cx.tables.type_dependent_defs().get(hir_id) { |
1027 | | - let def_id = def.def_id(); |
1028 | | - let substs = cx.tables.node_substs(hir_id); |
1029 | | - if method_call_refers_to_method(cx, method, def_id, substs, id) { |
1030 | | - return true; |
1031 | | - } |
1032 | | - } else { |
1033 | | - cx.tcx.sess.delay_span_bug(expr.span, |
1034 | | - "no type-dependent def for method call"); |
1035 | | - } |
1036 | | - } |
1037 | | - |
1038 | | - // Check for calls to methods via explicit paths (e.g. `T::method()`). |
1039 | | - match expr.node { |
1040 | | - hir::ExprKind::Call(ref callee, _) => { |
1041 | | - let def = if let hir::ExprKind::Path(ref qpath) = callee.node { |
1042 | | - cx.tables.qpath_def(qpath, callee.hir_id) |
1043 | | - } else { |
1044 | | - return false; |
1045 | | - }; |
1046 | | - match def { |
1047 | | - Def::Method(def_id) => { |
1048 | | - let substs = cx.tables.node_substs(callee.hir_id); |
1049 | | - method_call_refers_to_method(cx, method, def_id, substs, id) |
1050 | | - } |
1051 | | - _ => false, |
1052 | | - } |
1053 | | - } |
1054 | | - _ => false, |
1055 | | - } |
1056 | | - } |
1057 | | - |
1058 | | - // Check if the method call to the method with the ID `callee_id` |
1059 | | - // and instantiated with `callee_substs` refers to method `method`. |
1060 | | - fn method_call_refers_to_method<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, |
1061 | | - method: &ty::AssociatedItem, |
1062 | | - callee_id: DefId, |
1063 | | - callee_substs: &Substs<'tcx>, |
1064 | | - expr_id: ast::NodeId) |
1065 | | - -> bool { |
1066 | | - let tcx = cx.tcx; |
1067 | | - let callee_item = tcx.associated_item(callee_id); |
1068 | | - |
1069 | | - match callee_item.container { |
1070 | | - // This is an inherent method, so the `def_id` refers |
1071 | | - // directly to the method definition. |
1072 | | - ty::ImplContainer(_) => callee_id == method.def_id, |
1073 | | - |
1074 | | - // A trait method, from any number of possible sources. |
1075 | | - // Attempt to select a concrete impl before checking. |
1076 | | - ty::TraitContainer(trait_def_id) => { |
1077 | | - let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs); |
1078 | | - let trait_ref = ty::Binder::bind(trait_ref); |
1079 | | - let span = tcx.hir.span(expr_id); |
1080 | | - let obligation = |
1081 | | - traits::Obligation::new(traits::ObligationCause::misc(span, expr_id), |
1082 | | - cx.param_env, |
1083 | | - trait_ref.to_poly_trait_predicate()); |
1084 | | - |
1085 | | - tcx.infer_ctxt().enter(|infcx| { |
1086 | | - let mut selcx = traits::SelectionContext::new(&infcx); |
1087 | | - match selcx.select(&obligation) { |
1088 | | - // The method comes from a `T: Trait` bound. |
1089 | | - // If `T` is `Self`, then this call is inside |
1090 | | - // a default method definition. |
1091 | | - Ok(Some(traits::VtableParam(_))) => { |
1092 | | - let on_self = trait_ref.self_ty().is_self(); |
1093 | | - // We can only be recursing in a default |
1094 | | - // method if we're being called literally |
1095 | | - // on the `Self` type. |
1096 | | - on_self && callee_id == method.def_id |
1097 | | - } |
1098 | | - |
1099 | | - // The `impl` is known, so we check that with a |
1100 | | - // special case: |
1101 | | - Ok(Some(traits::VtableImpl(vtable_impl))) => { |
1102 | | - let container = ty::ImplContainer(vtable_impl.impl_def_id); |
1103 | | - // It matches if it comes from the same impl, |
1104 | | - // and has the same method name. |
1105 | | - container == method.container && |
1106 | | - callee_item.ident.name == method.ident.name |
1107 | | - } |
1108 | | - |
1109 | | - // There's no way to know if this call is |
1110 | | - // recursive, so we assume it's not. |
1111 | | - _ => false, |
1112 | | - } |
1113 | | - }) |
1114 | | - } |
1115 | | - } |
1116 | | - } |
1117 | | - } |
1118 | | -} |
1119 | | - |
1120 | 844 | declare_lint! { |
1121 | 845 | PLUGIN_AS_LIBRARY, |
1122 | 846 | Warn, |
@@ -1783,7 +1507,6 @@ impl LintPass for SoftLints { |
1783 | 1507 | MISSING_DEBUG_IMPLEMENTATIONS, |
1784 | 1508 | ANONYMOUS_PARAMETERS, |
1785 | 1509 | UNUSED_DOC_COMMENTS, |
1786 | | - UNCONDITIONAL_RECURSION, |
1787 | 1510 | PLUGIN_AS_LIBRARY, |
1788 | 1511 | PRIVATE_NO_MANGLE_FNS, |
1789 | 1512 | PRIVATE_NO_MANGLE_STATICS, |
|
0 commit comments