Implement initial IMPL_TRAIT_OVERCAPTURES lint · rust-lang/rust@d57e57c (original) (raw)

``

1

`+

use rustc_data_structures::{fx::FxIndexSet, unord::UnordSet};

`

``

2

`+

use rustc_errors::LintDiagnostic;

`

``

3

`+

use rustc_hir as hir;

`

``

4

`+

use rustc_hir::def::DefKind;

`

``

5

`+

use rustc_hir::def_id::{DefId, LocalDefId};

`

``

6

`+

use rustc_hir::intravisit;

`

``

7

`+

use rustc_middle::ty::{

`

``

8

`+

self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,

`

``

9

`+

};

`

``

10

`+

use rustc_span::Span;

`

``

11

+

``

12

`+

use crate::fluent_generated as fluent;

`

``

13

`+

use crate::{LateContext, LateLintPass};

`

``

14

+

``

15

`+

declare_lint! {

`

``

16

`+

/// UwU

`

``

17

`+

pub IMPL_TRAIT_OVERCAPTURES,

`

``

18

`+

Warn,

`

``

19

`+

"will capture more lifetimes than possibly intended in edition 2024",

`

``

20

`+

@future_incompatible = FutureIncompatibleInfo {

`

``

21

`+

reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),

`

``

22

`+

reference: "https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html",

`

``

23

`+

};

`

``

24

`+

}

`

``

25

+

``

26

`+

declare_lint_pass!(

`

``

27

`` +

/// Lint for use of async fn in the definition of a publicly-reachable

``

``

28

`+

/// trait.

`

``

29

`+

ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES]

`

``

30

`+

);

`

``

31

+

``

32

`+

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

`

``

33

`+

fn check_fn(

`

``

34

`+

&mut self,

`

``

35

`+

cx: &LateContext<'tcx>,

`

``

36

`+

_: intravisit::FnKind<'tcx>,

`

``

37

`+

_: &'tcx hir::FnDecl<'tcx>,

`

``

38

`+

_: &'tcx hir::Body<'tcx>,

`

``

39

`+

_: Span,

`

``

40

`+

parent_def_id: LocalDefId,

`

``

41

`+

) {

`

``

42

`+

match cx.tcx.def_kind(parent_def_id) {

`

``

43

`+

DefKind::AssocFn => {

`

``

44

`+

// RPITITs already capture all lifetimes in scope, so skip them.

`

``

45

`+

if matches!(

`

``

46

`+

cx.tcx.def_kind(cx.tcx.local_parent(parent_def_id)),

`

``

47

`+

DefKind::Trait | DefKind::Impl { of_trait: true }

`

``

48

`+

) {

`

``

49

`+

return;

`

``

50

`+

}

`

``

51

`+

}

`

``

52

`+

DefKind::Fn => {

`

``

53

`+

// All freee functions need to check for overcaptures.

`

``

54

`+

}

`

``

55

`+

DefKind::Closure => return,

`

``

56

`+

kind => {

`

``

57

`+

unreachable!(

`

``

58

`+

"expected function item, found {}",

`

``

59

`+

kind.descr(parent_def_id.to_def_id())

`

``

60

`+

)

`

``

61

`+

}

`

``

62

`+

}

`

``

63

+

``

64

`+

let sig = cx.tcx.fn_sig(parent_def_id).instantiate_identity();

`

``

65

+

``

66

`+

let mut in_scope_parameters = FxIndexSet::default();

`

``

67

`+

let mut current_def_id = Some(parent_def_id.to_def_id());

`

``

68

`+

while let Some(def_id) = current_def_id {

`

``

69

`+

let generics = cx.tcx.generics_of(def_id);

`

``

70

`+

for param in &generics.params {

`

``

71

`+

in_scope_parameters.insert(param.def_id);

`

``

72

`+

}

`

``

73

`+

current_def_id = generics.parent;

`

``

74

`+

}

`

``

75

+

``

76

`+

sig.visit_with(&mut VisitOpaqueTypes {

`

``

77

`+

tcx: cx.tcx,

`

``

78

`+

parent_def_id,

`

``

79

`+

in_scope_parameters,

`

``

80

`+

seen: Default::default(),

`

``

81

`+

});

`

``

82

`+

}

`

``

83

`+

}

`

``

84

+

``

85

`+

struct VisitOpaqueTypes<'tcx> {

`

``

86

`+

tcx: TyCtxt<'tcx>,

`

``

87

`+

parent_def_id: LocalDefId,

`

``

88

`+

in_scope_parameters: FxIndexSet,

`

``

89

`+

seen: FxIndexSet,

`

``

90

`+

}

`

``

91

+

``

92

`+

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {

`

``

93

`+

fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(

`

``

94

`+

&mut self,

`

``

95

`+

t: &ty::Binder<'tcx, T>,

`

``

96

`+

) -> Self::Result {

`

``

97

`+

let mut added = vec![];

`

``

98

`+

for arg in t.bound_vars() {

`

``

99

`+

let arg: ty::BoundVariableKind = arg;

`

``

100

`+

match arg {

`

``

101

`+

ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..)) => {

`

``

102

`+

added.push(def_id);

`

``

103

`+

let unique = self.in_scope_parameters.insert(def_id);

`

``

104

`+

assert!(unique);

`

``

105

`+

}

`

``

106

`+

ty::BoundVariableKind::Ty(_) => {

`

``

107

`` +

todo!("we don't support late-bound type params in impl Trait")

``

``

108

`+

}

`

``

109

`+

ty::BoundVariableKind::Region(..) => {

`

``

110

`+

unreachable!("all AST-derived bound regions should have a name")

`

``

111

`+

}

`

``

112

`+

ty::BoundVariableKind::Const => {

`

``

113

`+

unreachable!("non-lifetime binder consts are not allowed")

`

``

114

`+

}

`

``

115

`+

}

`

``

116

`+

}

`

``

117

+

``

118

`+

t.super_visit_with(self);

`

``

119

+

``

120

`+

for arg in added.into_iter().rev() {

`

``

121

`+

self.in_scope_parameters.shift_remove(&arg);

`

``

122

`+

}

`

``

123

`+

}

`

``

124

+

``

125

`+

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {

`

``

126

`+

if !t.has_opaque_types() {

`

``

127

`+

return;

`

``

128

`+

}

`

``

129

+

``

130

`+

if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind()

`

``

131

`+

&& let Some(opaque_def_id) = opaque_ty.def_id.as_local()

`

``

132

`+

&& self.seen.insert(opaque_def_id)

`

``

133

`+

&& let opaque =

`

``

134

`+

self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty()

`

``

135

`+

&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin

`

``

136

`+

&& parent_def_id == self.parent_def_id

`

``

137

`+

&& opaque.precise_capturing_args.is_none()

`

``

138

`+

{

`

``

139

`+

let mut captured = UnordSet::default();

`

``

140

`+

let variances = self.tcx.variances_of(opaque_def_id);

`

``

141

`+

let mut current_def_id = Some(opaque_def_id.to_def_id());

`

``

142

`+

while let Some(def_id) = current_def_id {

`

``

143

`+

let generics = self.tcx.generics_of(def_id);

`

``

144

`+

for param in &generics.params {

`

``

145

`+

if variances[param.index as usize] != ty::Invariant {

`

``

146

`+

continue;

`

``

147

`+

}

`

``

148

`+

captured.insert(extract_def_id_from_arg(

`

``

149

`+

self.tcx,

`

``

150

`+

generics,

`

``

151

`+

opaque_ty.args[param.index as usize],

`

``

152

`+

));

`

``

153

`+

}

`

``

154

`+

current_def_id = generics.parent;

`

``

155

`+

}

`

``

156

+

``

157

`+

let uncaptured_spans: Vec<_> = self

`

``

158

`+

.in_scope_parameters

`

``

159

`+

.iter()

`

``

160

`+

.filter(|def_id| !captured.contains(def_id))

`

``

161

`+

.map(|def_id| self.tcx.def_span(def_id))

`

``

162

`+

.collect();

`

``

163

+

``

164

`+

if !uncaptured_spans.is_empty() {

`

``

165

`+

self.tcx.emit_node_lint(

`

``

166

`+

IMPL_TRAIT_OVERCAPTURES,

`

``

167

`+

self.tcx.local_def_id_to_hir_id(opaque_def_id),

`

``

168

`+

ImplTraitOvercapturesLint {

`

``

169

`+

opaque_span: self.tcx.def_span(opaque_def_id),

`

``

170

`+

self_ty: t,

`

``

171

`+

num_captured: uncaptured_spans.len(),

`

``

172

`+

uncaptured_spans,

`

``

173

`+

},

`

``

174

`+

);

`

``

175

`+

}

`

``

176

+

``

177

`+

for clause in

`

``

178

`+

self.tcx.item_bounds(opaque_ty.def_id).iter_instantiated(self.tcx, opaque_ty.args)

`

``

179

`+

{

`

``

180

`+

clause.visit_with(self)

`

``

181

`+

}

`

``

182

`+

}

`

``

183

+

``

184

`+

t.super_visit_with(self);

`

``

185

`+

}

`

``

186

`+

}

`

``

187

+

``

188

`+

struct ImplTraitOvercapturesLint<'tcx> {

`

``

189

`+

opaque_span: Span,

`

``

190

`+

uncaptured_spans: Vec,

`

``

191

`+

self_ty: Ty<'tcx>,

`

``

192

`+

num_captured: usize,

`

``

193

`+

}

`

``

194

+

``

195

`+

impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {

`

``

196

`+

fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {

`

``

197

`+

diag.arg("self_ty", self.self_ty.to_string())

`

``

198

`+

.arg("num_captured", self.num_captured)

`

``

199

`+

.span(self.opaque_span)

`

``

200

`+

.span_note(self.uncaptured_spans, fluent::lint_note)

`

``

201

`+

.note(fluent::lint_note2);

`

``

202

`+

}

`

``

203

+

``

204

`+

fn msg(&self) -> rustc_errors::DiagMessage {

`

``

205

`+

fluent::lint_impl_trait_overcaptures

`

``

206

`+

}

`

``

207

`+

}

`

``

208

+

``

209

`+

fn extract_def_id_from_arg<'tcx>(

`

``

210

`+

tcx: TyCtxt<'tcx>,

`

``

211

`+

generics: &'tcx ty::Generics,

`

``

212

`+

arg: ty::GenericArg<'tcx>,

`

``

213

`+

) -> DefId {

`

``

214

`+

match arg.unpack() {

`

``

215

`+

ty::GenericArgKind::Lifetime(re) => match *re {

`

``

216

`+

ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,

`

``

217

`+

ty::ReBound(

`

``

218

`+

_,

`

``

219

`+

ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. },

`

``

220

`+

) => def_id,

`

``

221

`+

_ => unreachable!(),

`

``

222

`+

},

`

``

223

`+

ty::GenericArgKind::Type(ty) => {

`

``

224

`+

let ty::Param(param_ty) = *ty.kind() else {

`

``

225

`+

bug!();

`

``

226

`+

};

`

``

227

`+

generics.type_param(param_ty, tcx).def_id

`

``

228

`+

}

`

``

229

`+

ty::GenericArgKind::Const(ct) => {

`

``

230

`+

let ty::ConstKind::Param(param_ct) = ct.kind() else {

`

``

231

`+

bug!();

`

``

232

`+

};

`

``

233

`+

generics.const_param(param_ct, tcx).def_id

`

``

234

`+

}

`

``

235

`+

}

`

``

236

`+

}

`