resolve: Merge NameBindingKind::Module into NameBindingKind::Res
This commit is contained in:
parent
3014e79f9c
commit
babe2c0d0f
11 changed files with 61 additions and 111 deletions
|
|
@ -295,6 +295,12 @@ impl DefKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// This is a "module" in name resolution sense.
|
||||
#[inline]
|
||||
pub fn is_module_like(self) -> bool {
|
||||
matches!(self, DefKind::Mod | DefKind::Enum | DefKind::Trait)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_fn_like(self) -> bool {
|
||||
matches!(
|
||||
|
|
@ -720,6 +726,15 @@ impl<Id> Res<Id> {
|
|||
}
|
||||
}
|
||||
|
||||
/// If this is a "module" in name resolution sense, return its `DefId`.
|
||||
#[inline]
|
||||
pub fn module_like_def_id(&self) -> Option<DefId> {
|
||||
match self {
|
||||
Res::Def(def_kind, def_id) if def_kind.is_module_like() => Some(*def_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A human readable name for the res kind ("function", "module", etc.).
|
||||
pub fn descr(&self) -> &'static str {
|
||||
match *self {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::mem;
|
|||
use std::sync::Arc;
|
||||
|
||||
use rustc_attr_data_structures::Deprecation;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def::{CtorKind, DefKind};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
|
||||
use rustc_middle::arena::ArenaAllocatable;
|
||||
|
|
@ -510,10 +510,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
|
|||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(parent);
|
||||
if matches!(
|
||||
child.res,
|
||||
Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, _)
|
||||
) {
|
||||
if child.res.module_like_def_id().is_some() {
|
||||
bfs_queue.push_back(def_id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3454,9 +3454,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N
|
|||
collect_fn(&child.ident, ns, def_id);
|
||||
}
|
||||
|
||||
if matches!(defkind, DefKind::Mod | DefKind::Enum | DefKind::Trait)
|
||||
&& seen_defs.insert(def_id)
|
||||
{
|
||||
if defkind.is_module_like() && seen_defs.insert(def_id) {
|
||||
queue.push(def_id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,21 +39,6 @@ use crate::{
|
|||
|
||||
type Res = def::Res<NodeId>;
|
||||
|
||||
impl<'ra, Id: Into<DefId>> ToNameBinding<'ra>
|
||||
for (Module<'ra>, ty::Visibility<Id>, Span, LocalExpnId)
|
||||
{
|
||||
fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> {
|
||||
arenas.alloc_name_binding(NameBindingData {
|
||||
kind: NameBindingKind::Module(self.0),
|
||||
ambiguity: None,
|
||||
warn_ambiguity: false,
|
||||
vis: self.1.to_def_id(),
|
||||
span: self.2,
|
||||
expansion: self.3,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ra, Id: Into<DefId>> ToNameBinding<'ra> for (Res, ty::Visibility<Id>, Span, LocalExpnId) {
|
||||
fn to_name_binding(self, arenas: &'ra ResolverArenas<'ra>) -> NameBinding<'ra> {
|
||||
arenas.alloc_name_binding(NameBindingData {
|
||||
|
|
@ -121,7 +106,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
if !def_id.is_local() {
|
||||
// Query `def_kind` is not used because query system overhead is too expensive here.
|
||||
let def_kind = self.cstore().def_kind_untracked(def_id);
|
||||
if let DefKind::Mod | DefKind::Enum | DefKind::Trait = def_kind {
|
||||
if def_kind.is_module_like() {
|
||||
let parent = self
|
||||
.tcx
|
||||
.opt_parent(def_id)
|
||||
|
|
@ -223,12 +208,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
let expansion = parent_scope.expansion;
|
||||
// Record primary definitions.
|
||||
match res {
|
||||
Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, def_id) => {
|
||||
let module = self.expect_module(def_id);
|
||||
self.define(parent, ident, TypeNS, (module, vis, span, expansion));
|
||||
}
|
||||
Res::Def(
|
||||
DefKind::Struct
|
||||
DefKind::Mod
|
||||
| DefKind::Enum
|
||||
| DefKind::Trait
|
||||
| DefKind::Struct
|
||||
| DefKind::Union
|
||||
| DefKind::Variant
|
||||
| DefKind::TyAlias
|
||||
|
|
@ -766,7 +750,12 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
}
|
||||
|
||||
ItemKind::Mod(_, ident, ref mod_kind) => {
|
||||
let module = self.r.new_module(
|
||||
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
|
||||
|
||||
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
|
||||
self.r.mods_with_parse_errors.insert(def_id);
|
||||
}
|
||||
self.parent_scope.module = self.r.new_module(
|
||||
Some(parent),
|
||||
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
|
||||
expansion.to_expn_id(),
|
||||
|
|
@ -774,14 +763,6 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
parent.no_implicit_prelude
|
||||
|| ast::attr::contains_name(&item.attrs, sym::no_implicit_prelude),
|
||||
);
|
||||
self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
|
||||
|
||||
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
|
||||
self.r.mods_with_parse_errors.insert(def_id);
|
||||
}
|
||||
|
||||
// Descend into the module.
|
||||
self.parent_scope.module = module;
|
||||
}
|
||||
|
||||
// These items live in the value namespace.
|
||||
|
|
@ -804,15 +785,15 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
}
|
||||
|
||||
ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => {
|
||||
let module = self.r.new_module(
|
||||
self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
|
||||
|
||||
self.parent_scope.module = self.r.new_module(
|
||||
Some(parent),
|
||||
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
|
||||
expansion.to_expn_id(),
|
||||
item.span,
|
||||
parent.no_implicit_prelude,
|
||||
);
|
||||
self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
|
||||
self.parent_scope.module = module;
|
||||
}
|
||||
|
||||
// These items live in both the type and value namespaces.
|
||||
|
|
@ -920,8 +901,9 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
|
|||
}
|
||||
.map(|module| {
|
||||
let used = self.process_macro_use_imports(item, module);
|
||||
let res = module.res().unwrap();
|
||||
let vis = ty::Visibility::<LocalDefId>::Public;
|
||||
let binding = (module, vis, sp, expansion).to_name_binding(self.r.arenas);
|
||||
let binding = (res, vis, sp, expansion).to_name_binding(self.r.arenas);
|
||||
(used, Some(ModuleOrUniformRoot::Module(module)), binding)
|
||||
})
|
||||
.unwrap_or((true, None, self.r.dummy_binding));
|
||||
|
|
|
|||
|
|
@ -232,12 +232,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
let old_kind = match (ns, old_binding.module()) {
|
||||
let old_kind = match (ns, old_binding.res()) {
|
||||
(ValueNS, _) => "value",
|
||||
(MacroNS, _) => "macro",
|
||||
(TypeNS, _) if old_binding.is_extern_crate() => "extern crate",
|
||||
(TypeNS, Some(module)) if module.is_normal() => "module",
|
||||
(TypeNS, Some(module)) if module.is_trait() => "trait",
|
||||
(TypeNS, Res::Def(DefKind::Mod, _)) => "module",
|
||||
(TypeNS, Res::Def(DefKind::Trait, _)) => "trait",
|
||||
(TypeNS, _) => "type",
|
||||
};
|
||||
|
||||
|
|
@ -1319,7 +1319,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
// collect submodules to explore
|
||||
if let Some(module) = name_binding.module() {
|
||||
if let Some(def_id) = name_binding.res().module_like_def_id() {
|
||||
// form the path
|
||||
let mut path_segments = path_segments.clone();
|
||||
path_segments.push(ast::PathSegment::from_ident(ident));
|
||||
|
|
@ -1339,14 +1339,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
|
||||
if !is_extern_crate_that_also_appears_in_prelude || alias_import {
|
||||
// add the module to the lookup
|
||||
if seen_modules.insert(module.def_id()) {
|
||||
if seen_modules.insert(def_id) {
|
||||
if via_import { &mut worklist_via_import } else { &mut worklist }.push(
|
||||
(
|
||||
module,
|
||||
this.expect_module(def_id),
|
||||
path_segments,
|
||||
child_accessible,
|
||||
child_doc_visible,
|
||||
is_stable && this.is_stable(module.def_id(), name_binding.span),
|
||||
is_stable && this.is_stable(def_id, name_binding.span),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -2094,7 +2094,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
true, // re-export
|
||||
));
|
||||
}
|
||||
NameBindingKind::Res(_) | NameBindingKind::Module(_) => {}
|
||||
NameBindingKind::Res(_) => {}
|
||||
}
|
||||
let first = binding == first_binding;
|
||||
let def_span = self.tcx.sess.source_map().guess_head_span(binding.span);
|
||||
|
|
@ -2306,25 +2306,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
.ok()
|
||||
};
|
||||
if let Some(binding) = binding {
|
||||
let mut found = |what| {
|
||||
msg = format!(
|
||||
"expected {}, found {} `{}` in {}",
|
||||
ns.descr(),
|
||||
what,
|
||||
ident,
|
||||
parent
|
||||
)
|
||||
};
|
||||
if binding.module().is_some() {
|
||||
found("module")
|
||||
} else {
|
||||
match binding.res() {
|
||||
// Avoid using TyCtxt::def_kind_descr in the resolver, because it
|
||||
// indirectly *calls* the resolver, and would cause a query cycle.
|
||||
Res::Def(kind, id) => found(kind.descr(id)),
|
||||
_ => found(ns_to_try.descr()),
|
||||
}
|
||||
}
|
||||
msg = format!(
|
||||
"expected {}, found {} `{ident}` in {parent}",
|
||||
ns.descr(),
|
||||
binding.res().descr(),
|
||||
);
|
||||
};
|
||||
}
|
||||
(msg, None)
|
||||
|
|
|
|||
|
|
@ -1637,11 +1637,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
}
|
||||
|
||||
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
|
||||
if let Some(next_module) = binding.module() {
|
||||
if self.mods_with_parse_errors.contains(&next_module.def_id()) {
|
||||
if let Some(def_id) = binding.res().module_like_def_id() {
|
||||
if self.mods_with_parse_errors.contains(&def_id) {
|
||||
module_had_parse_errors = true;
|
||||
}
|
||||
module = Some(ModuleOrUniformRoot::Module(next_module));
|
||||
module = Some(ModuleOrUniformRoot::Module(self.expect_module(def_id)));
|
||||
record_segment_res(self, res);
|
||||
} else if res == Res::ToolMod && !is_last && opt_ns.is_some() {
|
||||
if binding.is_import() {
|
||||
|
|
|
|||
|
|
@ -679,9 +679,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
NameBindingKind::Res(res) => {
|
||||
Some(self.def_id_to_node_id(res.def_id().expect_local()))
|
||||
}
|
||||
NameBindingKind::Module(module) => {
|
||||
Some(self.def_id_to_node_id(module.def_id().expect_local()))
|
||||
}
|
||||
NameBindingKind::Import { import, .. } => import.id(),
|
||||
};
|
||||
if let Some(binding_id) = binding_id {
|
||||
|
|
|
|||
|
|
@ -2656,18 +2656,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
if result.is_some() || !name_binding.vis.is_visible_locally() {
|
||||
return;
|
||||
}
|
||||
if let Some(module) = name_binding.module() {
|
||||
if let Some(module_def_id) = name_binding.res().module_like_def_id() {
|
||||
// form the path
|
||||
let mut path_segments = path_segments.clone();
|
||||
path_segments.push(ast::PathSegment::from_ident(ident));
|
||||
let module_def_id = module.def_id();
|
||||
let doc_visible = doc_visible
|
||||
&& (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id));
|
||||
if module_def_id == def_id {
|
||||
let path =
|
||||
Path { span: name_binding.span, segments: path_segments, tokens: None };
|
||||
result = Some((
|
||||
module,
|
||||
r.expect_module(module_def_id),
|
||||
ImportSuggestion {
|
||||
did: Some(def_id),
|
||||
descr: "module",
|
||||
|
|
@ -2682,6 +2681,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
} else {
|
||||
// add the module to the lookup
|
||||
if seen_modules.insert(module_def_id) {
|
||||
let module = r.expect_module(module_def_id);
|
||||
worklist.push((module, path_segments, doc_visible));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -674,7 +674,6 @@ impl<'ra> Module<'ra> {
|
|||
}
|
||||
}
|
||||
|
||||
// Public for rustdoc.
|
||||
fn def_id(self) -> DefId {
|
||||
self.opt_def_id().expect("`ModuleData::def_id` is called on a block module")
|
||||
}
|
||||
|
|
@ -782,7 +781,6 @@ impl<'ra> ToNameBinding<'ra> for NameBinding<'ra> {
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
enum NameBindingKind<'ra> {
|
||||
Res(Res),
|
||||
Module(Module<'ra>),
|
||||
Import { binding: NameBinding<'ra>, import: Import<'ra> },
|
||||
}
|
||||
|
||||
|
|
@ -875,18 +873,9 @@ struct AmbiguityError<'ra> {
|
|||
}
|
||||
|
||||
impl<'ra> NameBindingData<'ra> {
|
||||
fn module(&self) -> Option<Module<'ra>> {
|
||||
match self.kind {
|
||||
NameBindingKind::Module(module) => Some(module),
|
||||
NameBindingKind::Import { binding, .. } => binding.module(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn res(&self) -> Res {
|
||||
match self.kind {
|
||||
NameBindingKind::Res(res) => res,
|
||||
NameBindingKind::Module(module) => module.res().unwrap(),
|
||||
NameBindingKind::Import { binding, .. } => binding.res(),
|
||||
}
|
||||
}
|
||||
|
|
@ -921,7 +910,7 @@ impl<'ra> NameBindingData<'ra> {
|
|||
DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..),
|
||||
_,
|
||||
)) => true,
|
||||
NameBindingKind::Res(..) | NameBindingKind::Module(..) => false,
|
||||
NameBindingKind::Res(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -930,11 +919,7 @@ impl<'ra> NameBindingData<'ra> {
|
|||
NameBindingKind::Import { import, .. } => {
|
||||
matches!(import.kind, ImportKind::ExternCrate { .. })
|
||||
}
|
||||
NameBindingKind::Module(module)
|
||||
if let ModuleKind::Def(DefKind::Mod, def_id, _) = module.kind =>
|
||||
{
|
||||
def_id.is_crate_root()
|
||||
}
|
||||
NameBindingKind::Res(Res::Def(_, def_id)) => def_id.is_crate_root(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1279,7 +1264,8 @@ impl<'ra> ResolverArenas<'ra> {
|
|||
if let Some(def_id) = def_id {
|
||||
module_map.insert(def_id, module);
|
||||
let vis = ty::Visibility::<DefId>::Public;
|
||||
let binding = (module, vis, module.span, LocalExpnId::ROOT).to_name_binding(self);
|
||||
let res = module.res().unwrap();
|
||||
let binding = (res, vis, module.span, LocalExpnId::ROOT).to_name_binding(self);
|
||||
module_self_bindings.insert(module, binding);
|
||||
}
|
||||
module
|
||||
|
|
@ -1838,7 +1824,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
module.ensure_traits(self);
|
||||
let traits = module.traits.borrow();
|
||||
for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() {
|
||||
if self.trait_may_have_item(trait_binding.module(), assoc_item) {
|
||||
let trait_module = self.get_module(trait_binding.res().def_id());
|
||||
if self.trait_may_have_item(trait_module, assoc_item) {
|
||||
let def_id = trait_binding.res().def_id();
|
||||
let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name);
|
||||
found_traits.push(TraitCandidate { def_id, import_ids });
|
||||
|
|
@ -2174,9 +2161,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
} else {
|
||||
self.crate_loader(|c| c.maybe_process_path_extern(ident.name))?
|
||||
};
|
||||
let crate_root = self.expect_module(crate_id.as_def_id());
|
||||
let res = Res::Def(DefKind::Mod, crate_id.as_def_id());
|
||||
let vis = ty::Visibility::<DefId>::Public;
|
||||
(crate_root, vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(self.arenas)
|
||||
(res, vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(self.arenas)
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,6 @@ crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt:
|
|||
crate0::{{expn2}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "produce_it")
|
||||
crate0::{{expn3}}: parent: crate0::{{expn2}}, call_site_ctxt: #3, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site")
|
||||
crate0::{{expn4}}: parent: crate0::{{expn3}}, call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "$crate::dummy")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "include")
|
||||
|
||||
SyntaxContexts:
|
||||
#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque)
|
||||
|
|
|
|||
|
|
@ -72,12 +72,6 @@ crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt:
|
|||
crate0::{{expn2}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "outer")
|
||||
crate0::{{expn3}}: parent: crate0::{{expn2}}, call_site_ctxt: #3, def_site_ctxt: #3, kind: Macro(Bang, "inner")
|
||||
crate0::{{expn4}}: parent: crate0::{{expn3}}, call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "print_bang")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "diagnostic::on_unimplemented")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive")
|
||||
crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "include")
|
||||
|
||||
SyntaxContexts:
|
||||
#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue