resolve: Generalize early_resolve_ident_in_lexical_scope
slightly
Flatten `ModuleOrUniformRoot` variants
This commit is contained in:
parent
c06e69ee70
commit
5e121756ef
3 changed files with 77 additions and 81 deletions
|
@ -102,6 +102,12 @@ enum Weak {
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ScopeSet {
|
||||||
|
Import(Namespace),
|
||||||
|
Macro(MacroKind),
|
||||||
|
Module,
|
||||||
|
}
|
||||||
|
|
||||||
/// A free importable items suggested in case of resolution failure.
|
/// A free importable items suggested in case of resolution failure.
|
||||||
struct ImportSuggestion {
|
struct ImportSuggestion {
|
||||||
path: Path,
|
path: Path,
|
||||||
|
@ -997,22 +1003,19 @@ impl<'a> LexicalScopeBinding<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
||||||
enum UniformRootKind {
|
|
||||||
CurrentScope,
|
|
||||||
ExternPrelude,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum ModuleOrUniformRoot<'a> {
|
enum ModuleOrUniformRoot<'a> {
|
||||||
/// Regular module.
|
/// Regular module.
|
||||||
Module(Module<'a>),
|
Module(Module<'a>),
|
||||||
|
|
||||||
/// This "virtual module" denotes either resolution in extern prelude
|
/// Virtual module that denotes resolution in extern prelude.
|
||||||
/// for paths starting with `::` on 2018 edition or `extern::`,
|
/// Used for paths starting with `::` on 2018 edition or `extern::`.
|
||||||
/// or resolution in current scope for single-segment imports.
|
ExternPrelude,
|
||||||
UniformRoot(UniformRootKind),
|
|
||||||
|
/// Virtual module that denotes resolution in current scope.
|
||||||
|
/// Used only for resolving single-segment imports. The reason it exists is that import paths
|
||||||
|
/// are always split into two parts, the first of which should be some kind of module.
|
||||||
|
CurrentScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PartialEq for ModuleOrUniformRoot<'a> {
|
impl<'a> PartialEq for ModuleOrUniformRoot<'a> {
|
||||||
|
@ -1020,8 +1023,8 @@ impl<'a> PartialEq for ModuleOrUniformRoot<'a> {
|
||||||
match (*self, *other) {
|
match (*self, *other) {
|
||||||
(ModuleOrUniformRoot::Module(lhs), ModuleOrUniformRoot::Module(rhs)) =>
|
(ModuleOrUniformRoot::Module(lhs), ModuleOrUniformRoot::Module(rhs)) =>
|
||||||
ptr::eq(lhs, rhs),
|
ptr::eq(lhs, rhs),
|
||||||
(ModuleOrUniformRoot::UniformRoot(lhs), ModuleOrUniformRoot::UniformRoot(rhs)) =>
|
(ModuleOrUniformRoot::ExternPrelude, ModuleOrUniformRoot::ExternPrelude) => true,
|
||||||
lhs == rhs,
|
(ModuleOrUniformRoot::CurrentScope, ModuleOrUniformRoot::CurrentScope) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1758,8 +1761,7 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> {
|
||||||
error_callback(self, span, ResolutionError::FailedToResolve(msg));
|
error_callback(self, span, ResolutionError::FailedToResolve(msg));
|
||||||
Def::Err
|
Def::Err
|
||||||
}
|
}
|
||||||
PathResult::Module(ModuleOrUniformRoot::UniformRoot(_)) |
|
PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
|
||||||
PathResult::Indeterminate => unreachable!(),
|
|
||||||
PathResult::Failed(span, msg, _) => {
|
PathResult::Failed(span, msg, _) => {
|
||||||
error_callback(self, span, ResolutionError::FailedToResolve(&msg));
|
error_callback(self, span, ResolutionError::FailedToResolve(&msg));
|
||||||
Def::Err
|
Def::Err
|
||||||
|
@ -2220,11 +2222,11 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
|
||||||
self.current_module = self.macro_def_scope(def);
|
self.current_module = self.macro_def_scope(def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ModuleOrUniformRoot::UniformRoot(UniformRootKind::ExternPrelude) => {
|
ModuleOrUniformRoot::ExternPrelude => {
|
||||||
ident.span = ident.span.modern();
|
ident.span = ident.span.modern();
|
||||||
ident.span.adjust(Mark::root());
|
ident.span.adjust(Mark::root());
|
||||||
}
|
}
|
||||||
ModuleOrUniformRoot::UniformRoot(UniformRootKind::CurrentScope) => {
|
ModuleOrUniformRoot::CurrentScope => {
|
||||||
// No adjustments
|
// No adjustments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3667,8 +3669,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
|
||||||
resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
|
resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
|
||||||
err_path_resolution()
|
err_path_resolution()
|
||||||
}
|
}
|
||||||
PathResult::Module(ModuleOrUniformRoot::UniformRoot(_)) |
|
PathResult::Module(..) | PathResult::Failed(..) => return None,
|
||||||
PathResult::Failed(..) => return None,
|
|
||||||
PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"),
|
PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3787,8 +3788,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
|
||||||
}
|
}
|
||||||
if name == keywords::Extern.name() ||
|
if name == keywords::Extern.name() ||
|
||||||
name == keywords::CrateRoot.name() && ident.span.rust_2018() {
|
name == keywords::CrateRoot.name() && ident.span.rust_2018() {
|
||||||
module =
|
module = Some(ModuleOrUniformRoot::ExternPrelude);
|
||||||
Some(ModuleOrUniformRoot::UniformRoot(UniformRootKind::ExternPrelude));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if name == keywords::CrateRoot.name() ||
|
if name == keywords::CrateRoot.name() ||
|
||||||
|
@ -3821,9 +3821,9 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
|
||||||
self.resolve_ident_in_module(module, ident, ns, None, 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) {
|
} else if opt_ns.is_none() || opt_ns == Some(MacroNS) {
|
||||||
assert!(ns == TypeNS);
|
assert!(ns == TypeNS);
|
||||||
self.early_resolve_ident_in_lexical_scope(ident, ns, None, opt_ns.is_none(),
|
let scopes = if opt_ns.is_none() { ScopeSet::Import(ns) } else { ScopeSet::Module };
|
||||||
parent_scope, record_used, record_used,
|
self.early_resolve_ident_in_lexical_scope(ident, scopes, parent_scope, record_used,
|
||||||
path_span)
|
record_used, path_span)
|
||||||
} else {
|
} else {
|
||||||
let record_used_id =
|
let record_used_id =
|
||||||
if record_used { crate_lint.node_id().or(Some(CRATE_NODE_ID)) } else { None };
|
if record_used { crate_lint.node_id().or(Some(CRATE_NODE_ID)) } else { None };
|
||||||
|
@ -3912,8 +3912,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
|
||||||
|
|
||||||
PathResult::Module(match module {
|
PathResult::Module(match module {
|
||||||
Some(module) => module,
|
Some(module) => module,
|
||||||
None if path.is_empty() =>
|
None if path.is_empty() => ModuleOrUniformRoot::CurrentScope,
|
||||||
ModuleOrUniformRoot::UniformRoot(UniformRootKind::CurrentScope),
|
|
||||||
_ => span_bug!(path_span, "resolve_path: non-empty path `{:?}` has no module", path),
|
_ => span_bug!(path_span, "resolve_path: non-empty path `{:?}` has no module", path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
|
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
|
||||||
use {CrateLint, Resolver, ResolutionError, Segment, Weak};
|
use {CrateLint, Resolver, ResolutionError, ScopeSet, Weak};
|
||||||
use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, ToNameBinding};
|
use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
|
||||||
use {is_known_tool, resolve_error};
|
use {is_known_tool, resolve_error};
|
||||||
use ModuleOrUniformRoot;
|
use ModuleOrUniformRoot;
|
||||||
use Namespace::{self, *};
|
use Namespace::*;
|
||||||
use build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport};
|
use build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport};
|
||||||
use resolve_imports::ImportResolver;
|
use resolve_imports::ImportResolver;
|
||||||
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX, DefIndex,
|
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX, DefIndex,
|
||||||
|
@ -502,7 +502,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||||
def
|
def
|
||||||
} else {
|
} else {
|
||||||
let binding = self.early_resolve_ident_in_lexical_scope(
|
let binding = self.early_resolve_ident_in_lexical_scope(
|
||||||
path[0].ident, MacroNS, Some(kind), false, parent_scope, false, force, path_span
|
path[0].ident, ScopeSet::Macro(kind), parent_scope, false, force, path_span
|
||||||
);
|
);
|
||||||
match binding {
|
match binding {
|
||||||
Ok(..) => {}
|
Ok(..) => {}
|
||||||
|
@ -527,9 +527,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||||
crate fn early_resolve_ident_in_lexical_scope(
|
crate fn early_resolve_ident_in_lexical_scope(
|
||||||
&mut self,
|
&mut self,
|
||||||
orig_ident: Ident,
|
orig_ident: Ident,
|
||||||
ns: Namespace,
|
scope_set: ScopeSet,
|
||||||
macro_kind: Option<MacroKind>,
|
|
||||||
is_import: bool,
|
|
||||||
parent_scope: &ParentScope<'a>,
|
parent_scope: &ParentScope<'a>,
|
||||||
record_used: bool,
|
record_used: bool,
|
||||||
force: bool,
|
force: bool,
|
||||||
|
@ -605,8 +603,6 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(force || !record_used); // `record_used` implies `force`
|
assert!(force || !record_used); // `record_used` implies `force`
|
||||||
assert!(macro_kind.is_none() || !is_import); // `is_import` implies no macro kind
|
|
||||||
let rust_2015 = orig_ident.span.rust_2015();
|
|
||||||
let mut ident = orig_ident.modern();
|
let mut ident = orig_ident.modern();
|
||||||
|
|
||||||
// Make sure `self`, `super` etc produce an error when passed to here.
|
// Make sure `self`, `super` etc produce an error when passed to here.
|
||||||
|
@ -628,6 +624,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||||
let mut innermost_result: Option<(&NameBinding, Flags)> = None;
|
let mut innermost_result: Option<(&NameBinding, Flags)> = None;
|
||||||
|
|
||||||
// Go through all the scopes and try to resolve the name.
|
// Go through all the scopes and try to resolve the name.
|
||||||
|
let rust_2015 = orig_ident.span.rust_2015();
|
||||||
|
let (ns, macro_kind, is_import) = match scope_set {
|
||||||
|
ScopeSet::Import(ns) => (ns, None, true),
|
||||||
|
ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false),
|
||||||
|
ScopeSet::Module => (TypeNS, None, false),
|
||||||
|
};
|
||||||
let mut where_to_resolve = match ns {
|
let mut where_to_resolve = match ns {
|
||||||
_ if is_import && rust_2015 => WhereToResolve::CrateRoot,
|
_ if is_import && rust_2015 => WhereToResolve::CrateRoot,
|
||||||
TypeNS | ValueNS => WhereToResolve::Module(parent_scope.module),
|
TypeNS | ValueNS => WhereToResolve::Module(parent_scope.module),
|
||||||
|
@ -1041,7 +1043,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||||
let macro_resolutions =
|
let macro_resolutions =
|
||||||
mem::replace(&mut *module.single_segment_macro_resolutions.borrow_mut(), Vec::new());
|
mem::replace(&mut *module.single_segment_macro_resolutions.borrow_mut(), Vec::new());
|
||||||
for (ident, kind, parent_scope, initial_binding) in macro_resolutions {
|
for (ident, kind, parent_scope, initial_binding) in macro_resolutions {
|
||||||
match self.early_resolve_ident_in_lexical_scope(ident, MacroNS, Some(kind), false,
|
match self.early_resolve_ident_in_lexical_scope(ident, ScopeSet::Macro(kind),
|
||||||
&parent_scope, true, true, ident.span) {
|
&parent_scope, true, true, ident.span) {
|
||||||
Ok(binding) => {
|
Ok(binding) => {
|
||||||
let initial_def = initial_binding.map(|initial_binding| {
|
let initial_def = initial_binding.map(|initial_binding| {
|
||||||
|
@ -1067,7 +1069,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
|
||||||
let builtin_attrs = mem::replace(&mut *module.builtin_attrs.borrow_mut(), Vec::new());
|
let builtin_attrs = mem::replace(&mut *module.builtin_attrs.borrow_mut(), Vec::new());
|
||||||
for (ident, parent_scope) in builtin_attrs {
|
for (ident, parent_scope) in builtin_attrs {
|
||||||
let _ = self.early_resolve_ident_in_lexical_scope(
|
let _ = self.early_resolve_ident_in_lexical_scope(
|
||||||
ident, MacroNS, Some(MacroKind::Attr), false, &parent_scope, true, true, ident.span
|
ident, ScopeSet::Macro(MacroKind::Attr), &parent_scope, true, true, ident.span
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
use self::ImportDirectiveSubclass::*;
|
use self::ImportDirectiveSubclass::*;
|
||||||
|
|
||||||
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
|
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
|
||||||
use {CrateLint, Module, ModuleOrUniformRoot, PerNS, UniformRootKind, Weak};
|
use {CrateLint, Module, ModuleOrUniformRoot, PerNS, ScopeSet, Weak};
|
||||||
use Namespace::{self, TypeNS, MacroNS};
|
use Namespace::{self, TypeNS, MacroNS};
|
||||||
use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
|
use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
|
||||||
use {Resolver, Segment};
|
use {Resolver, Segment};
|
||||||
|
@ -162,45 +162,42 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> {
|
||||||
) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
|
) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
|
||||||
let module = match module {
|
let module = match module {
|
||||||
ModuleOrUniformRoot::Module(module) => module,
|
ModuleOrUniformRoot::Module(module) => module,
|
||||||
ModuleOrUniformRoot::UniformRoot(uniform_root_kind) => {
|
ModuleOrUniformRoot::ExternPrelude => {
|
||||||
assert!(!restricted_shadowing);
|
assert!(!restricted_shadowing);
|
||||||
match uniform_root_kind {
|
return if let Some(binding) = self.extern_prelude_get(ident, !record_used) {
|
||||||
UniformRootKind::ExternPrelude => {
|
Ok(binding)
|
||||||
return if let Some(binding) = self.extern_prelude_get(ident, !record_used) {
|
} else if !self.graph_root.unresolved_invocations.borrow().is_empty() {
|
||||||
Ok(binding)
|
// Macro-expanded `extern crate` items can add names to extern prelude.
|
||||||
} else if !self.graph_root.unresolved_invocations.borrow().is_empty() {
|
Err((Undetermined, Weak::No))
|
||||||
// Macro-expanded `extern crate` items can add names to extern prelude.
|
} else {
|
||||||
Err((Undetermined, Weak::No))
|
Err((Determined, Weak::No))
|
||||||
} else {
|
}
|
||||||
Err((Determined, Weak::No))
|
}
|
||||||
}
|
ModuleOrUniformRoot::CurrentScope => {
|
||||||
}
|
assert!(!restricted_shadowing);
|
||||||
UniformRootKind::CurrentScope => {
|
let parent_scope =
|
||||||
let parent_scope =
|
parent_scope.expect("no parent scope for a single-segment import");
|
||||||
parent_scope.expect("no parent scope for a single-segment import");
|
|
||||||
|
|
||||||
if ns == TypeNS {
|
if ns == TypeNS {
|
||||||
if ident.name == keywords::Crate.name() ||
|
if ident.name == keywords::Crate.name() ||
|
||||||
ident.name == keywords::DollarCrate.name() {
|
ident.name == keywords::DollarCrate.name() {
|
||||||
let module = self.resolve_crate_root(ident);
|
let module = self.resolve_crate_root(ident);
|
||||||
let binding = (module, ty::Visibility::Public,
|
let binding = (module, ty::Visibility::Public,
|
||||||
module.span, Mark::root())
|
module.span, Mark::root())
|
||||||
.to_name_binding(self.arenas);
|
.to_name_binding(self.arenas);
|
||||||
return Ok(binding);
|
return Ok(binding);
|
||||||
} else if ident.name == keywords::Super.name() ||
|
} else if ident.name == keywords::Super.name() ||
|
||||||
ident.name == keywords::SelfValue.name() {
|
ident.name == keywords::SelfValue.name() {
|
||||||
// FIXME: Implement these with renaming requirements so that e.g.
|
// FIXME: Implement these with renaming requirements so that e.g.
|
||||||
// `use super;` doesn't work, but `use super as name;` does.
|
// `use super;` doesn't work, but `use super as name;` does.
|
||||||
// Fall through here to get an error from `early_resolve_...`.
|
// Fall through here to get an error from `early_resolve_...`.
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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| (determinacy, Weak::No));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let binding = self.early_resolve_ident_in_lexical_scope(
|
||||||
|
ident, ScopeSet::Import(ns), parent_scope, record_used, record_used, path_span
|
||||||
|
);
|
||||||
|
return binding.map_err(|determinacy| (determinacy, Weak::No));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -333,7 +330,7 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> {
|
||||||
}
|
}
|
||||||
let module = match glob_import.imported_module.get() {
|
let module = match glob_import.imported_module.get() {
|
||||||
Some(ModuleOrUniformRoot::Module(module)) => module,
|
Some(ModuleOrUniformRoot::Module(module)) => module,
|
||||||
Some(ModuleOrUniformRoot::UniformRoot(_)) => continue,
|
Some(_) => continue,
|
||||||
None => return Err((Undetermined, Weak::Yes)),
|
None => return Err((Undetermined, Weak::Yes)),
|
||||||
};
|
};
|
||||||
let (orig_current_module, mut ident) = (self.current_module, ident.modern());
|
let (orig_current_module, mut ident) = (self.current_module, ident.modern());
|
||||||
|
@ -966,9 +963,8 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
|
||||||
|
|
||||||
return if all_ns_failed {
|
return if all_ns_failed {
|
||||||
let resolutions = match module {
|
let resolutions = match module {
|
||||||
ModuleOrUniformRoot::Module(module) =>
|
ModuleOrUniformRoot::Module(module) => Some(module.resolutions.borrow()),
|
||||||
Some(module.resolutions.borrow()),
|
_ => None,
|
||||||
ModuleOrUniformRoot::UniformRoot(_) => None,
|
|
||||||
};
|
};
|
||||||
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
|
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
|
||||||
let names = resolutions.filter_map(|(&(ref i, _), resolution)| {
|
let names = resolutions.filter_map(|(&(ref i, _), resolution)| {
|
||||||
|
@ -1006,7 +1002,7 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
|
||||||
format!("no `{}` in the root{}", ident, lev_suggestion)
|
format!("no `{}` in the root{}", ident, lev_suggestion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ModuleOrUniformRoot::UniformRoot(_) => {
|
_ => {
|
||||||
if !ident.is_path_segment_keyword() {
|
if !ident.is_path_segment_keyword() {
|
||||||
format!("no `{}` external crate{}", ident, lev_suggestion)
|
format!("no `{}` external crate{}", ident, lev_suggestion)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1107,9 +1103,8 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
|
||||||
fn resolve_glob_import(&mut self, directive: &'b ImportDirective<'b>) {
|
fn resolve_glob_import(&mut self, directive: &'b ImportDirective<'b>) {
|
||||||
let module = match directive.imported_module.get().unwrap() {
|
let module = match directive.imported_module.get().unwrap() {
|
||||||
ModuleOrUniformRoot::Module(module) => module,
|
ModuleOrUniformRoot::Module(module) => module,
|
||||||
ModuleOrUniformRoot::UniformRoot(_) => {
|
_ => {
|
||||||
self.session.span_err(directive.span,
|
self.session.span_err(directive.span, "cannot glob-import all possible crates");
|
||||||
"cannot glob-import all possible crates");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue