|
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,
|
@@ -1724,7 +1448,6 @@ impl LintPass for SoftLints {
|
1724 | 1448 | MISSING_DEBUG_IMPLEMENTATIONS,
|
1725 | 1449 | ANONYMOUS_PARAMETERS,
|
1726 | 1450 | UNUSED_DOC_COMMENTS,
|
1727 |
| - UNCONDITIONAL_RECURSION, |
1728 | 1451 | PLUGIN_AS_LIBRARY,
|
1729 | 1452 | NO_MANGLE_CONST_ITEMS,
|
1730 | 1453 | NO_MANGLE_GENERIC_ITEMS,
|
|
0 commit comments