mago-semantics 1.20.1

PHP Semantics Checker.
Documentation
use mago_database::file::HasFileId;
use mago_php_version::feature::Feature;
use mago_reporting::Annotation;
use mago_reporting::Issue;
use mago_span::HasSpan;
use mago_span::Span;
use mago_syntax::ast::Argument;
use mago_syntax::ast::ArgumentList;

use crate::internal::context::Context;

#[inline]
pub fn check_argument_list<'ast, 'arena>(
    argument_list: &'ast ArgumentList<'arena>,
    context: &mut Context<'_, 'ast, 'arena>,
) {
    let mut last_named_argument: Option<Span> = None;
    let mut last_unpacking: Option<Span> = None;

    for argument in &argument_list.arguments {
        match &argument {
            Argument::Positional(positional_argument) => {
                if let Some(ellipsis) = positional_argument.ellipsis {
                    if let Some(last_named_argument) = last_named_argument {
                        context.report(
                            Issue::error("Cannot use argument unpacking after a named argument.")
                                .with_annotation(
                                    Annotation::primary(ellipsis.span()).with_message("Unpacking argument here."),
                                )
                                .with_annotation(
                                    Annotation::secondary(last_named_argument).with_message("Named argument here."),
                                )
                                .with_note("Unpacking arguments must come before named arguments."),
                        );
                    }

                    last_unpacking = Some(ellipsis.span());
                } else {
                    if let Some(named_argument) = last_named_argument {
                        context.report(
                            Issue::error("Cannot use positional argument after a named argument.")
                                .with_annotation(
                                    Annotation::primary(positional_argument.span())
                                        .with_message("Positional argument defined here."),
                                )
                                .with_annotation(
                                    Annotation::secondary(named_argument).with_message("Named argument here."),
                                )
                                .with_note("Positional arguments must come before named arguments."),
                        );
                    }

                    if let Some(unpacking) = last_unpacking {
                        context.report(
                            Issue::error("Cannot use positional argument after argument unpacking.")
                                .with_annotation(
                                    Annotation::primary(positional_argument.span())
                                        .with_message("Positional argument defined here."),
                                )
                                .with_annotation(
                                    Annotation::secondary(unpacking).with_message("Argument unpacking here."),
                                )
                                .with_note("Positional arguments must come before unpacking arguments."),
                        );
                    }
                }
            }
            Argument::Named(named_argument) => {
                if !context.version.is_supported(Feature::NamedArguments) {
                    context.report(
                        Issue::error("Named arguments are only available in PHP 8.0 and above.").with_annotation(
                            Annotation::primary(named_argument.span()).with_message("Named argument used here."),
                        ),
                    );
                }

                last_named_argument = Some(named_argument.span());
            }
        }
    }

    if !context.version.is_supported(Feature::TrailingCommaInFunctionCalls)
        && let Some(last_comma) = argument_list.arguments.get_trailing_token()
    {
        context.report(
            Issue::error("Trailing comma in function calls is only available in PHP 7.3 and later.")
                .with_annotation(
                    Annotation::primary(last_comma.span_for(context.source_file.file_id())).with_message("Trailing comma found here."),
                )
                .with_help(
                    "Remove the trailing comma to make the code compatible with PHP 7.2 and earlier versions, or upgrade to PHP 7.3 or later.",
                )
        );
    }
}