mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 03:54:42 +00:00 
			
		
		
		
	Merge pull request #19405 from ChayimFriedman2/no-expand
minor: Remove unnecessary expansions
This commit is contained in:
		
						commit
						8316ffeab1
					
				
					 2 changed files with 31 additions and 68 deletions
				
			
		|  | @ -1388,7 +1388,7 @@ impl<'db> SemanticsImpl<'db> { | ||||||
| 
 | 
 | ||||||
|         let (mut source_ty, _) = analyzer.type_of_expr(self.db, expr)?; |         let (mut source_ty, _) = analyzer.type_of_expr(self.db, expr)?; | ||||||
| 
 | 
 | ||||||
|         analyzer.expr_adjustments(self.db, expr).map(|it| { |         analyzer.expr_adjustments(expr).map(|it| { | ||||||
|             it.iter() |             it.iter() | ||||||
|                 .map(|adjust| { |                 .map(|adjust| { | ||||||
|                     let target = |                     let target = | ||||||
|  | @ -1521,7 +1521,7 @@ impl<'db> SemanticsImpl<'db> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Either<Field, TupleField>> { |     pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Either<Field, TupleField>> { | ||||||
|         self.analyze(field.syntax())?.resolve_field(self.db, field) |         self.analyze(field.syntax())?.resolve_field(field) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn resolve_field_fallback( |     pub fn resolve_field_fallback( | ||||||
|  | @ -1664,7 +1664,7 @@ impl<'db> SemanticsImpl<'db> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { |     fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { | ||||||
|         self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit) |         self.analyze(record_lit.syntax())?.resolve_variant(record_lit) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> { |     pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> { | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ use hir_def::{ | ||||||
|     type_ref::{Mutability, TypesMap, TypesSourceMap}, |     type_ref::{Mutability, TypesMap, TypesSourceMap}, | ||||||
| }; | }; | ||||||
| use hir_expand::{ | use hir_expand::{ | ||||||
|     HirFileId, InFile, InMacroFile, MacroFileId, MacroFileIdExt, |     HirFileId, InFile, MacroFileId, MacroFileIdExt, | ||||||
|     mod_path::path, |     mod_path::path, | ||||||
|     name::{AsName, Name}, |     name::{AsName, Name}, | ||||||
| }; | }; | ||||||
|  | @ -129,13 +129,8 @@ impl SourceAnalyzer { | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprOrPatId> { |     fn expr_id(&self, expr: ast::Expr) -> Option<ExprOrPatId> { | ||||||
|         let src = match expr { |         let src = InFile::new(self.file_id, expr); | ||||||
|             ast::Expr::MacroExpr(expr) => { |  | ||||||
|                 self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?.into() |  | ||||||
|             } |  | ||||||
|             _ => InFile::new(self.file_id, expr.clone()), |  | ||||||
|         }; |  | ||||||
|         let sm = self.body_source_map()?; |         let sm = self.body_source_map()?; | ||||||
|         sm.node_expr(src.as_ref()) |         sm.node_expr(src.as_ref()) | ||||||
|     } |     } | ||||||
|  | @ -151,37 +146,10 @@ impl SourceAnalyzer { | ||||||
|         if let Pat::Bind { id, .. } = self.body()?.pats[pat_id.as_pat()?] { Some(id) } else { None } |         if let Pat::Bind { id, .. } = self.body()?.pats[pat_id.as_pat()?] { Some(id) } else { None } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn expand_expr( |     pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment]> { | ||||||
|         &self, |  | ||||||
|         db: &dyn HirDatabase, |  | ||||||
|         expr: InFile<ast::MacroCall>, |  | ||||||
|     ) -> Option<InMacroFile<ast::Expr>> { |  | ||||||
|         let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?; |  | ||||||
|         let expanded = db.parse_macro_expansion(macro_file).value.0.syntax_node(); |  | ||||||
|         let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) { |  | ||||||
|             match stmts.expr()? { |  | ||||||
|                 ast::Expr::MacroExpr(mac) => { |  | ||||||
|                     self.expand_expr(db, InFile::new(macro_file.into(), mac.macro_call()?))? |  | ||||||
|                 } |  | ||||||
|                 expr => InMacroFile::new(macro_file, expr), |  | ||||||
|             } |  | ||||||
|         } else if let Some(call) = ast::MacroCall::cast(expanded.clone()) { |  | ||||||
|             self.expand_expr(db, InFile::new(macro_file.into(), call))? |  | ||||||
|         } else { |  | ||||||
|             InMacroFile::new(macro_file, ast::Expr::cast(expanded)?) |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         Some(res) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub(crate) fn expr_adjustments( |  | ||||||
|         &self, |  | ||||||
|         db: &dyn HirDatabase, |  | ||||||
|         expr: &ast::Expr, |  | ||||||
|     ) -> Option<&[Adjustment]> { |  | ||||||
|         // It is safe to omit destructuring assignments here because they have no adjustments (neither
 |         // It is safe to omit destructuring assignments here because they have no adjustments (neither
 | ||||||
|         // expressions nor patterns).
 |         // expressions nor patterns).
 | ||||||
|         let expr_id = self.expr_id(db, expr)?.as_expr()?; |         let expr_id = self.expr_id(expr.clone())?.as_expr()?; | ||||||
|         let infer = self.infer.as_ref()?; |         let infer = self.infer.as_ref()?; | ||||||
|         infer.expr_adjustments.get(&expr_id).map(|v| &**v) |         infer.expr_adjustments.get(&expr_id).map(|v| &**v) | ||||||
|     } |     } | ||||||
|  | @ -191,7 +159,7 @@ impl SourceAnalyzer { | ||||||
|         db: &dyn HirDatabase, |         db: &dyn HirDatabase, | ||||||
|         expr: &ast::Expr, |         expr: &ast::Expr, | ||||||
|     ) -> Option<(Type, Option<Type>)> { |     ) -> Option<(Type, Option<Type>)> { | ||||||
|         let expr_id = self.expr_id(db, expr)?; |         let expr_id = self.expr_id(expr.clone())?; | ||||||
|         let infer = self.infer.as_ref()?; |         let infer = self.infer.as_ref()?; | ||||||
|         let coerced = expr_id |         let coerced = expr_id | ||||||
|             .as_expr() |             .as_expr() | ||||||
|  | @ -284,7 +252,7 @@ impl SourceAnalyzer { | ||||||
|         db: &dyn HirDatabase, |         db: &dyn HirDatabase, | ||||||
|         call: &ast::MethodCallExpr, |         call: &ast::MethodCallExpr, | ||||||
|     ) -> Option<Callable> { |     ) -> Option<Callable> { | ||||||
|         let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?; |         let expr_id = self.expr_id(call.clone().into())?.as_expr()?; | ||||||
|         let (func, substs) = self.infer.as_ref()?.method_resolution(expr_id)?; |         let (func, substs) = self.infer.as_ref()?.method_resolution(expr_id)?; | ||||||
|         let ty = db.value_ty(func.into())?.substitute(Interner, &substs); |         let ty = db.value_ty(func.into())?.substitute(Interner, &substs); | ||||||
|         let ty = Type::new_with_resolver(db, &self.resolver, ty); |         let ty = Type::new_with_resolver(db, &self.resolver, ty); | ||||||
|  | @ -298,7 +266,7 @@ impl SourceAnalyzer { | ||||||
|         db: &dyn HirDatabase, |         db: &dyn HirDatabase, | ||||||
|         call: &ast::MethodCallExpr, |         call: &ast::MethodCallExpr, | ||||||
|     ) -> Option<Function> { |     ) -> Option<Function> { | ||||||
|         let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?; |         let expr_id = self.expr_id(call.clone().into())?.as_expr()?; | ||||||
|         let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?; |         let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?; | ||||||
| 
 | 
 | ||||||
|         Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into()) |         Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into()) | ||||||
|  | @ -309,7 +277,7 @@ impl SourceAnalyzer { | ||||||
|         db: &dyn HirDatabase, |         db: &dyn HirDatabase, | ||||||
|         call: &ast::MethodCallExpr, |         call: &ast::MethodCallExpr, | ||||||
|     ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> { |     ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> { | ||||||
|         let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?; |         let expr_id = self.expr_id(call.clone().into())?.as_expr()?; | ||||||
|         let inference_result = self.infer.as_ref()?; |         let inference_result = self.infer.as_ref()?; | ||||||
|         match inference_result.method_resolution(expr_id) { |         match inference_result.method_resolution(expr_id) { | ||||||
|             Some((f_in_trait, substs)) => { |             Some((f_in_trait, substs)) => { | ||||||
|  | @ -339,11 +307,10 @@ impl SourceAnalyzer { | ||||||
| 
 | 
 | ||||||
|     pub(crate) fn resolve_field( |     pub(crate) fn resolve_field( | ||||||
|         &self, |         &self, | ||||||
|         db: &dyn HirDatabase, |  | ||||||
|         field: &ast::FieldExpr, |         field: &ast::FieldExpr, | ||||||
|     ) -> Option<Either<Field, TupleField>> { |     ) -> Option<Either<Field, TupleField>> { | ||||||
|         let &(def, ..) = self.def.as_ref()?; |         let &(def, ..) = self.def.as_ref()?; | ||||||
|         let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?; |         let expr_id = self.expr_id(field.clone().into())?.as_expr()?; | ||||||
|         self.infer.as_ref()?.field_resolution(expr_id).map(|it| { |         self.infer.as_ref()?.field_resolution(expr_id).map(|it| { | ||||||
|             it.map_either(Into::into, |f| TupleField { owner: def, tuple: f.tuple, index: f.index }) |             it.map_either(Into::into, |f| TupleField { owner: def, tuple: f.tuple, index: f.index }) | ||||||
|         }) |         }) | ||||||
|  | @ -373,7 +340,7 @@ impl SourceAnalyzer { | ||||||
|         field: &ast::FieldExpr, |         field: &ast::FieldExpr, | ||||||
|     ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> { |     ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> { | ||||||
|         let &(def, ..) = self.def.as_ref()?; |         let &(def, ..) = self.def.as_ref()?; | ||||||
|         let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?; |         let expr_id = self.expr_id(field.clone().into())?.as_expr()?; | ||||||
|         let inference_result = self.infer.as_ref()?; |         let inference_result = self.infer.as_ref()?; | ||||||
|         match inference_result.field_resolution(expr_id) { |         match inference_result.field_resolution(expr_id) { | ||||||
|             Some(field) => match field { |             Some(field) => match field { | ||||||
|  | @ -444,7 +411,7 @@ impl SourceAnalyzer { | ||||||
|         db: &dyn HirDatabase, |         db: &dyn HirDatabase, | ||||||
|         await_expr: &ast::AwaitExpr, |         await_expr: &ast::AwaitExpr, | ||||||
|     ) -> Option<FunctionId> { |     ) -> Option<FunctionId> { | ||||||
|         let mut ty = self.ty_of_expr(db, &await_expr.expr()?)?.clone(); |         let mut ty = self.ty_of_expr(await_expr.expr()?)?.clone(); | ||||||
| 
 | 
 | ||||||
|         let into_future_trait = self |         let into_future_trait = self | ||||||
|             .resolver |             .resolver | ||||||
|  | @ -494,7 +461,7 @@ impl SourceAnalyzer { | ||||||
|                 self.infer |                 self.infer | ||||||
|                     .as_ref() |                     .as_ref() | ||||||
|                     .and_then(|infer| { |                     .and_then(|infer| { | ||||||
|                         let expr = self.expr_id(db, &prefix_expr.clone().into())?.as_expr()?; |                         let expr = self.expr_id(prefix_expr.clone().into())?.as_expr()?; | ||||||
|                         let (func, _) = infer.method_resolution(expr)?; |                         let (func, _) = infer.method_resolution(expr)?; | ||||||
|                         let (deref_mut_trait, deref_mut) = self.lang_trait_fn( |                         let (deref_mut_trait, deref_mut) = self.lang_trait_fn( | ||||||
|                             db, |                             db, | ||||||
|  | @ -513,7 +480,7 @@ impl SourceAnalyzer { | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let ty = self.ty_of_expr(db, &prefix_expr.expr()?)?; |         let ty = self.ty_of_expr(prefix_expr.expr()?)?; | ||||||
| 
 | 
 | ||||||
|         // HACK: subst for all methods coincides with that for their trait because the methods
 |         // HACK: subst for all methods coincides with that for their trait because the methods
 | ||||||
|         // don't have any generic parameters, so we skip building another subst for the methods.
 |         // don't have any generic parameters, so we skip building another subst for the methods.
 | ||||||
|  | @ -527,8 +494,8 @@ impl SourceAnalyzer { | ||||||
|         db: &dyn HirDatabase, |         db: &dyn HirDatabase, | ||||||
|         index_expr: &ast::IndexExpr, |         index_expr: &ast::IndexExpr, | ||||||
|     ) -> Option<FunctionId> { |     ) -> Option<FunctionId> { | ||||||
|         let base_ty = self.ty_of_expr(db, &index_expr.base()?)?; |         let base_ty = self.ty_of_expr(index_expr.base()?)?; | ||||||
|         let index_ty = self.ty_of_expr(db, &index_expr.index()?)?; |         let index_ty = self.ty_of_expr(index_expr.index()?)?; | ||||||
| 
 | 
 | ||||||
|         let (index_trait, index_fn) = |         let (index_trait, index_fn) = | ||||||
|             self.lang_trait_fn(db, LangItem::Index, &Name::new_symbol_root(sym::index.clone()))?; |             self.lang_trait_fn(db, LangItem::Index, &Name::new_symbol_root(sym::index.clone()))?; | ||||||
|  | @ -536,7 +503,7 @@ impl SourceAnalyzer { | ||||||
|             .infer |             .infer | ||||||
|             .as_ref() |             .as_ref() | ||||||
|             .and_then(|infer| { |             .and_then(|infer| { | ||||||
|                 let expr = self.expr_id(db, &index_expr.clone().into())?.as_expr()?; |                 let expr = self.expr_id(index_expr.clone().into())?.as_expr()?; | ||||||
|                 let (func, _) = infer.method_resolution(expr)?; |                 let (func, _) = infer.method_resolution(expr)?; | ||||||
|                 let (index_mut_trait, index_mut_fn) = self.lang_trait_fn( |                 let (index_mut_trait, index_mut_fn) = self.lang_trait_fn( | ||||||
|                     db, |                     db, | ||||||
|  | @ -561,8 +528,8 @@ impl SourceAnalyzer { | ||||||
|         binop_expr: &ast::BinExpr, |         binop_expr: &ast::BinExpr, | ||||||
|     ) -> Option<FunctionId> { |     ) -> Option<FunctionId> { | ||||||
|         let op = binop_expr.op_kind()?; |         let op = binop_expr.op_kind()?; | ||||||
|         let lhs = self.ty_of_expr(db, &binop_expr.lhs()?)?; |         let lhs = self.ty_of_expr(binop_expr.lhs()?)?; | ||||||
|         let rhs = self.ty_of_expr(db, &binop_expr.rhs()?)?; |         let rhs = self.ty_of_expr(binop_expr.rhs()?)?; | ||||||
| 
 | 
 | ||||||
|         let (op_trait, op_fn) = lang_items_for_bin_op(op) |         let (op_trait, op_fn) = lang_items_for_bin_op(op) | ||||||
|             .and_then(|(name, lang_item)| self.lang_trait_fn(db, lang_item, &name))?; |             .and_then(|(name, lang_item)| self.lang_trait_fn(db, lang_item, &name))?; | ||||||
|  | @ -581,7 +548,7 @@ impl SourceAnalyzer { | ||||||
|         db: &dyn HirDatabase, |         db: &dyn HirDatabase, | ||||||
|         try_expr: &ast::TryExpr, |         try_expr: &ast::TryExpr, | ||||||
|     ) -> Option<FunctionId> { |     ) -> Option<FunctionId> { | ||||||
|         let ty = self.ty_of_expr(db, &try_expr.expr()?)?; |         let ty = self.ty_of_expr(try_expr.expr()?)?; | ||||||
| 
 | 
 | ||||||
|         let op_fn = db.lang_item(self.resolver.krate(), LangItem::TryTraitBranch)?.as_function()?; |         let op_fn = db.lang_item(self.resolver.krate(), LangItem::TryTraitBranch)?.as_function()?; | ||||||
|         let op_trait = match op_fn.lookup(db.upcast()).container { |         let op_trait = match op_fn.lookup(db.upcast()).container { | ||||||
|  | @ -721,7 +688,7 @@ impl SourceAnalyzer { | ||||||
|         let resolved = (|| { |         let resolved = (|| { | ||||||
|             let infer = self.infer.as_deref()?; |             let infer = self.infer.as_deref()?; | ||||||
|             if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { |             if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { | ||||||
|                 let expr_id = self.expr_id(db, &path_expr.into())?; |                 let expr_id = self.expr_id(path_expr.into())?; | ||||||
|                 if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) { |                 if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) { | ||||||
|                     let (assoc, subst) = match assoc { |                     let (assoc, subst) = match assoc { | ||||||
|                         AssocItemId::FunctionId(f_in_trait) => { |                         AssocItemId::FunctionId(f_in_trait) => { | ||||||
|  | @ -819,7 +786,7 @@ impl SourceAnalyzer { | ||||||
|                     return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); |                     return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); | ||||||
|                 } |                 } | ||||||
|             } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) { |             } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) { | ||||||
|                 let expr_id = self.expr_id(db, &rec_lit.into())?; |                 let expr_id = self.expr_id(rec_lit.into())?; | ||||||
|                 if let Some(VariantId::EnumVariantId(variant)) = |                 if let Some(VariantId::EnumVariantId(variant)) = | ||||||
|                     infer.variant_resolution_for_expr_or_pat(expr_id) |                     infer.variant_resolution_for_expr_or_pat(expr_id) | ||||||
|                 { |                 { | ||||||
|  | @ -1010,7 +977,7 @@ impl SourceAnalyzer { | ||||||
|             let subst = (|| { |             let subst = (|| { | ||||||
|                 let parent = parent()?; |                 let parent = parent()?; | ||||||
|                 let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) { |                 let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) { | ||||||
|                     let expr_id = self.expr_id(db, &expr)?; |                     let expr_id = self.expr_id(expr)?; | ||||||
|                     self.infer.as_ref()?.type_of_expr_or_pat(expr_id)? |                     self.infer.as_ref()?.type_of_expr_or_pat(expr_id)? | ||||||
|                 } else if let Some(pat) = ast::Pat::cast(parent) { |                 } else if let Some(pat) = ast::Pat::cast(parent) { | ||||||
|                     let pat_id = self.pat_id(&pat)?; |                     let pat_id = self.pat_id(&pat)?; | ||||||
|  | @ -1064,7 +1031,7 @@ impl SourceAnalyzer { | ||||||
|         let body = self.body()?; |         let body = self.body()?; | ||||||
|         let infer = self.infer.as_ref()?; |         let infer = self.infer.as_ref()?; | ||||||
| 
 | 
 | ||||||
|         let expr_id = self.expr_id(db, &literal.clone().into())?; |         let expr_id = self.expr_id(literal.clone().into())?; | ||||||
|         let substs = infer[expr_id].as_adt()?.1; |         let substs = infer[expr_id].as_adt()?.1; | ||||||
| 
 | 
 | ||||||
|         let (variant, missing_fields, _exhaustive) = match expr_id { |         let (variant, missing_fields, _exhaustive) = match expr_id { | ||||||
|  | @ -1130,13 +1097,9 @@ impl SourceAnalyzer { | ||||||
|         Some(macro_call_id.as_macro_file()).filter(|it| it.expansion_level(db.upcast()) < 64) |         Some(macro_call_id.as_macro_file()).filter(|it| it.expansion_level(db.upcast()) < 64) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub(crate) fn resolve_variant( |     pub(crate) fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { | ||||||
|         &self, |  | ||||||
|         db: &dyn HirDatabase, |  | ||||||
|         record_lit: ast::RecordExpr, |  | ||||||
|     ) -> Option<VariantId> { |  | ||||||
|         let infer = self.infer.as_ref()?; |         let infer = self.infer.as_ref()?; | ||||||
|         let expr_id = self.expr_id(db, &record_lit.into())?; |         let expr_id = self.expr_id(record_lit.into())?; | ||||||
|         infer.variant_resolution_for_expr_or_pat(expr_id) |         infer.variant_resolution_for_expr_or_pat(expr_id) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1284,8 +1247,8 @@ impl SourceAnalyzer { | ||||||
|         Some((trait_id, fn_id)) |         Some((trait_id, fn_id)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { |     fn ty_of_expr(&self, expr: ast::Expr) -> Option<&Ty> { | ||||||
|         self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?) |         self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(expr.clone())?) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Wirth
						Lukas Wirth