resolve: Resolve single-segment imports using in-scope resolution on 2018 edition
This commit is contained in:
parent
1cfd08c0c4
commit
07af4ec7a2
@ -809,6 +809,7 @@ fn process_legacy_macro_imports(&mut self, item: &Item, module: Module<'a>,
|
||||
ModuleOrUniformRoot::Module(module),
|
||||
ident,
|
||||
MacroNS,
|
||||
None,
|
||||
false,
|
||||
span,
|
||||
);
|
||||
|
@ -103,6 +103,15 @@ enum DeterminacyExt {
|
||||
WeakUndetermined,
|
||||
}
|
||||
|
||||
impl DeterminacyExt {
|
||||
fn to_determinacy(self) -> Determinacy {
|
||||
match self {
|
||||
DeterminacyExt::Determined => Determined,
|
||||
DeterminacyExt::Undetermined | DeterminacyExt::WeakUndetermined => Undetermined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A free importable items suggested in case of resolution failure.
|
||||
struct ImportSuggestion {
|
||||
path: Path,
|
||||
@ -998,15 +1007,22 @@ fn def(self) -> Def {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
enum UniformRootKind {
|
||||
CurrentScope,
|
||||
ExternPrelude,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ModuleOrUniformRoot<'a> {
|
||||
enum ModuleOrUniformRoot<'a> {
|
||||
/// Regular module.
|
||||
Module(Module<'a>),
|
||||
|
||||
/// The `{{root}}` (`CrateRoot` aka "global") / `extern` initial segment
|
||||
/// in which external crates resolve, and also `crate` (only in `{{root}}`,
|
||||
/// but *not* `extern`), in the Rust 2018 edition.
|
||||
UniformRoot(Name),
|
||||
/// This "virtual module" denotes either resolution in extern prelude
|
||||
/// for paths starting with `::` on 2018 edition or `extern::`,
|
||||
/// or resolution in current scope for single-segment imports.
|
||||
UniformRoot(UniformRootKind),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -2158,23 +2174,31 @@ fn hygienic_lexical_parent_with_compatibility_fallback(&mut self, module: Module
|
||||
None
|
||||
}
|
||||
|
||||
fn resolve_ident_in_module(&mut self,
|
||||
module: ModuleOrUniformRoot<'a>,
|
||||
mut ident: Ident,
|
||||
ns: Namespace,
|
||||
record_used: bool,
|
||||
span: Span)
|
||||
-> Result<&'a NameBinding<'a>, Determinacy> {
|
||||
fn resolve_ident_in_module(
|
||||
&mut self,
|
||||
module: ModuleOrUniformRoot<'a>,
|
||||
mut ident: Ident,
|
||||
ns: Namespace,
|
||||
parent_scope: Option<&ParentScope<'a>>,
|
||||
record_used: bool,
|
||||
path_span: Span
|
||||
) -> Result<&'a NameBinding<'a>, Determinacy> {
|
||||
ident.span = ident.span.modern();
|
||||
let orig_current_module = self.current_module;
|
||||
if let ModuleOrUniformRoot::Module(module) = module {
|
||||
if let Some(def) = ident.span.adjust(module.expansion) {
|
||||
self.current_module = self.macro_def_scope(def);
|
||||
match module {
|
||||
ModuleOrUniformRoot::Module(module) => {
|
||||
if let Some(def) = ident.span.adjust(module.expansion) {
|
||||
self.current_module = self.macro_def_scope(def);
|
||||
}
|
||||
}
|
||||
ModuleOrUniformRoot::UniformRoot(UniformRootKind::ExternPrelude) => {
|
||||
ident.span.adjust(Mark::root());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let result = self.resolve_ident_in_module_unadjusted(
|
||||
module, ident, ns, record_used, span,
|
||||
);
|
||||
let result = self.resolve_ident_in_module_unadjusted_ext(
|
||||
module, ident, ns, parent_scope, false, record_used, path_span,
|
||||
).map_err(DeterminacyExt::to_determinacy);
|
||||
self.current_module = orig_current_module;
|
||||
result
|
||||
}
|
||||
@ -2671,6 +2695,7 @@ fn check_trait_item<F>(&mut self, ident: Ident, ns: Namespace, span: Span, err:
|
||||
ModuleOrUniformRoot::Module(module),
|
||||
ident,
|
||||
ns,
|
||||
None,
|
||||
false,
|
||||
span,
|
||||
).is_err() {
|
||||
@ -3699,9 +3724,9 @@ fn resolve_path(
|
||||
continue;
|
||||
}
|
||||
if name == keywords::Extern.name() ||
|
||||
name == keywords::CrateRoot.name() &&
|
||||
self.session.rust_2018() {
|
||||
module = Some(ModuleOrUniformRoot::UniformRoot(name));
|
||||
name == keywords::CrateRoot.name() && self.session.rust_2018() {
|
||||
module =
|
||||
Some(ModuleOrUniformRoot::UniformRoot(UniformRootKind::ExternPrelude));
|
||||
continue;
|
||||
}
|
||||
if name == keywords::CrateRoot.name() ||
|
||||
@ -3731,7 +3756,7 @@ fn resolve_path(
|
||||
}
|
||||
|
||||
let binding = if let Some(module) = module {
|
||||
self.resolve_ident_in_module(module, ident, ns, record_used, path_span)
|
||||
self.resolve_ident_in_module(module, ident, ns, None, record_used, path_span)
|
||||
} else if opt_ns.is_none() || opt_ns == Some(MacroNS) {
|
||||
assert!(ns == TypeNS);
|
||||
self.early_resolve_ident_in_lexical_scope(ident, ns, None, opt_ns.is_none(),
|
||||
@ -3751,7 +3776,7 @@ fn resolve_path(
|
||||
def, path.len() - 1
|
||||
));
|
||||
}
|
||||
_ => Err(if record_used { Determined } else { Undetermined }),
|
||||
_ => Err(Determinacy::determined(record_used)),
|
||||
}
|
||||
};
|
||||
|
||||
@ -3825,7 +3850,8 @@ fn resolve_path(
|
||||
|
||||
PathResult::Module(match module {
|
||||
Some(module) => module,
|
||||
None if path.is_empty() => ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name()),
|
||||
None if path.is_empty() =>
|
||||
ModuleOrUniformRoot::UniformRoot(UniformRootKind::CurrentScope),
|
||||
_ => span_bug!(path_span, "resolve_path: non-empty path `{:?}` has no module", path),
|
||||
})
|
||||
}
|
||||
@ -4037,6 +4063,7 @@ fn extract_node_id(t: &Ty) -> Option<NodeId> {
|
||||
ModuleOrUniformRoot::Module(module),
|
||||
ident,
|
||||
ns,
|
||||
None,
|
||||
false,
|
||||
module.span,
|
||||
) {
|
||||
@ -4359,6 +4386,7 @@ fn get_traits_containing_item(&mut self, mut ident: Ident, ns: Namespace)
|
||||
ModuleOrUniformRoot::Module(module),
|
||||
ident,
|
||||
ns,
|
||||
None,
|
||||
false,
|
||||
module.span,
|
||||
).is_ok() {
|
||||
@ -4945,6 +4973,10 @@ fn report_conflict<'b>(&mut self,
|
||||
|
||||
fn extern_prelude_get(&mut self, ident: Ident, speculative: bool, skip_feature_gate: bool)
|
||||
-> Option<&'a NameBinding<'a>> {
|
||||
if ident.is_path_segment_keyword() {
|
||||
// Make sure `self`, `super` etc produce an error when passed to here.
|
||||
return None;
|
||||
}
|
||||
self.extern_prelude.get(&ident.modern()).cloned().and_then(|entry| {
|
||||
if let Some(binding) = entry.extern_crate_item {
|
||||
if !speculative && !skip_feature_gate && entry.introduced_by_item &&
|
||||
|
@ -371,11 +371,11 @@ fn check_unused_macros(&self) {
|
||||
}
|
||||
|
||||
impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||
pub fn dummy_parent_scope(&mut self) -> ParentScope<'a> {
|
||||
pub fn dummy_parent_scope(&self) -> ParentScope<'a> {
|
||||
self.invoc_parent_scope(Mark::root(), Vec::new())
|
||||
}
|
||||
|
||||
fn invoc_parent_scope(&mut self, invoc_id: Mark, derives: Vec<ast::Path>) -> ParentScope<'a> {
|
||||
fn invoc_parent_scope(&self, invoc_id: Mark, derives: Vec<ast::Path>) -> ParentScope<'a> {
|
||||
let invoc = self.invocations[&invoc_id];
|
||||
ParentScope {
|
||||
module: invoc.module.get().nearest_item_scope(),
|
||||
@ -610,6 +610,11 @@ struct Flags: u8 {
|
||||
assert!(macro_kind.is_none() || !is_import); // `is_import` implies no macro kind
|
||||
ident = ident.modern();
|
||||
|
||||
// Make sure `self`, `super` etc produce an error when passed to here.
|
||||
if ident.is_path_segment_keyword() {
|
||||
return Err(Determinacy::Determined);
|
||||
}
|
||||
|
||||
// This is *the* result, resolution from the scope closest to the resolved identifier.
|
||||
// However, sometimes this result is "weak" because it comes from a glob import or
|
||||
// a macro expansion, and in this case it cannot shadow names from outer scopes, e.g.
|
||||
@ -671,6 +676,7 @@ struct Flags: u8 {
|
||||
ModuleOrUniformRoot::Module(module),
|
||||
ident,
|
||||
ns,
|
||||
None,
|
||||
true,
|
||||
record_used,
|
||||
path_span,
|
||||
|
@ -11,7 +11,7 @@
|
||||
use self::ImportDirectiveSubclass::*;
|
||||
|
||||
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
|
||||
use {CrateLint, DeterminacyExt, Module, ModuleOrUniformRoot, PerNS};
|
||||
use {CrateLint, DeterminacyExt, Module, ModuleOrUniformRoot, PerNS, UniformRootKind};
|
||||
use Namespace::{self, TypeNS, MacroNS};
|
||||
use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
|
||||
use {Resolver, Segment};
|
||||
@ -23,7 +23,7 @@
|
||||
use rustc::ty;
|
||||
use rustc::lint::builtin::BuiltinLintDiagnostics;
|
||||
use rustc::lint::builtin::{DUPLICATE_MACRO_EXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE};
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::def_id::{CrateNum, DefId};
|
||||
use rustc::hir::def::*;
|
||||
use rustc::session::DiagnosticMessageId;
|
||||
use rustc::util::nodemap::FxHashSet;
|
||||
@ -61,7 +61,7 @@ pub enum ImportDirectiveSubclass<'a> {
|
||||
|
||||
/// One import directive.
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct ImportDirective<'a> {
|
||||
crate struct ImportDirective<'a> {
|
||||
/// The id of the `extern crate`, `UseTree` etc that imported this `ImportDirective`.
|
||||
///
|
||||
/// In the case where the `ImportDirective` was expanded from a "nested" use tree,
|
||||
@ -144,11 +144,8 @@ fn resolution(&self, module: Module<'a>, ident: Ident, ns: Namespace)
|
||||
path_span: Span,
|
||||
) -> Result<&'a NameBinding<'a>, Determinacy> {
|
||||
self.resolve_ident_in_module_unadjusted_ext(
|
||||
module, ident, ns, false, record_used, path_span
|
||||
).map_err(|determinacy_ext| match determinacy_ext {
|
||||
DeterminacyExt::Determined => Determined,
|
||||
DeterminacyExt::Undetermined | DeterminacyExt::WeakUndetermined => Undetermined,
|
||||
})
|
||||
module, ident, ns, None, false, record_used, path_span
|
||||
).map_err(DeterminacyExt::to_determinacy)
|
||||
}
|
||||
|
||||
/// Attempts to resolve `ident` in namespaces `ns` of `module`.
|
||||
@ -158,87 +155,55 @@ fn resolution(&self, module: Module<'a>, ident: Ident, ns: Namespace)
|
||||
module: ModuleOrUniformRoot<'a>,
|
||||
ident: Ident,
|
||||
ns: Namespace,
|
||||
parent_scope: Option<&ParentScope<'a>>,
|
||||
restricted_shadowing: bool,
|
||||
record_used: bool,
|
||||
path_span: Span,
|
||||
) -> Result<&'a NameBinding<'a>, DeterminacyExt> {
|
||||
let module = match module {
|
||||
ModuleOrUniformRoot::Module(module) => module,
|
||||
ModuleOrUniformRoot::UniformRoot(root) => {
|
||||
// HACK(eddyb): `resolve_path` uses `keywords::Invalid` to indicate
|
||||
// paths of length 0, and currently these are relative `use` paths.
|
||||
let can_be_relative = !ident.is_path_segment_keyword() &&
|
||||
root == keywords::Invalid.name();
|
||||
if can_be_relative {
|
||||
// Try first to resolve relatively.
|
||||
let mut ctxt = ident.span.ctxt().modern();
|
||||
let self_module = self.resolve_self(&mut ctxt, self.current_module);
|
||||
|
||||
let binding = self.resolve_ident_in_module_unadjusted_ext(
|
||||
ModuleOrUniformRoot::Module(self_module),
|
||||
ident,
|
||||
ns,
|
||||
restricted_shadowing,
|
||||
record_used,
|
||||
path_span,
|
||||
);
|
||||
|
||||
// FIXME(eddyb) This may give false negatives, specifically
|
||||
// if a crate with the same name is found in `extern_prelude`,
|
||||
// preventing the check below this one from returning `binding`
|
||||
// in all cases.
|
||||
//
|
||||
// That is, if there's no crate with the same name, `binding`
|
||||
// is always returned, which is the result of doing the exact
|
||||
// same lookup of `ident`, in the `self` module.
|
||||
// But when a crate does exist, it will get chosen even when
|
||||
// macro expansion could result in a success from the lookup
|
||||
// in the `self` module, later on.
|
||||
if binding.is_ok() {
|
||||
return binding;
|
||||
ModuleOrUniformRoot::UniformRoot(uniform_root_kind) => {
|
||||
assert!(!restricted_shadowing);
|
||||
match uniform_root_kind {
|
||||
UniformRootKind::ExternPrelude => {
|
||||
return if let Some(binding) =
|
||||
self.extern_prelude_get(ident, !record_used, false) {
|
||||
Ok(binding)
|
||||
} else if !self.graph_root.unresolved_invocations.borrow().is_empty() {
|
||||
// Macro-expanded `extern crate` items can add names to extern prelude.
|
||||
Err(DeterminacyExt::Undetermined)
|
||||
} else {
|
||||
Err(DeterminacyExt::Determined)
|
||||
}
|
||||
}
|
||||
UniformRootKind::CurrentScope => {
|
||||
let parent_scope =
|
||||
parent_scope.expect("no parent scope for a single-segment import");
|
||||
|
||||
// Fall back to resolving to an external crate.
|
||||
if !(
|
||||
ns == TypeNS &&
|
||||
!ident.is_path_segment_keyword() &&
|
||||
self.extern_prelude.contains_key(&ident.modern())
|
||||
) {
|
||||
// ... unless the crate name is not in the `extern_prelude`.
|
||||
return binding;
|
||||
if ns == TypeNS {
|
||||
if ident.name == keywords::Crate.name() ||
|
||||
ident.name == keywords::DollarCrate.name() {
|
||||
let module = self.resolve_crate_root(ident);
|
||||
let binding = (module, ty::Visibility::Public,
|
||||
module.span, Mark::root())
|
||||
.to_name_binding(self.arenas);
|
||||
return Ok(binding);
|
||||
} else if ident.name == keywords::Super.name() ||
|
||||
ident.name == keywords::SelfValue.name() {
|
||||
// FIXME: Implement these with renaming requirements so that e.g.
|
||||
// `use super;` doesn't work, but `use super as name;` does.
|
||||
}
|
||||
}
|
||||
|
||||
let binding = self.early_resolve_ident_in_lexical_scope(
|
||||
ident, ns, None, true, parent_scope, record_used, record_used, path_span
|
||||
);
|
||||
return binding.map_err(|determinacy| match determinacy {
|
||||
Determined => DeterminacyExt::Determined,
|
||||
Undetermined => DeterminacyExt::Undetermined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let crate_root = if
|
||||
ns == TypeNS &&
|
||||
root != keywords::Extern.name() &&
|
||||
(
|
||||
ident.name == keywords::Crate.name() ||
|
||||
ident.name == keywords::DollarCrate.name()
|
||||
)
|
||||
{
|
||||
self.resolve_crate_root(ident)
|
||||
} else if
|
||||
ns == TypeNS &&
|
||||
!ident.is_path_segment_keyword()
|
||||
{
|
||||
if let Some(binding) = self.extern_prelude_get(ident, !record_used, false) {
|
||||
let module = self.get_module(binding.def().def_id());
|
||||
self.populate_module_if_necessary(module);
|
||||
return Ok(binding);
|
||||
} else if !self.graph_root.unresolved_invocations.borrow().is_empty() {
|
||||
// Macro-expanded `extern crate`items still can add names to extern prelude.
|
||||
return Err(DeterminacyExt::Undetermined);
|
||||
} else {
|
||||
return Err(DeterminacyExt::Determined);
|
||||
}
|
||||
} else {
|
||||
return Err(DeterminacyExt::Determined);
|
||||
};
|
||||
self.populate_module_if_necessary(crate_root);
|
||||
let binding = (crate_root, ty::Visibility::Public,
|
||||
crate_root.span, Mark::root()).to_name_binding(self.arenas);
|
||||
return Ok(binding);
|
||||
}
|
||||
};
|
||||
|
||||
@ -312,7 +277,8 @@ fn resolution(&self, module: Module<'a>, ident: Ident, ns: Namespace)
|
||||
SingleImport { source, .. } => source,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match self.resolve_ident_in_module(module, ident, ns, false, path_span) {
|
||||
match self.resolve_ident_in_module(module, ident, ns, Some(&single_import.parent_scope),
|
||||
false, path_span) {
|
||||
Err(Determined) => continue,
|
||||
Ok(binding) if !self.is_accessible_from(
|
||||
binding.vis, single_import.parent_scope.module
|
||||
@ -439,8 +405,8 @@ pub fn add_import_directive(&mut self,
|
||||
|
||||
// Given a binding and an import directive that resolves to it,
|
||||
// return the corresponding binding defined by the import directive.
|
||||
pub fn import(&self, binding: &'a NameBinding<'a>, directive: &'a ImportDirective<'a>)
|
||||
-> &'a NameBinding<'a> {
|
||||
crate fn import(&self, binding: &'a NameBinding<'a>, directive: &'a ImportDirective<'a>)
|
||||
-> &'a NameBinding<'a> {
|
||||
let vis = if binding.pseudo_vis().is_at_least(directive.vis.get(), self) ||
|
||||
// c.f. `PUB_USE_OF_PRIVATE_EXTERN_CRATE`
|
||||
!directive.is_glob() && binding.is_extern_crate() {
|
||||
@ -768,10 +734,9 @@ fn resolve_import(&mut self, directive: &'b ImportDirective<'b>) -> bool {
|
||||
let module = if let Some(module) = directive.imported_module.get() {
|
||||
module
|
||||
} else {
|
||||
let vis = directive.vis.get();
|
||||
// For better failure detection, pretend that the import will not define any names
|
||||
// while resolving its module path.
|
||||
directive.vis.set(ty::Visibility::Invisible);
|
||||
// For better failure detection, pretend that the import will
|
||||
// not define any names while resolving its module path.
|
||||
let orig_vis = directive.vis.replace(ty::Visibility::Invisible);
|
||||
let result = self.resolve_path(
|
||||
&directive.module_path[..],
|
||||
None,
|
||||
@ -780,7 +745,7 @@ fn resolve_import(&mut self, directive: &'b ImportDirective<'b>) -> bool {
|
||||
directive.span,
|
||||
directive.crate_lint(),
|
||||
);
|
||||
directive.vis.set(vis);
|
||||
directive.vis.set(orig_vis);
|
||||
|
||||
match result {
|
||||
PathResult::Module(module) => module,
|
||||
@ -803,11 +768,15 @@ fn resolve_import(&mut self, directive: &'b ImportDirective<'b>) -> bool {
|
||||
let mut indeterminate = false;
|
||||
self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS {
|
||||
if let Err(Undetermined) = result[ns].get() {
|
||||
result[ns].set(this.resolve_ident_in_module(module,
|
||||
source,
|
||||
ns,
|
||||
false,
|
||||
directive.span));
|
||||
// For better failure detection, pretend that the import will
|
||||
// not define any names while resolving its module path.
|
||||
let orig_vis = directive.vis.replace(ty::Visibility::Invisible);
|
||||
let binding = this.resolve_ident_in_module(
|
||||
module, source, ns, Some(&directive.parent_scope), false, directive.span
|
||||
);
|
||||
directive.vis.set(orig_vis);
|
||||
|
||||
result[ns].set(binding);
|
||||
} else {
|
||||
return
|
||||
};
|
||||
@ -940,7 +909,10 @@ fn finalize_import(
|
||||
if all_ns_err {
|
||||
let mut all_ns_failed = true;
|
||||
self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS {
|
||||
if this.resolve_ident_in_module(module, ident, ns, true, span).is_ok() {
|
||||
let binding = this.resolve_ident_in_module(
|
||||
module, ident, ns, Some(&directive.parent_scope), true, span
|
||||
);
|
||||
if binding.is_ok() {
|
||||
all_ns_failed = false;
|
||||
}
|
||||
});
|
||||
@ -1140,8 +1112,9 @@ fn finalize_resolutions_in(&mut self, module: Module<'b>) {
|
||||
if binding.is_import() || binding.is_macro_def() {
|
||||
let def = binding.def();
|
||||
if def != Def::Err {
|
||||
if !def.def_id().is_local() {
|
||||
self.cstore.export_macros_untracked(def.def_id().krate);
|
||||
let def_id = def.def_id();
|
||||
if !def_id.is_local() && def_id.krate != CrateNum::BuiltinMacros {
|
||||
self.cstore.export_macros_untracked(def_id.krate);
|
||||
}
|
||||
reexports.push(Export {
|
||||
ident: ident.modern(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
// edition:2018
|
||||
|
||||
#![feature(alloc)]
|
||||
#![feature(alloc, underscore_imports)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
@ -23,7 +23,7 @@ fn check() {
|
||||
}
|
||||
|
||||
mod import_in_scope {
|
||||
use alloc;
|
||||
use alloc as _;
|
||||
//~^ ERROR use of extern prelude names introduced with `extern crate` items is unstable
|
||||
use alloc::boxed;
|
||||
//~^ ERROR use of extern prelude names introduced with `extern crate` items is unstable
|
||||
|
@ -1,7 +1,7 @@
|
||||
error[E0658]: use of extern prelude names introduced with `extern crate` items is unstable (see issue #55599)
|
||||
--> $DIR/feature-gate-extern_crate_item_prelude.rs:26:9
|
||||
|
|
||||
LL | use alloc;
|
||||
LL | use alloc as _;
|
||||
| ^^^^^
|
||||
|
|
||||
= help: add #![feature(extern_crate_item_prelude)] to the crate attributes to enable
|
||||
|
12
src/test/ui/rust-2018/uniform-paths/from-decl-macro.rs
Normal file
12
src/test/ui/rust-2018/uniform-paths/from-decl-macro.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// compile-pass
|
||||
// edition:2018
|
||||
|
||||
#![feature(decl_macro)]
|
||||
|
||||
macro check() {
|
||||
::std::vec::Vec::<u8>::new()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
check!();
|
||||
}
|
Loading…
Reference in New Issue
Block a user