1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
use std::fmt::Write;

use rustc_hir::def::DefKind;
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_middle::ty::{GenericArgs, TyCtxt};
use rustc_span::symbol::sym;

fn format_variances(tcx: TyCtxt<'_>, def_id: LocalDefId) -> String {
    let variances = tcx.variances_of(def_id);
    let generics = GenericArgs::identity_for_item(tcx, def_id);
    // 7 = 2-letter parameter + ": " + 1-letter variance + ", "
    let mut ret = String::with_capacity(2 + 7 * variances.len());
    ret.push('[');
    for (arg, variance) in generics.iter().zip(variances.iter()) {
        write!(ret, "{arg}: {variance:?}, ").unwrap();
    }
    // Remove trailing `, `.
    if !variances.is_empty() {
        ret.pop();
        ret.pop();
    }
    ret.push(']');
    ret
}

pub(crate) fn variances(tcx: TyCtxt<'_>) {
    if tcx.has_attr(CRATE_DEF_ID, sym::rustc_variance_of_opaques) {
        for id in tcx.hir().items() {
            let DefKind::OpaqueTy = tcx.def_kind(id.owner_id) else { continue };

            tcx.dcx().emit_err(crate::errors::VariancesOf {
                span: tcx.def_span(id.owner_id),
                variances: format_variances(tcx, id.owner_id.def_id),
            });
        }
    }

    for id in tcx.hir().items() {
        if !tcx.has_attr(id.owner_id, sym::rustc_variance) {
            continue;
        }

        tcx.dcx().emit_err(crate::errors::VariancesOf {
            span: tcx.def_span(id.owner_id),
            variances: format_variances(tcx, id.owner_id.def_id),
        });
    }
}