Skip to content

Generated Bash completion does not escape square brackets #6407

Description

@electricbrass

Please complete the following tasks

Rust Version

1.96.0

Clap Version

clap 4.6.1 and clap_complete 4.6.5

Minimal reproducible code

use clap::{CommandFactory, Parser};
use clap_complete::{Shell, generate};
use std::io;

#[derive(Parser)]
struct Args {
    arg: Option<String>,
}

fn main() {
    generate(
        Shell::Bash,
        &mut Args::command(),
        "clap_bug",
        &mut io::stdout(),
    );
}

Steps to reproduce the bug with the above code

In Bash:

  1. source <(cargo run --quiet)
  2. shopt -s failglob
  3. Type "clap_bug " (including the space) and trigger completion with tab

Actual Behaviour

There are 3 variations on the behavior: Bash default, nullglob enabled, and failglob enabled.

In all 3 cases, when showing the completion, an optional argument enclosed in square brackets is treated as a pattern matching bracket expression. This will attempt to match any single character contained within the brackets. As this is a filename expansion context, it will attempt to match filenames in the current directory. For example, in this case the completion generated by clap is [ARG], and so it will match the filenames A, R, and G:
Image

If there are no matching filenames, the behavior depends on the current configuration:
If both failglob and nullglob are disabled, it will appear to work "correctly" and it is treated as a literal string:
Image

If nullglob is enabled, the argument will just be missing from the completion:
Image

If failglob is enabled, it will immediately abort completion with an error:
Image

Expected Behaviour

It should always be a literal string, instead of a match pattern, so that it behaves consistently across these 3 modes and is not affected by directory contents.

Additional Context

I tried modifying the generated completion in a few ways to fix this (various combinations of backslashes and single quotes) and couldn't find anything that worked consistently. I think really the completion just should not be including placeholders like this, since it's not actually a valid argument. For example, I found this through bob, where it tried to show [VERSION] as a completion option for bob uninstall. If bob uninstall had no other completion candidates, fixing this issue would mean that it would complete with the literal text [VERSION], which isn't actually a valid argument for that program. It would also then trigger pattern matching if I were to hit enter without deleting that text:

Image

I don't know much about non-Bash shells but I wouldn't be surprised if this affects Zsh as well.
Edit: It looks like clap's generated completions for Zsh instead have it just fall back to the default completion of filenames for these cases instead of trying to use a placeholder.

Debug Output

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-completionArea: completion generatorC-bugCategory: bugS-triageStatus: New; needs maintainer attention.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions