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

Code before:
#[violation_metadata(stable_since = "0.14.11")]
pub(crate) struct Airflow3FunctionSignatureChange {
    function_name: String,
    change_type: FunctionSignatureChangeType,
}

impl Violation for Airflow3FunctionSignatureChange {
    const FIX_AVAILABILITY: FixAvailability = FixAvailability::None;

    #[derive_message_formats]
    fn message(&self) -> String {
        let Airflow3FunctionSignatureChange {
            function_name,
            change_type,
        } = self;
        match change_type {
            FunctionSignatureChangeType::KeywordOnly { .. } => {
                format!("`{function_name}` only accepts keyword arguments in Airflow 3.0")
            }
        }
    }

    fn fix_title(&self) -> Option<String> {
        let Airflow3FunctionSignatureChange { change_type, .. } = self;
        match change_type {
            FunctionSignatureChangeType::KeywordOnly { message } => Some(message.to_string()),
        }
    }
}

/// AIR303
pub(crate) fn airflow_3_function_signature_change_expr(checker: &Checker, expr: &Expr) {
    if !checker.semantic().seen_module(Modules::AIRFLOW) {
        return;
    }

    airflow_3_keyword_args_only_function(checker, expr);
}

/// Check for functions that changed to only accept keyword arguments
fn airflow_3_keyword_args_only_function(checker: &Checker, expr: &Expr) {
    if let Expr::Call(call_expr @ ExprCall { arguments, .. }) = expr {
        check_keyword_only_method(checker, call_expr, arguments);
    }
}

fn check_keyword_only_method(checker: &Checker, call_expr: &ExprCall, arguments: &Arguments) {
    let Expr::Attribute(ExprAttribute { attr, value, .. }) = &*call_expr.func else {
        return;
    };


Review comment (suggestion):
I am wondering whether we should put it onto the heap using `to_string()`.

The `message` is a fixed-size/static string as defined on L98.

Code after:
#[violation_metadata(preview_since = "0.14.11")]
pub(crate) struct Airflow3IncompatibleFunctionSignature {
    function_name: String,
    change_type: FunctionSignatureChangeType,
}

impl Violation for Airflow3IncompatibleFunctionSignature {
    const FIX_AVAILABILITY: FixAvailability = FixAvailability::None;

    #[derive_message_formats]
    fn message(&self) -> String {
        let Airflow3IncompatibleFunctionSignature {
            function_name,
            change_type,
        } = self;
        match change_type {
            FunctionSignatureChangeType::KeywordOnly { .. } => {
                format!("`{function_name}` signature is changed in Airflow 3.0")
            }
        }
    }

    fn fix_title(&self) -> Option<String> {
        let Airflow3IncompatibleFunctionSignature { change_type, .. } = self;
        match change_type {
            FunctionSignatureChangeType::KeywordOnly { message } => Some(message.to_string()),
        }
    }
}

/// AIR303
pub(crate) fn airflow_3_incompatible_function_signature(checker: &Checker, expr: &Expr) {
    if !checker.semantic().seen_module(Modules::AIRFLOW) {
        return;
    }

    let Expr::Call(ExprCall {
        func, arguments, ..
    }) = expr
    else {
        return;
    };

    let Expr::Attribute(ExprAttribute { attr, value, .. }) = func.as_ref() else {
        return;
    };

    // Resolve the qualified name: try variable assignments first, then fall back to direct
    // constructor calls.
    let qualified_name = typing::resolve_assignment(value, checker.semantic()).or_else(|| {
        value