Auto merge of #122747 - Urgau:non-local-defs_perfect_impl, r= · rust-lang/rust@c5a16cd (original) (raw)

1

``

`-

use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind};

`

``

1

`+

use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, TyKind};

`

``

2

`+

use rustc_hir::{Path, QPath};

`

``

3

`+

use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};

`

``

4

`+

use rustc_infer::infer::InferCtxt;

`

``

5

`+

use rustc_infer::traits::{Obligation, ObligationCause};

`

``

6

`+

use rustc_middle::query::Key;

`

``

7

`+

use rustc_middle::ty::TypeSuperFoldable;

`

``

8

`+

use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable, TypeFolder};

`

2

9

`use rustc_span::def_id::{DefId, LOCAL_CRATE};

`

``

10

`+

use rustc_span::Span;

`

3

11

`use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind};

`

``

12

`+

use rustc_trait_selection::infer::TyCtxtInferExt;

`

``

13

`+

use rustc_trait_selection::traits::error_reporting::ambiguity::{

`

``

14

`+

compute_applicable_impls_for_diagnostics, Ambiguity,

`

``

15

`+

};

`

4

16

``

5

17

`use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};

`

6

18

`use crate::{LateContext, LateLintPass, LintContext};

`

`@@ -35,7 +47,7 @@ declare_lint! {

`

35

47

`/// All nested bodies (functions, enum discriminant, array length, consts) (expect for

`

36

48

`` /// const _: Ty = { ... } in top-level module, which is still undecided) are checked.

``

37

49

`pub NON_LOCAL_DEFINITIONS,

`

38

``

`-

Allow,

`

``

50

`+

Warn,

`

39

51

`"checks for non-local definitions",

`

40

52

` report_in_external_macro

`

41

53

`}

`

`@@ -66,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {

`

66

78

`return;

`

67

79

`}

`

68

80

``

69

``

`-

let parent = cx.tcx.parent(item.owner_id.def_id.into());

`

``

81

`+

let def_id = item.owner_id.def_id.into();

`

``

82

+

``

83

`+

let parent = cx.tcx.parent(def_id);

`

70

84

`let parent_def_kind = cx.tcx.def_kind(parent);

`

71

85

`let parent_opt_item_name = cx.tcx.opt_item_name(parent);

`

72

86

``

`@@ -155,9 +169,54 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {

`

155

169

`.map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent))

`

156

170

`.unwrap_or(false);

`

157

171

``

158

``

`-

// If none of them have a local parent (LOGICAL NOR) this means that

`

159

``

`-

// this impl definition is a non-local definition and so we lint on it.

`

160

``

`-

if !(self_ty_has_local_parent || of_trait_has_local_parent) {

`

``

172

`+

// Detecting if the impl definition is leaking outside of it's defining scope.

`

``

173

`+

//

`

``

174

`+

// Rule: for each impl, instantiate all local types with inference vars and

`

``

175

`+

// then assemble candidates for that goal, if there are more than 1 (non-private

`

``

176

`+

// impls), it does not leak.

`

``

177

`+

//

`

``

178

`+

// https://github.com/rust-lang/rust/issues/121621#issuecomment-1976826895

`

``

179

`+

let impl_is_not_leaky = cx

`

``

180

`+

.tcx

`

``

181

`+

.impl_trait_ref(def_id)

`

``

182

`+

.map(|binder| {

`

``

183

`+

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

`

``

184

`+

let trait_ref = binder

`

``

185

`+

.instantiate(cx.tcx, infcx.fresh_args_for_item(item.span, def_id));

`

``

186

+

``

187

`+

let trait_ref = trait_ref.fold_with(&mut LocalTypeInferenceFolder {

`

``

188

`+

infcx: &infcx,

`

``

189

`+

to_ignore: 1,

`

``

190

`+

impl_parent: parent,

`

``

191

`+

impl_parent_parent: parent_parent,

`

``

192

`+

span: item.span,

`

``

193

`+

});

`

``

194

+

``

195

`+

let poly_trait_obligation = Obligation::new(

`

``

196

`+

cx.tcx,

`

``

197

`+

ObligationCause::dummy(),

`

``

198

`+

ty::ParamEnv::empty(),

`

``

199

`+

Binder::dummy(trait_ref),

`

``

200

`+

);

`

``

201

+

``

202

`+

let ambiguities = compute_applicable_impls_for_diagnostics(

`

``

203

`+

&infcx,

`

``

204

`+

&poly_trait_obligation,

`

``

205

`+

);

`

``

206

+

``

207

`+

let mut it = ambiguities.iter().filter(|ambi| match ambi {

`

``

208

`+

Ambiguity::DefId(did) => {

`

``

209

`+

!did_has_local_parent(*did, cx.tcx, parent, parent_parent)

`

``

210

`+

}

`

``

211

`+

Ambiguity::ParamEnv(_) => false,

`

``

212

`+

});

`

``

213

+

``

214

`+

let _ = it.next();

`

``

215

`+

it.next().is_some()

`

``

216

`+

})

`

``

217

`+

.unwrap_or(false);

`

``

218

+

``

219

`+

if !(self_ty_has_local_parent || of_trait_has_local_parent || impl_is_not_leaky) {

`

161

220

`let const_anon = if self.body_depth == 1

`

162

221

` && parent_def_kind == DefKind::Const

`

163

222

` && parent_opt_item_name != Some(kw::Underscore)

`

`@@ -207,6 +266,47 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {

`

207

266

`}

`

208

267

`}

`

209

268

``

``

269

`+

/// Replace every local type by inference variable.

`

``

270

`+

///

`

``

271


/// ```text

``

272

`+

/// <Global as std::cmp::PartialEq<Global>>

`

``

273

`+

/// to

`

``

274

`+

/// <Global<_> as std::cmp::PartialEq<Global<_>>>

`

``

275


/// ```

``

276

`+

struct LocalTypeInferenceFolder<'a, 'tcx> {

`

``

277

`+

infcx: &'a InferCtxt<'tcx>,

`

``

278

`+

to_ignore: u32,

`

``

279

`+

impl_parent: DefId,

`

``

280

`+

impl_parent_parent: Option,

`

``

281

`+

span: Span,

`

``

282

`+

}

`

``

283

+

``

284

`+

impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for LocalTypeInferenceFolder<'a, 'tcx> {

`

``

285

`+

fn interner(&self) -> TyCtxt<'tcx> {

`

``

286

`+

self.infcx.tcx

`

``

287

`+

}

`

``

288

+

``

289

`+

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

`

``

290

`+

if let Some(ty_did) = t.ty_def_id()

`

``

291

`+

&& did_has_local_parent(

`

``

292

`+

ty_did,

`

``

293

`+

self.infcx.tcx,

`

``

294

`+

self.impl_parent,

`

``

295

`+

self.impl_parent_parent,

`

``

296

`+

)

`

``

297

`+

&& self.to_ignore == 0

`

``

298

`+

{

`

``

299

`+

self.infcx.next_ty_var(TypeVariableOrigin {

`

``

300

`+

kind: TypeVariableOriginKind::TypeInference,

`

``

301

`+

span: self.span,

`

``

302

`+

})

`

``

303

`+

} else {

`

``

304

`+

self.to_ignore = self.to_ignore.saturating_sub(1);

`

``

305

`+

t.super_fold_with(self)

`

``

306

`+

}

`

``

307

`+

}

`

``

308

`+

}

`

``

309

+

210

310

`/// Given a path and a parent impl def id, this checks if the if parent resolution

`

211

311

`/// def id correspond to the def id of the parent impl definition.

`

212

312

`///

`

`@@ -216,16 +316,29 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {

`

216

316

`/// std::convert::PartialEq<Foo>

`

217

317

`/// ^^^^^^^^^^^^^^^^^^^^^^^

`

218

318

```` /// ```

````

``

319

`+

#[inline]

`

219

320

`fn path_has_local_parent(

`

220

321

`path: &Path<'_>,

`

221

322

`cx: &LateContext<'_>,

`

222

323

`impl_parent: DefId,

`

223

324

`impl_parent_parent: Option,

`

224

325

`) -> bool {

`

225

``

`-

path.res.opt_def_id().is_some_and(|did| {

`

226

``

`-

did.is_local() && {

`

227

``

`-

let res_parent = cx.tcx.parent(did);

`

228

``

`-

res_parent == impl_parent || Some(res_parent) == impl_parent_parent

`

229

``

`-

}

`

230

``

`-

})

`

``

326

`+

path.res

`

``

327

`+

.opt_def_id()

`

``

328

`+

.is_some_and(|did| did_has_local_parent(did, cx.tcx, impl_parent, impl_parent_parent))

`

``

329

`+

}

`

``

330

+

``

331

`+

/// Given a def id and a parent impl def id, this checks if the parent

`

``

332

`+

/// def id correspond to the def id of the parent impl definition.

`

``

333

`+

#[inline]

`

``

334

`+

fn did_has_local_parent(

`

``

335

`+

did: DefId,

`

``

336

`+

tcx: TyCtxt<'_>,

`

``

337

`+

impl_parent: DefId,

`

``

338

`+

impl_parent_parent: Option,

`

``

339

`+

) -> bool {

`

``

340

`+

did.is_local() && {

`

``

341

`+

let res_parent = tcx.parent(did);

`

``

342

`+

res_parent == impl_parent || Some(res_parent) == impl_parent_parent

`

``

343

`+

}

`

231

344

`}

`