@@ -44,6 +44,7 @@ use crate::ast::{
44
44
ExprConstructionError , PatternElem , PolicySetError , PrincipalConstraint ,
45
45
PrincipalOrResourceConstraint , ResourceConstraint ,
46
46
} ;
47
+ use crate :: est:: extract_single_argument;
47
48
use itertools:: Either ;
48
49
use smol_str:: SmolStr ;
49
50
use std:: cmp:: Ordering ;
@@ -451,32 +452,37 @@ impl ast::Id {
451
452
errs : & mut ParseErrors ,
452
453
span : miette:: SourceSpan ,
453
454
) -> Option < ast:: Expr > {
454
- let mut adj_args = args. iter_mut ( ) . peekable ( ) ;
455
- match ( self . as_ref ( ) , adj_args. next ( ) , adj_args. peek ( ) ) {
456
- ( "contains" , Some ( a) , None ) => {
457
- // move the value out of the argument, replacing it with a dummy,
458
- // after this we can no longer use the original args
459
- let arg = mem:: replace ( a, ast:: Expr :: val ( false ) ) ;
460
- Some ( construct_method_contains ( e, arg, span) )
461
- }
462
- ( "containsAll" , Some ( a) , None ) => {
463
- let arg = mem:: replace ( a, ast:: Expr :: val ( false ) ) ;
464
- Some ( construct_method_contains_all ( e, arg, span) )
465
- }
466
- ( "containsAny" , Some ( a) , None ) => {
467
- let arg = mem:: replace ( a, ast:: Expr :: val ( false ) ) ;
468
- Some ( construct_method_contains_any ( e, arg, span) )
469
- }
470
- ( name, _, _) => {
471
- if EXTENSION_STYLES . methods . contains ( & name) {
455
+ match self . as_ref ( ) {
456
+ "contains" => extract_single_argument ( args. into_iter ( ) , "contains" , span)
457
+ . map ( |arg| construct_method_contains ( e, arg, span) )
458
+ . map_err ( |err| errs. push ( err) )
459
+ . ok ( ) ,
460
+ "containsAll" => extract_single_argument ( args. into_iter ( ) , "containsAll" , span)
461
+ . map ( |arg| construct_method_contains_all ( e, arg, span) )
462
+ . map_err ( |err| errs. push ( err) )
463
+ . ok ( ) ,
464
+ "containsAny" => extract_single_argument ( args. into_iter ( ) , "containsAny" , span)
465
+ . map ( |arg| construct_method_contains_any ( e, arg, span) )
466
+ . map_err ( |err| errs. push ( err) )
467
+ . ok ( ) ,
468
+ id => {
469
+ if EXTENSION_STYLES . methods . contains ( & id) {
472
470
args. insert ( 0 , e) ;
473
471
// INVARIANT (MethodStyleArgs), we call insert above, so args is non-empty
474
- Some ( construct_ext_meth ( name . to_string ( ) , args, span) )
472
+ Some ( construct_ext_meth ( id . to_string ( ) , args, span) )
475
473
} else {
476
- errs. push ( ToASTError :: new (
477
- ToASTErrorKind :: InvalidMethodName ( name. to_string ( ) ) ,
478
- span,
479
- ) ) ;
474
+ let unqual_name = ast:: Name :: unqualified_name ( self . clone ( ) ) ;
475
+ if EXTENSION_STYLES . functions . contains ( & unqual_name) {
476
+ errs. push ( ToASTError :: new (
477
+ ToASTErrorKind :: MethodCallOnFunction ( unqual_name. id ) ,
478
+ span,
479
+ ) ) ;
480
+ } else {
481
+ errs. push ( ToASTError :: new (
482
+ ToASTErrorKind :: InvalidMethodName ( id. to_string ( ) ) ,
483
+ span,
484
+ ) ) ;
485
+ }
480
486
None
481
487
}
482
488
}
@@ -2008,15 +2014,14 @@ impl ast::Name {
2008
2014
// error on standard methods
2009
2015
if self . path . is_empty ( ) {
2010
2016
let id = self . id . as_ref ( ) ;
2011
- match id {
2012
- "contains" | "containsAll" | "containsAny" => {
2013
- errs. push ( ToASTError :: new (
2014
- ToASTErrorKind :: FunctionCallOnMethod ( self . id ) ,
2015
- span,
2016
- ) ) ;
2017
- return None ;
2018
- }
2019
- _ => { }
2017
+ if EXTENSION_STYLES . methods . contains ( id)
2018
+ || matches ! ( id, "contains" | "containsAll" | "containsAny" )
2019
+ {
2020
+ errs. push ( ToASTError :: new (
2021
+ ToASTErrorKind :: FunctionCallOnMethod ( self . id ) ,
2022
+ span,
2023
+ ) ) ;
2024
+ return None ;
2020
2025
}
2021
2026
}
2022
2027
if EXTENSION_STYLES . functions . contains ( & self ) {
@@ -4116,4 +4121,72 @@ mod tests {
4116
4121
}
4117
4122
) ;
4118
4123
}
4124
+
4125
+ #[ test]
4126
+ fn invalid_methods_function_calls ( ) {
4127
+ let invalid_exprs = [
4128
+ (
4129
+ r#"contains([], 1)"# ,
4130
+ ExpectedErrorMessage :: error_and_help (
4131
+ "`contains` is a method, not a function" ,
4132
+ "use a method-style call: `e.contains(..)`" ,
4133
+ ) ,
4134
+ ) ,
4135
+ (
4136
+ r#"[].contains()"# ,
4137
+ ExpectedErrorMessage :: error (
4138
+ "call to `contains` requires exactly 1 argument, but got 0 arguments" ,
4139
+ ) ,
4140
+ ) ,
4141
+ (
4142
+ r#"[].contains(1, 2)"# ,
4143
+ ExpectedErrorMessage :: error (
4144
+ "call to `contains` requires exactly 1 argument, but got 2 arguments" ,
4145
+ ) ,
4146
+ ) ,
4147
+ (
4148
+ r#"[].containsAll()"# ,
4149
+ ExpectedErrorMessage :: error (
4150
+ "call to `containsAll` requires exactly 1 argument, but got 0 arguments" ,
4151
+ ) ,
4152
+ ) ,
4153
+ (
4154
+ r#"[].containsAll(1, 2)"# ,
4155
+ ExpectedErrorMessage :: error (
4156
+ "call to `containsAll` requires exactly 1 argument, but got 2 arguments" ,
4157
+ ) ,
4158
+ ) ,
4159
+ (
4160
+ r#"[].containsAny()"# ,
4161
+ ExpectedErrorMessage :: error (
4162
+ "call to `containsAny` requires exactly 1 argument, but got 0 arguments" ,
4163
+ ) ,
4164
+ ) ,
4165
+ (
4166
+ r#"[].containsAny(1, 2)"# ,
4167
+ ExpectedErrorMessage :: error (
4168
+ "call to `containsAny` requires exactly 1 argument, but got 2 arguments" ,
4169
+ ) ,
4170
+ ) ,
4171
+ (
4172
+ r#""1.1.1.1".ip()"# ,
4173
+ ExpectedErrorMessage :: error_and_help (
4174
+ "`ip` is a function, not a method" ,
4175
+ "use a function-style call: `ip(..)`" ,
4176
+ ) ,
4177
+ ) ,
4178
+ (
4179
+ r#"greaterThan(1, 2)"# ,
4180
+ ExpectedErrorMessage :: error_and_help (
4181
+ "`greaterThan` is a method, not a function" ,
4182
+ "use a method-style call: `e.greaterThan(..)`" ,
4183
+ ) ,
4184
+ ) ,
4185
+ ] ;
4186
+ for ( src, expected) in invalid_exprs {
4187
+ assert_matches ! ( parse_expr( src) , Err ( e) => {
4188
+ expect_err( src, & e, & expected) ;
4189
+ } ) ;
4190
+ }
4191
+ }
4119
4192
}
0 commit comments