Auto merge of #118391 - compiler-errors:lifetimes-eq, r= · rust-lang/rust@7a2c7d7 (original) (raw)

``

1

`+

#![allow(rustc::diagnostic_outside_of_impl)]

`

``

2

`+

#![allow(rustc::untranslatable_diagnostic)]

`

``

3

+

``

4

`+

use rustc_data_structures::fx::FxHashSet;

`

``

5

`+

use rustc_hir as hir;

`

``

6

`+

use rustc_hir::def::DefKind;

`

``

7

`+

use rustc_infer::infer::outlives::env::OutlivesEnvironment;

`

``

8

`+

use rustc_infer::infer::{SubregionOrigin, TyCtxtInferExt};

`

``

9

`+

use rustc_macros::LintDiagnostic;

`

``

10

`+

use rustc_middle::ty::{self, TyCtxt};

`

``

11

`+

use rustc_session::lint::builtin::UNUSED_LIFETIMES;

`

``

12

`+

use rustc_span::DUMMY_SP;

`

``

13

`+

use rustc_trait_selection::traits::{outlives_bounds::InferCtxtExt, ObligationCtxt};

`

``

14

+

``

15

`+

use crate::{LateContext, LateLintPass};

`

``

16

+

``

17

`+

declare_lint_pass!(RedundantLifetimeArgs => []);

`

``

18

+

``

19

`+

impl<'tcx> LateLintPass<'tcx> for RedundantLifetimeArgs {

`

``

20

`+

fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {

`

``

21

`+

check(cx.tcx, cx.param_env, item.owner_id);

`

``

22

`+

}

`

``

23

+

``

24

`+

fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {

`

``

25

`+

check(cx.tcx, cx.param_env, item.owner_id);

`

``

26

`+

}

`

``

27

+

``

28

`+

fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {

`

``

29

`+

if cx

`

``

30

`+

.tcx

`

``

31

`+

.hir()

`

``

32

`+

.expect_item(cx.tcx.local_parent(item.owner_id.def_id))

`

``

33

`+

.expect_impl()

`

``

34

`+

.of_trait

`

``

35

`+

.is_some()

`

``

36

`+

{

`

``

37

`+

// Don't check for redundant lifetimes for trait implementations,

`

``

38

`+

// since the signature is required to be compatible with the trait.

`

``

39

`+

return;

`

``

40

`+

}

`

``

41

+

``

42

`+

check(cx.tcx, cx.param_env, item.owner_id);

`

``

43

`+

}

`

``

44

`+

}

`

``

45

+

``

46

`+

fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::OwnerId) {

`

``

47

`+

let def_kind = tcx.def_kind(owner_id);

`

``

48

`+

match def_kind {

`

``

49

`+

DefKind::Struct

`

``

50

`+

| DefKind::Union

`

``

51

`+

| DefKind::Enum

`

``

52

`+

| DefKind::Trait

`

``

53

`+

| DefKind::TraitAlias

`

``

54

`+

| DefKind::AssocTy

`

``

55

`+

| DefKind::Fn

`

``

56

`+

| DefKind::Const

`

``

57

`+

| DefKind::AssocFn

`

``

58

`+

| DefKind::AssocConst

`

``

59

`+

| DefKind::Impl { of_trait: _ } => {

`

``

60

`+

// Proceed

`

``

61

`+

}

`

``

62

`+

DefKind::Mod

`

``

63

`+

| DefKind::Variant

`

``

64

`+

| DefKind::TyAlias

`

``

65

`+

| DefKind::ForeignTy

`

``

66

`+

| DefKind::TyParam

`

``

67

`+

| DefKind::ConstParam

`

``

68

`+

| DefKind::Static(_)

`

``

69

`+

| DefKind::Ctor(_, _)

`

``

70

`+

| DefKind::Macro(_)

`

``

71

`+

| DefKind::ExternCrate

`

``

72

`+

| DefKind::Use

`

``

73

`+

| DefKind::ForeignMod

`

``

74

`+

| DefKind::AnonConst

`

``

75

`+

| DefKind::InlineConst

`

``

76

`+

| DefKind::OpaqueTy

`

``

77

`+

| DefKind::Field

`

``

78

`+

| DefKind::LifetimeParam

`

``

79

`+

| DefKind::GlobalAsm

`

``

80

`+

| DefKind::Closure => return,

`

``

81

`+

}

`

``

82

+

``

83

`+

let infcx = &tcx.infer_ctxt().build();

`

``

84

`+

let ocx = ObligationCtxt::new(infcx);

`

``

85

+

``

86

`+

// Compute the implied outlives bounds for the item. This ensures that we treat

`

``

87

`` +

// a signature with an argument like &'a &'b () as implicitly having 'b: 'a.

``

``

88

`+

let Ok(assumed_wf_types) = ocx.assumed_wf_types(param_env, owner_id.def_id) else {

`

``

89

`+

return;

`

``

90

`+

};

`

``

91

`+

let implied_bounds = infcx.implied_bounds_tys(param_env, owner_id.def_id, assumed_wf_types);

`

``

92

`+

let outlives_env = &OutlivesEnvironment::with_bounds(param_env, implied_bounds);

`

``

93

+

``

94

`+

// The ordering of this lifetime map is a bit subtle.

`

``

95

`+

//

`

``

96

`+

// Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,

`

``

97

`` +

// where we can prove that 'candidate = 'victim.

``

``

98

`+

//

`

``

99

`` +

// 'static must come first in this list because we can never replace 'static with

``

``

100

`` +

// something else, but if we find some lifetime 'a where 'a = 'static, we want to

``

``

101

`` +

// suggest replacing 'a with 'static.

``

``

102

`+

let mut lifetimes = vec![tcx.lifetimes.re_static];

`

``

103

`+

lifetimes.extend(

`

``

104

`+

ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()),

`

``

105

`+

);

`

``

106

`+

// If we are in a function, add its late-bound lifetimes too.

`

``

107

`+

if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {

`

``

108

`+

for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() {

`

``

109

`+

let ty::BoundVariableKind::Region(kind) = var else { continue };

`

``

110

`+

lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind));

`

``

111

`+

}

`

``

112

`+

}

`

``

113

+

``

114

`+

// Keep track of lifetimes which have already been replaced with other lifetimes.

`

``

115

`` +

// This makes sure that if 'a = 'b = 'c, we don't say 'c should be replaced by

``

``

116

`` +

// both 'a and 'b.

``

``

117

`+

let mut shadowed = FxHashSet::default();

`

``

118

+

``

119

`+

for (idx, &candidate) in lifetimes.iter().enumerate() {

`

``

120

`+

// Don't suggest removing a lifetime twice.

`

``

121

`+

if shadowed.contains(&candidate) {

`

``

122

`+

continue;

`

``

123

`+

}

`

``

124

+

``

125

`` +

// Can't rename a named lifetime named '_ without ambiguity.

``

``

126

`+

if !candidate.has_name() {

`

``

127

`+

continue;

`

``

128

`+

}

`

``

129

+

``

130

`+

for &victim in &lifetimes[(idx + 1)..] {

`

``

131

`+

// We only care about lifetimes that are "real", i.e. that have a def-id.

`

``

132

`+

let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })

`

``

133

`+

| ty::ReLateParam(ty::LateParamRegion {

`

``

134

`+

bound_region: ty::BoundRegionKind::BrNamed(def_id, _),

`

``

135

`+

..

`

``

136

`+

})) = victim.kind()

`

``

137

`+

else {

`

``

138

`+

continue;

`

``

139

`+

};

`

``

140

+

``

141

`+

// Do not rename lifetimes not local to this item since they'll overlap

`

``

142

`+

// with the lint running on the parent. We still want to consider parent

`

``

143

`+

// lifetimes which make child lifetimes redundant, otherwise we would

`

``

144

`` +

// have truncated the identity_for_item args above.

``

``

145

`+

if tcx.parent(def_id) != owner_id.to_def_id() {

`

``

146

`+

continue;

`

``

147

`+

}

`

``

148

+

``

149

`+

let infcx = infcx.fork();

`

``

150

+

``

151

`` +

// Require that 'candidate = 'victim

``

``

152

`+

infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), candidate, victim);

`

``

153

`+

infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), victim, candidate);

`

``

154

+

``

155

`` +

// If there are no lifetime errors, then we have proven that 'candidate = 'victim!

``

``

156

`+

if infcx.resolve_regions(outlives_env).is_empty() {

`

``

157

`+

shadowed.insert(victim);

`

``

158

`+

tcx.emit_spanned_lint(

`

``

159

`+

UNUSED_LIFETIMES,

`

``

160

`+

tcx.local_def_id_to_hir_id(def_id.expect_local()),

`

``

161

`+

tcx.def_span(def_id),

`

``

162

`+

RedundantLifetimeArgsLint { candidate, victim },

`

``

163

`+

);

`

``

164

`+

}

`

``

165

`+

}

`

``

166

`+

}

`

``

167

`+

}

`

``

168

+

``

169

`+

#[derive(LintDiagnostic)]

`

``

170

`+

#[diag(lint_redundant_lifetime_args)]

`

``

171

`+

#[note]

`

``

172

`+

struct RedundantLifetimeArgsLint<'tcx> {

`

``

173

`+

/// The lifetime we have found to be redundant.

`

``

174

`+

victim: ty::Region<'tcx>,

`

``

175

`+

// The lifetime we can replace the victim with.

`

``

176

`+

candidate: ty::Region<'tcx>,

`

``

177

`+

}

`