Repository: astral-sh/ruff
File: crates/ruff_linter/src/rules/airflow/rules/function_signature_change_in_3.rs (Rust)

Code before:
                .iter()
                .any(|p| p.name().as_str() == "ti_key"),
            // check valid signature `def get_link(self, operator, ti_key)`
            3 => parameters
                .posonlyargs
                .iter()
                .chain(parameters.args.iter())
                .nth(2)
                .is_some_and(|p| p.name().as_str() == "ti_key"),
            _ => false,
        };

        if !is_valid_signature {
            checker.report_diagnostic(
                Airflow3IncompatibleFunctionSignature {
                    function_name: "get_link".to_string(),
                    change: FunctionSignatureChange::Message(
                        "Use `def get_link(self, operator, *, ti_key)` or `def get_link(self, operator, ti_key)` as the method signature.",
                    ),
                },
                function_def.name.range(),
            );
        }
    }
}

fn check_method_arguments(
    checker: &Checker,
    qualified_name: &QualifiedName,
    attr: &Identifier,
    arguments: &Arguments,
) {
    let has_positional_args =
        arguments.find_positional(0).is_some() || arguments.args.iter().any(Expr::is_starred_expr);

    if let ["airflow", "lineage", "hook", "HookLineageCollector"] = qualified_name.segments() {
        if attr.as_str() == "create_asset" && has_positional_args {
            checker.report_diagnostic(
                Airflow3IncompatibleFunctionSignature {
                    function_name: attr.to_string(),
                    change: FunctionSignatureChange::Message(
                        "Pass positional arguments as keyword arguments (e.g., `create_asset(uri=...)`)",
                    ),
                },
                attr.range(),
            );
        }
    }
}

fn check_constructor_arguments(

Review comment (suggestion):
updated the logic to check for valid signature pattern, which is either `def get_link(self, operator, *, ti_key)` or ``def get_link(self, operator, ti_key)`, and the diagnostic will be raised when the method signature is evaluated to invalid.

The name of the first two positional parameters doesn't matter for the rule. If there are 2 positional parameters (i.e., `self`, `operator`), check if the keyword-only parameter is named `ti_key`. If there are 3 positional parameters, check if the third one is named `ti_key`.

The message and test cases are updated accordingly.

Code after:

        let is_valid_signature = match positional_count {
            // check valid signature `def get_link(self, operator, *, ti_key)`
            2 => parameters
                .kwonlyargs
                .iter()
                .any(|p| p.name().as_str() == "ti_key"),
            // check valid signature `def get_link(self, operator, ti_key)`
            3 => parameters
                .posonlyargs
                .iter()
                .chain(parameters.args.iter())
                .nth(2)
                .is_some_and(|p| p.name().as_str() == "ti_key"),
            _ => false,
        };

        if !is_valid_signature {
            checker.report_diagnostic(
                Airflow3IncompatibleFunctionSignature {
                    function_name: "get_link".to_string(),
                    change: FunctionSignatureChange::Message(
                        "Use `def get_link(self, operator, *, ti_key)` or `def get_link(self, operator, ti_key)` as the method signature.",
                    ),
                },
                function_def.name.range(),
            );
        }
    }
}

fn check_method_arguments(
    checker: &Checker,
    qualified_name: &QualifiedName,
    attr: &Identifier,
    arguments: &Arguments,
) {
    let has_positional_args =
        arguments.find_positional(0).is_some() || arguments.args.iter().any(Expr::is_starred_expr);

    if let ["airflow", "lineage", "hook", "HookLineageCollector"] = qualified_name.segments() {
        if attr.as_str() == "create_asset" && has_positional_args {
            checker.report_diagnostic(
                Airflow3IncompatibleFunctionSignature {
                    function_name: attr.to_string(),
                    change: FunctionSignatureChange::Message(
                        "Pass positional arguments as keyword arguments (e.g., `create_asset(uri=...)`)",
                    ),
                },
                attr.range(),
            );