1use crate::{
2 AsObject, Context, Py, PyAtomicRef, PyObject, PyObjectRef, PyResult, VirtualMachine,
3 anystr::SplitLinesArgs,
4 builtins::{
5 PyBaseExceptionRef, PyDict, PyDictRef, PyList, PyListRef, PyStr, PyStrInterned, PyStrRef,
6 PyTuple, PyTupleRef, PyType, PyTypeRef,
7 },
8 convert::TryFromObject,
9};
10use core::sync::atomic::{AtomicUsize, Ordering};
11use rustpython_common::lock::OnceCell;
12
13pub struct WarningsState {
14 pub filters: PyAtomicRef<PyList>,
15 pub once_registry: PyAtomicRef<PyDict>,
16 pub default_action: PyAtomicRef<PyStr>,
17 pub filters_version: AtomicUsize,
18 pub context_var: OnceCell<PyObjectRef>,
19 lock_count: AtomicUsize,
20}
21
22impl WarningsState {
23 fn create_default_filters(ctx: &Context) -> PyListRef {
24 ctx.new_list(vec![
26 ctx.new_tuple(vec![
27 ctx.new_str("default").into(),
28 ctx.none(),
29 ctx.exceptions.deprecation_warning.as_object().to_owned(),
30 ctx.new_str("__main__").into(),
31 ctx.new_int(0).into(),
32 ])
33 .into(),
34 ctx.new_tuple(vec![
35 ctx.new_str("ignore").into(),
36 ctx.none(),
37 ctx.exceptions.deprecation_warning.as_object().to_owned(),
38 ctx.none(),
39 ctx.new_int(0).into(),
40 ])
41 .into(),
42 ctx.new_tuple(vec![
43 ctx.new_str("ignore").into(),
44 ctx.none(),
45 ctx.exceptions
46 .pending_deprecation_warning
47 .as_object()
48 .to_owned(),
49 ctx.none(),
50 ctx.new_int(0).into(),
51 ])
52 .into(),
53 ctx.new_tuple(vec![
54 ctx.new_str("ignore").into(),
55 ctx.none(),
56 ctx.exceptions.import_warning.as_object().to_owned(),
57 ctx.none(),
58 ctx.new_int(0).into(),
59 ])
60 .into(),
61 ctx.new_tuple(vec![
62 ctx.new_str("ignore").into(),
63 ctx.none(),
64 ctx.exceptions.resource_warning.as_object().to_owned(),
65 ctx.none(),
66 ctx.new_int(0).into(),
67 ])
68 .into(),
69 ])
70 }
71
72 pub fn init_state(ctx: &Context) -> Self {
73 Self {
74 filters: Self::create_default_filters(ctx).into(),
75 once_registry: ctx.new_dict().into(),
76 default_action: ctx.new_str("default").into(),
77 filters_version: AtomicUsize::new(0),
78 context_var: OnceCell::new(),
79 lock_count: AtomicUsize::new(0),
80 }
81 }
82
83 pub fn acquire_lock(&self) {
84 self.lock_count.fetch_add(1, Ordering::SeqCst);
85 }
86
87 pub fn release_lock(&self) -> bool {
88 let prev = self.lock_count.load(Ordering::SeqCst);
89 if prev == 0 {
90 return false;
91 }
92 self.lock_count.fetch_sub(1, Ordering::SeqCst);
93 true
94 }
95
96 pub fn filters_mutated(&self) {
97 self.filters_version.fetch_add(1, Ordering::SeqCst);
98 }
99}
100
101fn check_matched(obj: &PyObject, arg: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
104 if vm.is_none(obj) {
105 return Ok(true);
106 }
107 if obj.class().is(vm.ctx.types.str_type) {
108 return obj.rich_compare_bool(arg, crate::types::PyComparisonOp::Eq, vm);
109 }
110 let result = vm.call_method(obj, "match", (arg.to_owned(),))?;
111 result.is_true(vm)
112}
113
114fn get_warnings_attr(
115 vm: &VirtualMachine,
116 attr_name: &'static PyStrInterned,
117 try_import: bool,
118) -> PyResult<Option<PyObjectRef>> {
119 let module = if try_import
120 && !vm
121 .state
122 .finalizing
123 .load(core::sync::atomic::Ordering::SeqCst)
124 {
125 match vm.import("warnings", 0) {
126 Ok(module) => module,
127 Err(e) if e.fast_isinstance(vm.ctx.exceptions.import_error) => return Ok(None),
128 Err(e) => return Err(e),
129 }
130 } else {
131 let Ok(modules) = vm.sys_module.get_attr(identifier!(vm, modules), vm) else {
132 return Ok(None);
133 };
134 match modules.get_item(vm.ctx.intern_str("warnings"), vm) {
135 Ok(module) => module,
136 Err(_) => return Ok(None),
137 }
138 };
139
140 vm.get_attribute_opt(&module, attr_name)
141}
142
143fn get_warnings_filters(vm: &VirtualMachine) -> PyResult<PyListRef> {
146 if let Some(filters_obj) = get_warnings_attr(vm, identifier!(vm, filters), false)? {
147 let filters = PyListRef::try_from_object(vm, filters_obj)
148 .map_err(|_| vm.new_value_error("_warnings.filters must be a list"))?;
149 vm.state
150 .warnings
151 .filters
152 .swap_to_temporary_refs(filters, vm);
153 }
154 Ok(vm.state.warnings.filters.to_owned())
155}
156
157fn get_warnings_context_filters(vm: &VirtualMachine) -> PyResult<Option<PyListRef>> {
158 let Some(ctx_var) = vm.state.warnings.context_var.get() else {
159 return Ok(None);
160 };
161 if vm.is_none(ctx_var) {
162 return Ok(None);
163 }
164 let ctx = vm.call_method(ctx_var, "get", (vm.ctx.none(),))?;
165 if vm.is_none(&ctx) {
166 return Ok(None);
167 }
168 let filters = ctx.get_attr("_filters", vm)?;
169 PyListRef::try_from_object(vm, filters)
170 .map(Some)
171 .map_err(|_| vm.new_value_error("_filters of warnings._warnings_context must be a list"))
172}
173
174fn bless_my_loader(
175 module_globals: &PyObject,
176 vm: &VirtualMachine,
177) -> PyResult<Option<PyObjectRef>> {
178 let external = vm
179 .importlib
180 .get_attr("_bootstrap_external", vm)
181 .or_else(|_| vm.import("importlib._bootstrap_external", 0))?;
182 if vm.is_none(&external) {
183 return Ok(None);
184 }
185 let bless = external.get_attr("_bless_my_loader", vm)?;
186 let loader = bless.call((module_globals.to_owned(),), vm)?;
187 if vm.is_none(&loader) {
188 Ok(None)
189 } else {
190 Ok(Some(loader))
191 }
192}
193
194pub(crate) fn get_source_line(
195 module_globals: &PyObject,
196 lineno: usize,
197 vm: &VirtualMachine,
198) -> PyResult<Option<PyObjectRef>> {
199 let Some(loader) = bless_my_loader(module_globals, vm)? else {
200 return Ok(None);
201 };
202
203 let module_name = if let Some(dict) = module_globals.downcast_ref::<PyDict>() {
204 match dict.get_item_opt(identifier!(vm, __name__), vm)? {
205 Some(name) => name,
206 None => return Ok(None),
207 }
208 } else {
209 match module_globals.get_item(identifier!(vm, __name__), vm) {
210 Ok(name) => name,
211 Err(_) => return Ok(None),
212 }
213 };
214
215 let Some(get_source) = vm.get_attribute_opt(&loader, vm.ctx.intern_str("get_source"))? else {
216 return Ok(None);
217 };
218 let source = get_source.call((module_name,), vm)?;
219 if vm.is_none(&source) {
220 return Ok(None);
221 }
222
223 let Some(source_str) = source.downcast_ref::<PyStr>() else {
224 return Err(vm.new_type_error(format!("expected str, not {}", source.class().name())));
225 };
226 let lines = source_str.splitlines(SplitLinesArgs { keepends: false }, vm);
227 if lineno == 0 {
228 return Err(vm.new_index_error("list index out of range"));
229 }
230 lines
231 .get(lineno - 1)
232 .cloned()
233 .ok_or_else(|| vm.new_index_error("list index out of range"))
234 .map(Some)
235}
236
237fn get_default_action(vm: &VirtualMachine) -> PyResult<PyObjectRef> {
240 if let Some(action) = get_warnings_attr(vm, identifier!(vm, defaultaction), false)? {
241 if !action.class().is(vm.ctx.types.str_type) {
242 return Err(vm.new_type_error(format!(
243 "_warnings.defaultaction must be a string, not '{}'",
244 action.class().name()
245 )));
246 }
247 let action = PyStrRef::try_from_object(vm, action)?;
248 vm.state
249 .warnings
250 .default_action
251 .swap_to_temporary_refs(action, vm);
252 }
253 Ok(vm.state.warnings.default_action.to_owned().into())
254}
255
256fn get_once_registry(vm: &VirtualMachine) -> PyResult<PyObjectRef> {
259 if let Some(registry) = get_warnings_attr(vm, identifier!(vm, onceregistry), false)? {
260 if !registry.class().is(vm.ctx.types.dict_type) {
261 return Err(vm.new_type_error(format!(
262 "_warnings.onceregistry must be a dict, not '{}'",
263 registry.class().name()
264 )));
265 }
266 let registry = PyDictRef::try_from_object(vm, registry)?;
267 vm.state
268 .warnings
269 .once_registry
270 .swap_to_temporary_refs(registry, vm);
271 }
272 Ok(vm.state.warnings.once_registry.to_owned().into())
273}
274
275fn already_warned(
276 registry: &PyObject,
277 key: &PyObject,
278 should_set: bool,
279 vm: &VirtualMachine,
280) -> PyResult<bool> {
281 if vm.is_none(registry) {
282 return Ok(false);
283 }
284
285 let current_version = vm.state.warnings.filters_version.load(Ordering::SeqCst);
286 let version_obj = registry.get_item(identifier!(&vm.ctx, version), vm).ok();
287
288 let version_matches = version_obj.as_ref().is_some_and(|v| {
289 v.try_int(vm)
290 .is_ok_and(|i| i.as_u32_mask() as usize == current_version)
291 });
292
293 if version_matches {
294 if let Ok(val) = registry.get_item(key, vm)
295 && val.is_true(vm)?
296 {
297 return Ok(true);
298 }
299 } else if let Ok(dict) = PyDictRef::try_from_object(vm, registry.to_owned()) {
300 dict.clear();
301 dict.set_item(
302 identifier!(&vm.ctx, version),
303 vm.ctx.new_int(current_version).into(),
304 vm,
305 )?;
306 }
307
308 if should_set {
309 registry.set_item(key, vm.ctx.true_value.clone().into(), vm)?;
310 }
311 Ok(false)
312}
313
314fn update_registry(
317 registry: &PyObject,
318 text: &PyObject,
319 category: &PyObject,
320 add_zero: bool,
321 vm: &VirtualMachine,
322) -> PyResult<bool> {
323 let altkey: PyObjectRef = if add_zero {
324 PyTuple::new_ref(
325 vec![
326 text.to_owned(),
327 category.to_owned(),
328 vm.ctx.new_int(0).into(),
329 ],
330 &vm.ctx,
331 )
332 .into()
333 } else {
334 PyTuple::new_ref(vec![text.to_owned(), category.to_owned()], &vm.ctx).into()
335 };
336 already_warned(registry, &altkey, true, vm)
337}
338
339fn normalize_module(filename: &Py<PyStr>, vm: &VirtualMachine) -> PyObjectRef {
340 match filename.byte_len() {
341 0 => vm.new_pyobj("<unknown>"),
342 len if len >= 3 && filename.as_bytes().ends_with(b".py") => {
343 vm.new_pyobj(&filename.as_wtf8()[..len - 3])
344 }
345 _ => filename.as_object().to_owned(),
346 }
347}
348
349fn filter_search(
351 category: &PyObject,
352 text: &PyObject,
353 lineno: usize,
354 module: &PyObject,
355 filters: &PyListRef,
356 list_name: &str,
357 vm: &VirtualMachine,
358) -> PyResult<Option<PyObjectRef>> {
359 let mut i = 0;
362 while i < filters.borrow_vec().len() {
363 let Some(tmp_item) = filters.borrow_vec().get(i).cloned() else {
364 break;
365 };
366 let tmp_item = PyTupleRef::try_from_object(vm, tmp_item)
367 .ok()
368 .filter(|t| t.as_slice().len() == 5)
369 .ok_or_else(|| {
370 vm.new_value_error(format!("warnings.{list_name} item {i} isn't a 5-tuple"))
371 })?;
372
373 let action = &tmp_item.as_slice()[0];
375 if !action.class().is(vm.ctx.types.str_type) {
376 return Err(vm.new_type_error(format!(
377 "action must be a string, not '{}'",
378 action.class().name()
379 )));
380 }
381 let good_msg = check_matched(&tmp_item.as_slice()[1], text, vm)?;
382 let is_subclass = category.is_subclass(&tmp_item.as_slice()[2], vm)?;
383 let good_mod = check_matched(&tmp_item.as_slice()[3], module, vm)?;
384 let ln: usize = tmp_item.as_slice()[4]
385 .try_int(vm)
386 .map_or(0, |v| v.as_u32_mask() as _);
387
388 if good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln) {
389 return Ok(Some(action.to_owned()));
390 }
391 i += 1;
392 }
393 Ok(None)
394}
395
396fn get_filter(
397 category: &PyObject,
398 text: &PyObject,
399 lineno: usize,
400 module: &PyObject,
401 vm: &VirtualMachine,
402) -> PyResult {
403 if let Some(context_filters) = get_warnings_context_filters(vm)? {
404 if let Some(action) = filter_search(
405 category,
406 text,
407 lineno,
408 module,
409 &context_filters,
410 "_warnings_context _filters",
411 vm,
412 )? {
413 return Ok(action);
414 }
415 return get_default_action(vm);
416 }
417
418 let filters = get_warnings_filters(vm)?;
419 if let Some(action) = filter_search(category, text, lineno, module, &filters, "filters", vm)? {
420 return Ok(action);
421 }
422 get_default_action(vm)
423}
424
425pub fn warn(
426 message: PyObjectRef,
427 category: Option<PyTypeRef>,
428 stack_level: isize,
429 source: Option<PyObjectRef>,
430 vm: &VirtualMachine,
431) -> PyResult<()> {
432 warn_with_skip(message, category, stack_level, source, None, vm)
433}
434
435pub fn warn_unawaited_coroutine(coro: &PyObject, qualname: &Py<PyStr>, vm: &VirtualMachine) {
442 let unraisable_msg = format!(
443 "Exception ignored while finalizing coroutine {}",
444 coro.repr(vm)
445 .map_or_else(|_| String::new(), |s| s.to_string())
446 );
447 let mut warned = false;
448 if let Some(helper) =
449 get_warnings_attr(vm, vm.ctx.intern_str("_warn_unawaited_coroutine"), true)
450 .ok()
451 .flatten()
452 {
453 match helper.call((coro.to_owned(),), vm) {
454 Ok(_) => warned = true,
455 Err(e) => {
456 if e.fast_isinstance(vm.ctx.exceptions.runtime_warning) {
457 warned = true;
458 }
459 vm.run_unraisable(e, Some(unraisable_msg.clone()), coro.to_owned());
460 }
461 }
462 }
463 if !warned {
464 let msg = format!("coroutine '{qualname}' was never awaited");
465 if let Err(e) =
466 crate::stdlib::_warnings::warn(vm.ctx.exceptions.runtime_warning, msg, 1, vm)
467 {
468 vm.run_unraisable(e, Some(unraisable_msg), coro.to_owned());
469 }
470 }
471}
472
473pub fn warn_with_skip(
475 message: PyObjectRef,
476 category: Option<PyTypeRef>,
477 mut stack_level: isize,
478 source: Option<PyObjectRef>,
479 skip_file_prefixes: Option<&Py<PyTuple>>,
480 vm: &VirtualMachine,
481) -> PyResult<()> {
482 if let Some(prefixes) = skip_file_prefixes
483 && !prefixes.as_slice().is_empty()
484 && stack_level < 2
485 {
486 stack_level = 2;
487 }
488 let (filename, lineno, module, registry) = setup_context(stack_level, skip_file_prefixes, vm)?;
489 warn_explicit(
490 category, message, filename, lineno, module, registry, None, source, vm,
491 )
492}
493
494#[allow(clippy::too_many_arguments)]
496pub fn warn_explicit(
497 category: Option<PyTypeRef>,
498 message: PyObjectRef,
499 filename: PyStrRef,
500 lineno: usize,
501 module: Option<PyObjectRef>,
502 registry: PyObjectRef,
503 source_line: Option<PyObjectRef>,
504 source: Option<PyObjectRef>,
505 vm: &VirtualMachine,
506) -> PyResult<()> {
507 let module = module.unwrap_or_else(|| normalize_module(&filename, vm));
509 if vm.is_none(&module) {
510 return Ok(());
511 }
512
513 let is_warning = message.fast_isinstance(vm.ctx.exceptions.warning);
515 let (text, category, message) = if is_warning {
516 let text = message.str(vm)?;
517 let cat = message.class().to_owned();
518 (text, cat, message)
519 } else {
520 let text = message.str(vm)?;
522 let cat = category.unwrap_or_else(|| vm.ctx.exceptions.user_warning.to_owned());
523 let instance = cat.as_object().call((text.clone(),), vm)?;
524 (text, cat, instance)
525 };
526
527 let lineno_obj: PyObjectRef = vm.ctx.new_int(lineno).into();
528
529 let key: PyObjectRef = PyTuple::new_ref(
531 vec![
532 text.clone().into(),
533 category.as_object().to_owned(),
534 lineno_obj.clone(),
535 ],
536 &vm.ctx,
537 )
538 .into();
539
540 if !vm.is_none(®istry) && already_warned(®istry, &key, false, vm)? {
542 return Ok(());
543 }
544
545 let action = get_filter(category.as_object(), text.as_object(), lineno, &module, vm)?;
547 let action_str = PyStrRef::try_from_object(vm, action)
548 .map_err(|_| vm.new_type_error("action must be a string"))?;
549
550 if action_str.as_bytes() == b"error" {
551 let exc = PyBaseExceptionRef::try_from_object(vm, message)?;
552 return Err(exc);
553 }
554 if action_str.as_bytes() == b"ignore" {
555 return Ok(());
556 }
557
558 let already = if action_str.as_wtf8() != "always" && action_str.as_wtf8() != "all" {
561 if !vm.is_none(®istry) {
562 registry.set_item(&*key, vm.ctx.true_value.clone().into(), vm)?;
563 }
564
565 let action_s = action_str.to_str();
566 match action_s {
567 Some("once") => {
568 let reg = if vm.is_none(®istry) {
569 get_once_registry(vm)?
570 } else {
571 registry
572 };
573 update_registry(®, text.as_ref(), category.as_object(), false, vm)?
574 }
575 Some("module") => {
576 if !vm.is_none(®istry) {
577 update_registry(®istry, text.as_ref(), category.as_object(), false, vm)?
578 } else {
579 false
580 }
581 }
582 Some("default") => false,
583 _ => {
584 return Err(vm.new_runtime_error(format!(
585 "Unrecognized action ({action_str}) in warnings.filters:\n {action_str}"
586 )));
587 }
588 }
589 } else {
590 false
591 };
592
593 if already {
594 return Ok(());
595 }
596
597 call_show_warning(
598 category,
599 &text,
600 message,
601 filename,
602 lineno,
603 lineno_obj,
604 source_line,
605 source,
606 vm,
607 )
608}
609
610#[allow(clippy::too_many_arguments)]
611fn call_show_warning(
612 category: PyTypeRef,
613 text: &Py<PyStr>,
614 message: PyObjectRef,
615 filename: PyStrRef,
616 lineno: usize,
617 lineno_obj: PyObjectRef,
618 source_line: Option<PyObjectRef>,
619 source: Option<PyObjectRef>,
620 vm: &VirtualMachine,
621) -> PyResult<()> {
622 let Some(show_fn) = get_warnings_attr(vm, identifier!(vm, _showwarnmsg), source.is_some())?
623 else {
624 show_warning(&filename, lineno, text, &category, source_line, vm);
625 return Ok(());
626 };
627
628 if !show_fn.is_callable() {
629 return Err(vm.new_type_error("warnings._showwarnmsg() must be set to a callable"));
630 }
631
632 let Some(warnmsg_cls) = get_warnings_attr(vm, identifier!(vm, WarningMessage), false)? else {
633 return Err(vm.new_runtime_error("unable to get warnings.WarningMessage"));
634 };
635
636 let msg = warnmsg_cls.call(
637 vec![
638 message,
639 category.into(),
640 filename.into(),
641 lineno_obj,
642 vm.ctx.none(),
643 vm.ctx.none(),
644 vm.unwrap_or_none(source),
645 ],
646 vm,
647 )?;
648
649 show_fn.call((msg,), vm)?;
650 Ok(())
651}
652
653fn show_warning(
654 filename: &Py<PyStr>,
655 lineno: usize,
656 text: &Py<PyStr>,
657 category: &Py<PyType>,
658 source_line: Option<PyObjectRef>,
659 vm: &VirtualMachine,
660) {
661 let stderr = crate::stdlib::sys::PyStderr(vm);
662 writeln!(
663 stderr,
664 "{}:{}: {}: {}",
665 filename,
666 lineno,
667 category.name(),
668 text
669 );
670 if let Some(source_line) = source_line
671 && let Ok(line) = source_line.str(vm)
672 {
673 writeln!(stderr, "{line}");
674 }
675}
676
677fn is_filename_to_skip(frame: &crate::frame::FrameObject, prefixes: &Py<PyTuple>) -> bool {
679 let filename = frame.f_code().co_filename();
680 let filename_bytes = filename.as_bytes();
681 prefixes.as_slice().iter().any(|prefix| {
682 prefix
683 .downcast_ref::<PyStr>()
684 .is_some_and(|s| filename_bytes.starts_with(s.as_bytes()))
685 })
686}
687
688fn next_external_frame_with_skip(
690 frame: &crate::frame::FrameObjectRef,
691 skip_file_prefixes: Option<&Py<PyTuple>>,
692 vm: &VirtualMachine,
693) -> Option<crate::frame::FrameObjectRef> {
694 let mut f = frame.f_back(vm);
695 loop {
696 let current: crate::frame::FrameObjectRef = f.take()?;
697 if current.is_internal_frame()
698 || skip_file_prefixes.is_some_and(|p| is_filename_to_skip(¤t, p))
699 {
700 f = current.f_back(vm);
701 } else {
702 return Some(current);
703 }
704 }
705}
706
707fn setup_context(
710 mut stack_level: isize,
711 skip_file_prefixes: Option<&Py<PyTuple>>,
712 vm: &VirtualMachine,
713) -> PyResult<(PyStrRef, usize, Option<PyObjectRef>, PyObjectRef)> {
714 let mut f = crate::frame::current_thread_frame_materialize(vm);
717
718 if stack_level <= 0 || f.as_ref().is_some_and(|frame| frame.is_internal_frame()) {
721 while {
722 stack_level -= 1;
723 stack_level > 0
724 } {
725 match f {
726 Some(tmp) => f = tmp.f_back(vm),
727 None => break,
728 }
729 }
730 } else {
731 while {
732 stack_level -= 1;
733 stack_level > 0
734 } {
735 match f {
736 Some(tmp) => f = next_external_frame_with_skip(&tmp, skip_file_prefixes, vm),
737 None => break,
738 }
739 }
740 }
741
742 let (globals, filename, lineno) = if let Some(f) = f {
743 (
744 f.iframe().globals().to_owned(),
745 f.iframe().code().source_path(),
746 f.lineno().max(0) as usize,
747 )
748 } else if let Some(frame) = vm.current_frame() {
749 (
751 frame.iframe().globals().to_owned(),
752 vm.ctx.intern_str("<sys>"),
753 1,
754 )
755 } else {
756 let globals = vm
758 .sys_module
759 .as_object()
760 .get_attr(identifier!(vm, __dict__), vm)
761 .and_then(|d| {
762 d.downcast::<crate::builtins::PyDict>()
763 .map_err(|_| vm.new_type_error("sys.__dict__ is not a dictionary"))
764 })?;
765 (globals, vm.ctx.intern_str("<sys>"), 0)
766 };
767
768 let registry = match globals.get_item("__warningregistry__", vm) {
769 Ok(r) => r,
770 Err(_) => {
771 let r = vm.ctx.new_dict();
772 globals.set_item("__warningregistry__", r.clone().into(), vm)?;
773 r.into()
774 }
775 };
776
777 let module = globals
779 .get_item("__name__", vm)
780 .unwrap_or_else(|_| vm.new_pyobj("<string>"));
781 Ok((filename.to_owned(), lineno, Some(module), registry))
782}