Fatalization of calling import/unimport method with argument

This commit is mostly a manual reversion of commit f1cf82e.  Return to
using a perl_croak in universal.c; adjust regen/warnings.pl as needed; run 'make
regen'; get t/op/universal.t passing.  No documentation changes yet.

Add test for undefined unimport method, then refactor repeated code into
a subroutine.  Perform a pattern match rather than a string equality
test because testing for exact line numbers inside a test program is too
fragile for maintenance purposes.

'mispelled' was misspelled in one location; correct.  Suppress 'used
only once' warning in one location.  POD formatting improvements as
suggested by Elvin Aslanov, with one other word change.

Correct case of one character in error message so that it's the same in
both universal.c and pod/perldiag.pod.  This enables us to preserve
status of universal.c in t/porting/diag.t.  Per: Tony Cook review.

Rebasing on blead in the wake of d306795336, meant that merge conflicts
appeared, which necessitated two rounds of running 'make regen'.
Simplify test_undefined_method(). Update entry in
pod/perldeprecation.pod.

For: GH #23623
This commit is contained in:
James E Keenan 2025-12-08 10:20:36 -05:00 committed by James E Keenan
parent d3c72e2bf3
commit f430ad845a
8 changed files with 61 additions and 79 deletions

44
lib/warnings.pm generated
View File

@ -123,18 +123,15 @@ our %Offsets = (
# Warnings Categories added in Perl 5.037
'experimental::class' => 148,
# Warnings Categories added in Perl 5.039002
'deprecated::missing_import_called_with_args'=> 150,
# Warnings Categories added in Perl 5.039008
'deprecated::subsequent_use_version'=> 152,
'deprecated::subsequent_use_version'=> 150,
# Warnings Categories added in Perl 5.041
'experimental::keyword_all' => 154,
'experimental::keyword_any' => 156,
'experimental::keyword_all' => 152,
'experimental::keyword_any' => 154,
# Warnings Categories added in Perl 5.043
'experimental::signature_named_parameters'=> 158,
'experimental::signature_named_parameters'=> 156,
);
our %Bits = (
@ -144,30 +141,29 @@ our %Bits = (
'closed' => "\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [6]
'closure' => "\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [1]
'debugging' => "\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [22]
'deprecated' => "\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x04\x00\x00\x45\x01", # [2,48,61,72,73,75,76]
'deprecated' => "\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x04\x00\x00\x45\x00", # [2,48,61,72,73,75]
'deprecated::delimiter_will_be_paired'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00", # [73]
'deprecated::dot_in_inc' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00", # [61]
'deprecated::missing_import_called_with_args'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00", # [75]
'deprecated::subsequent_use_version'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", # [76]
'deprecated::subsequent_use_version'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00", # [75]
'deprecated::unicode_property_name' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00", # [48]
'deprecated::version_downgrade' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00", # [72]
'digit' => "\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [31]
'exec' => "\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [7]
'exiting' => "\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [3]
'experimental' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x45\x01\x41\x55\x15\x10\x54", # [52,53,55,56,60,63..70,74,77..79]
'experimental' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x45\x01\x41\x55\x15\x10\x15", # [52,53,55,56,60,63..70,74,76..78]
'experimental::args_array_with_signatures'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00", # [67]
'experimental::builtin' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00", # [68]
'experimental::class' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00", # [74]
'experimental::declared_refs' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00", # [60]
'experimental::defer' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00", # [69]
'experimental::extra_paired_delimiters'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00", # [70]
'experimental::keyword_all' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04", # [77]
'experimental::keyword_any' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10", # [78]
'experimental::keyword_all' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", # [76]
'experimental::keyword_any' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04", # [77]
'experimental::private_use' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00", # [63]
'experimental::re_strict' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00", # [55]
'experimental::refaliasing' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00", # [56]
'experimental::regex_sets' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00", # [53]
'experimental::signature_named_parameters'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40", # [79]
'experimental::signature_named_parameters'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10", # [78]
'experimental::try' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00", # [66]
'experimental::uniprop_wildcards' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00", # [64]
'experimental::vlb' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00", # [65]
@ -227,30 +223,29 @@ our %DeadBits = (
'closed' => "\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [6]
'closure' => "\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [1]
'debugging' => "\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [22]
'deprecated' => "\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x08\x00\x00\x8a\x02", # [2,48,61,72,73,75,76]
'deprecated' => "\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x08\x00\x00\x8a\x00", # [2,48,61,72,73,75]
'deprecated::delimiter_will_be_paired'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00", # [73]
'deprecated::dot_in_inc' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00", # [61]
'deprecated::missing_import_called_with_args'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00", # [75]
'deprecated::subsequent_use_version'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02", # [76]
'deprecated::subsequent_use_version'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00", # [75]
'deprecated::unicode_property_name' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", # [48]
'deprecated::version_downgrade' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00", # [72]
'digit' => "\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [31]
'exec' => "\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [7]
'exiting' => "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", # [3]
'experimental' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8a\x02\x82\xaa\x2a\x20\xa8", # [52,53,55,56,60,63..70,74,77..79]
'experimental' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8a\x02\x82\xaa\x2a\x20\x2a", # [52,53,55,56,60,63..70,74,76..78]
'experimental::args_array_with_signatures'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00", # [67]
'experimental::builtin' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00", # [68]
'experimental::class' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00", # [74]
'experimental::declared_refs' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00", # [60]
'experimental::defer' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00", # [69]
'experimental::extra_paired_delimiters'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00", # [70]
'experimental::keyword_all' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08", # [77]
'experimental::keyword_any' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20", # [78]
'experimental::keyword_all' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02", # [76]
'experimental::keyword_any' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08", # [77]
'experimental::private_use' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00", # [63]
'experimental::re_strict' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00", # [55]
'experimental::refaliasing' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00", # [56]
'experimental::regex_sets' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00", # [53]
'experimental::signature_named_parameters'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80", # [79]
'experimental::signature_named_parameters'=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20", # [78]
'experimental::try' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00", # [66]
'experimental::uniprop_wildcards' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00", # [64]
'experimental::vlb' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00", # [65]
@ -305,6 +300,7 @@ our %DeadBits = (
our %NoOp = (
'deprecated::goto_construct' => 1,
'deprecated::missing_import_called_with_args'=> 1,
'deprecated::smartmatch' => 1,
'experimental::alpha_assertions' => 1,
'experimental::bitwise' => 1,
@ -320,8 +316,8 @@ our %NoOp = (
# These are used by various things, including our own tests
our $NONE = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
our $DEFAULT = "\x10\x01\x00\x00\x00\x50\x04\x00\x00\x00\x00\x00\x01\x40\x05\x45\x55\x15\x55\x55"; # [2,4,22,23,25,48,55..57,60,61,63..70,72..79]
our $LAST_BIT = 160 ;
our $DEFAULT = "\x10\x01\x00\x00\x00\x50\x04\x00\x00\x00\x00\x00\x01\x40\x05\x45\x55\x15\x55\x15"; # [2,4,22,23,25,48,55..57,60,61,63..70,72..78]
our $LAST_BIT = 158 ;
our $BYTES = 20 ;
sub Croaker
@ -928,8 +924,6 @@ The current hierarchy is:
| |
| +- deprecated::dot_in_inc
| |
| +- deprecated::missing_import_called_with_args
| |
| +- deprecated::subsequent_use_version
| |
| +- deprecated::unicode_property_name

View File

@ -47,14 +47,14 @@ Category: "deprecated::unicode_property_name"
=head3 Calling a missing C<import()> or C<unimport()> method with an argument
Historically calling C<import()> or C<unimport()> on any class which did
not define such a method would be silently ignored. Effectively Perl
behaved as though there was an empty method defined in the C<UNIVERSAL>
package (even when there was no such method actually defined). As of
Perl version 5.39.2 calling such a method I<with> an argument will
trigger a warning, and in Perl version 5.44 this warning will be
upgraded to an error. (Calling such a method with no arguments at all
will always be safe.)
Historically calling C<import()> or C<unimport()> on any class which did not
define such a method would be silently ignored. Effectively Perl behaved as
though there was an empty method defined in the C<UNIVERSAL> package (even
when there was no such method actually defined). Beginning with Perl version
5.39.2 (production version 5.40), calling such a method I<with> an argument
triggered a warning, and in Perl version 5.44 this warning became upgraded to
an error. (Calling such a method with no arguments at all will always be
safe.)
Category: "deprecated::missing_import_called_with_args"

View File

@ -1339,21 +1339,15 @@ a string overload and is also not a blessed CODE reference. In short the
C<require> function does not know what to do with the object.
See also L<perlfunc/require>.
=item Attempt to call undefined %s method with arguments ("%s"%s)
via package "%s" (Perhaps you forgot to load the package?)
=item Attempt to call undefined %s method with arguments via package
"%s" (Perhaps you forgot to load the package?)
(D deprecated::missing_import_called_with_args) You called the
C<import()> or C<unimport()> method of a class that has no import method
defined in its inheritance graph, and passed an argument to the method.
This is very often the sign of a misspelled package name in a use or
require statement that has silently succeded due to a case insensitive
(F) You called the C<import()> or C<unimport()> method of a class that has no
such method defined in its inheritance graph, and passed an argument to the
method. This is very often the sign of a misspelled package name in a C<use>
or C<require> statement that has silently succeded due to a case insensitive
file system.
Another common reason this may happen is when mistakenly attempting to
import or unimport a symbol from a class definition or package which
does not use C<Exporter> or otherwise define its own C<import> or
C<unimport> method.
=item Can't locate package %s for @%s::ISA
(W syntax) The @ISA array contained the name of another package that

View File

@ -190,6 +190,7 @@ my %NAME_TO_VALUE; # ('NAME' => index_number, ....);
# so no warning bit is needed anymore
my %NO_BIT_FOR = map { ( uc $_ => 1, $_ => 1 ) } qw(
deprecated::goto_construct
deprecated::missing_import_called_with_args
deprecated::smartmatch
experimental::lexical_subs
experimental::postderef

View File

@ -196,25 +196,26 @@ my $x = {}; bless $x, 'X';
ok $x->isa('UNIVERSAL');
ok $x->isa('UNIVERSAL');
sub test_undefined_method {
my $method = shift;
my @message_components = (
q|Attempt to call undefined|,
q|method with arguments via package "Some::Package"|,
q|(Perhaps you forgot to load the package?)|,
);
my $message = join ' ' => (
$message_components[0],
$method,
@message_components[1,2],
);
my $pattern = qr/\Q$message\E/;
{
my $err;
$SIG{__WARN__}= sub { die $_[0] };
eval { Some::Package->import("bar") };
my $err = $@;
$err=~s!t/op!op!;
is $err, "Attempt to call undefined import method with arguments (\"bar\")"
. " via package \"Some::Package\" (Perhaps you forgot to load"
. " the package?) at op/universal.t line 203.\n";
eval { Some::Package->unimport(1.234) };
$err = $@;
$err=~s!t/op!op!;
is $err, "Attempt to call undefined unimport method with arguments (\"1.234\")"
. " via package \"Some::Package\" (Perhaps you forgot to load"
. " the package?) at op/universal.t line 209.\n";
eval { Some::Package->$method("bar") };
like $@, $pattern, "Got expected pattern for undefined $method";
}
test_undefined_method($_) for (qw| import unimport |);
# This segfaulted in a blead.
fresh_perl_is('package Foo; Foo->VERSION; print "ok"', 'ok');

View File

@ -137,6 +137,7 @@ while (<$diagfh>) {
&& !$entries{$cur_entry}{cattodo}) {
my $data_line= $entries{$cur_entry}{todo_line};
TODO: {
no warnings 'once';
local $::TODO = "Remove the TODO entry \"$cur_entry\" from DATA "
. "at $0 line $data_line as it is already in $pod near line $.";
ok($cur_entry);

View File

@ -451,14 +451,10 @@ XS(XS_UNIVERSAL_import_unimport)
* depends on it has its own "no import" logic that produces better
* warnings than this does. */
if (strNE(class_pv,"_charnames"))
ck_warner_d(packWARN(WARN_DEPRECATED__MISSING_IMPORT_CALLED_WITH_ARGS),
"Attempt to call undefined %s method with arguments "
"(%" SVf_QUOTEDPREFIX "%s) via package "
Perl_croak(aTHX_
"Attempt to call undefined %s method with arguments via package "
"%" SVf_QUOTEDPREFIX " (Perhaps you forgot to load the package?)",
ix ? "unimport" : "import",
SVfARG(ST(1)),
(items > 2 ? " ..." : ""),
SVfARG(ST(0)));
ix ? "unimport" : "import", SVfARG(ST(0)));
}
XSRETURN_EMPTY;
}

15
warnings.h generated
View File

@ -149,26 +149,22 @@
#define WARN_EXPERIMENTAL__CLASS 74
/* Warnings Categories added in Perl 5.039002 */
#define WARN_DEPRECATED__MISSING_IMPORT_CALLED_WITH_ARGS 75
/* Warnings Categories added in Perl 5.039008 */
#define WARN_DEPRECATED__SUBSEQUENT_USE_VERSION 76
#define WARN_DEPRECATED__SUBSEQUENT_USE_VERSION 75
/* Warnings Categories added in Perl 5.041 */
#define WARN_EXPERIMENTAL__KEYWORD_ALL 77
#define WARN_EXPERIMENTAL__KEYWORD_ANY 78
#define WARN_EXPERIMENTAL__KEYWORD_ALL 76
#define WARN_EXPERIMENTAL__KEYWORD_ANY 77
/* Warnings Categories added in Perl 5.043 */
#define WARN_EXPERIMENTAL__SIGNATURE_NAMED_PARAMETERS 79
#define WARN_EXPERIMENTAL__SIGNATURE_NAMED_PARAMETERS 78
#define WARNsize 20
#define WARN_ALLstring "\125\125\125\125\125\125\125\125\125\125\125\125\125\125\125\125\125\125\125\125"
#define WARN_NONEstring "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
#define WARN_DEFAULTstring "\x10\x01\x00\x00\x00\x50\x04\x00\x00\x00\x00\x00\x01\x40\x05\x45\x55\x15\x55\x55"
#define WARN_DEFAULTstring "\x10\x01\x00\x00\x00\x50\x04\x00\x00\x00\x00\x00\x01\x40\x05\x45\x55\x15\x55\x15"
#define isLEXWARN_on \
cBOOL(PL_curcop && PL_curcop->cop_warnings != pWARN_STD)
@ -355,7 +351,6 @@ category parameters passed.
=for apidoc Amnh||WARN_DEPRECATED__VERSION_DOWNGRADE
=for apidoc Amnh||WARN_DEPRECATED__DELIMITER_WILL_BE_PAIRED
=for apidoc Amnh||WARN_EXPERIMENTAL__CLASS
=for apidoc Amnh||WARN_DEPRECATED__MISSING_IMPORT_CALLED_WITH_ARGS
=for apidoc Amnh||WARN_DEPRECATED__SUBSEQUENT_USE_VERSION
=for apidoc Amnh||WARN_EXPERIMENTAL__KEYWORD_ALL
=for apidoc Amnh||WARN_EXPERIMENTAL__KEYWORD_ANY