NAME

Perl::Critic::Policy::ValuesAndExpressions::RequireConsistentQuoting - Use consistent and optimal quoting

VERSION

version v0.1.4

SYNOPSIS

# Bad examples:
my $greeting = 'hello';                 # use double quotes for simple strings
my @words    = qw{word(with)parens};    # use qw[] for unbalanced content
my $text     = qq(simple);              # use "" instead of qq()
my $file     = q!path/to/file!;         # use "" instead of q()
use Config 'arg1', 'arg2';              # simple strings should use qw()
use lib ( "$HOME/perl" );               # complex expressions need no
                                        # parentheses

# Good examples:
my $greeting = "hello";                 # double quotes for simple strings
my @words    = qw[ word(with)parens ];  # optimal delimiter choice
my $text     = "simple";                # "" preferred over qq()
my $file     = "path/to/file";          # "" reduces punctuation
use Config qw( arg1 arg2 );             # simple use arguments use qw()
use lib "$HOME/perl";                   # interpolation uses normal rules

DESCRIPTION

This policy enforces consistent quoting to improve code readability and maintainability. It applies three simple rules:

Rule 1: Reduce punctuation

Prefer fewer characters and simpler syntax. Prefer real quotes over quote-like operators when possible.

# Good
my $text    = "hello world";            # "" preferred over qq()
my $literal = 'contains$literal';       # '' preferred over q()
my $path    = "path/to/file";           # simple quotes reduce punctuation

# Bad
my $text    = qq(hello world);          # unnecessary quote operator
my $literal = q(contains$literal);      # unnecessary quote operator
my $path    = q!path/to/file!;          # unnecessary quote operator

Rule 2: Prefer interpolated strings

If it doesn't matter whether a string is interpolated or not, prefer the interpolated version (double quotes).

# Good
my $name  = "John";                     # simple string uses double quotes
my $email = 'user@domain.com';          # literal @ uses single quotes
my $var   = 'Price: $10';               # literal $ uses single quotes

# Bad
my $name = 'John';                      # should use double quotes

Rule 3: Use bracket delimiters in preference order

If the best choice is a quote-like operator, prefer (), [], <>, or {} in that order.

# Good
my @words = qw( simple list );          # () preferred when content is simple
my @data  = qw[ has(parens) ];          # [] optimal - handles unbalanced ()
my $cmd   = qx( has[brackets] );        # () optimal - handles unbalanced []
my $text  = q( has<angles> );           # () optimal - handles unbalanced <>

# Bad - exotic delimiters
my @words = qw/word word/;              # should use qw()
my $path  = q|some|path|;               # should use ""
my $text  = qq#some#text#;              # should use ""

Special Case: Use and No statements

Use and no statements have special quoting requirements for their import lists. Both use and no statements follow identical rules:

  • Modules with no arguments or empty parentheses are acceptable

  • Single version numbers (e.g., 1.23, v5.10.0) are exempt from all rules

  • Fat comma (=>) arguments should have no parentheses for readability

  • Complex expressions (variables, conditionals, structures) should have no parentheses

  • Arguments requiring interpolation follow normal string quoting rules individually

  • Simple string arguments without interpolation should use qw( ) with parentheses only

This design promotes readability whilst maintaining compatibility with perlimports.

# Good - basic cases
use Foo;                                # no arguments
use Bar ();                             # empty parentheses
use Baz 1.23;                           # version numbers exempt
no warnings;                            # no statements follow same rules

# Good - fat comma arguments (no parentheses)
use Data::Printer
  deparse       => 0,
  show_unicode  => 1,
  class         => { expand => "all" };

# Good - complex expressions (no parentheses)
use Module $VERSION;
use Config $DEBUG ? "verbose" : "quiet";
use Handler { config => "file.conf" };

# Good - interpolation cases (normal string rules)
use lib "$HOME/perl", "/usr/lib";       # interpolation prevents qw()
no warnings "$category", "another";     # applies to no statements too

# Good - simple strings use qw()
use Foo qw( arg1 arg2 arg3 );           # multiple simple arguments
no warnings qw( experimental uninitialized );

# Bad - incorrect quoting
use Foo 'single_arg';                   # single quotes should use qw()
use Bar "arg1", "arg2";                 # simple strings need qw()
use Baz qw[ arg1 arg2 ];                # qw() must use parentheses only
use Qux ( key => "value" );             # fat comma needs no parentheses
use Quux ( $VERSION );                  # complex expressions need no
                                        # parentheses

Special Case: Newlines

Strings containing newlines do not follow the rules. But note that outside of a few very special cases, strings with literal newlines are not a good idea.

# Allowed
my $text = qq(
  line 1
  line 2
);

RATIONALE

  • Minimising escape characters improves readability and reduces errors

  • Simple quotes are preferred over their q() and qq() equivalents when possible

  • Double quotes are preferred for consistency and to allow potential interpolation

  • Many years ago, Tom Christiansen wrote a lengthy article on how perl's default quoting system is interpolation, and not interpolating means something extraordinary is happening. I can't find the original article, but you can see that double quotes are used by default in The Perl Cookbook, for example.

  • Only bracket delimiters should be used (no exotic delimiters like /, |, #, etc.)

  • Optimal delimiter selection reduces visual noise in code

AFFILIATION

This Policy is part of the Perl::Critic::PJCJ distribution.

CONFIGURATION

This Policy is not configurable except for the standard options.

EXAMPLES

String Literals

# Bad
my $greeting = 'hello';                 # Rule 2: should use double quotes
my $email    = "user@domain.com";       # Rule 2: should use single quotes
                                        # (literal @)
my $path     = 'C:\Program Files';      # Rule 2: should use double quotes

# Good
my $greeting = "hello";                 # double quotes for simple strings
my $email    = 'user@domain.com';       # single quotes for literal @
my $path     = "C:\\Program Files";     # double quotes handle backslashes

Quote Operators

# Bad
my $simple = q(hello);                  # Rule 1: should use ''
my $text   = qq(hello);                 # Rule 1: should use ""
my @words  = qw/one two/;               # Rule 3: should use qw( )
my $cmd    = qx|ls|;                    # Rule 3: should use qx( )

# Good
my $simple = 'hello$literal';           # single quotes for literal content
my $text   = "hello";                   # double quotes preferred
my @words  = qw( one two );             # bracket delimiters only
my $cmd    = qx( ls );                  # bracket delimiters only

Optimal Delimiter Selection

# Bad - unbalanced delimiters
my @list = qw(word(with)parens);        # Rules 1, 3: unbalanced () in content
my $cmd  = qx[command[with]brackets];   # Rules 1, 3: unbalanced [] in content
my $text = q{word{with}braces};         # Rules 1, 3: unbalanced {} in content

# Good - balanced delimiters
my @list = qw[ word(with)parens ];      # [] handles parentheses in content
my $cmd  = qx( command[with]brackets ); # () handles brackets in content

Complex Content

# When content has multiple quote types, quote-like operators may be needed
my $both = qq(has 'single' and "double" quotes); # qq() handles both
                                                  # quote types cleanly

Use and No Statement Examples

# Bad
use Foo 'single_arg';                   # single quotes should use qw()
use Bar "arg1", "arg2";                 # simple strings need qw()
use Baz qw[ arg1 arg2 ];                # qw() must use parentheses only
use Qux ( key => "value" );             # fat comma should have no parentheses
use Quux ( $VERSION );                  # complex expressions need no
                                        # parentheses
no warnings ( "experimental" );         # simple strings should use qw()

# Good
use Foo;                                # no arguments
use Bar ();                             # empty parentheses
use Baz 1.23;                           # version numbers exempt
use Qux qw( single_arg );               # simple string uses qw()
use Quux qw( arg1 arg2 arg3 );          # multiple simple arguments
no warnings qw( experimental uninitialized ); # no statements follow same
                                                # rules

# Fat comma examples (no parentheses)
use Data::Printer
  deparse       => 0,
  show_unicode  => 1;
use Config
  key           => "value",
  another_key   => { nested => "structure" };

# Complex expression examples (no parentheses)
use Module $VERSION;                    # variable argument
use Config $DEBUG ? "verbose" : "quiet"; # conditional expression
use Handler { config => "file.conf" };   # hash reference

# Interpolation examples (normal string rules apply)
use lib "$HOME/perl", "/usr/lib";       # interpolation prevents qw()
use lib "$x/d1", "$x/d2";               # both strings need interpolation
use lib "$HOME/perl", "static";         # mixed interpolation uses double
                                        # quotes
no warnings "$category", "another";     # no statements handle
                                        # interpolation too

METHODS

supported_parameters

This policy has no configurable parameters.

violates

The main entry point for policy violation checking. Uses a dispatch table to route different quote token types to their appropriate checking methods. This design allows for efficient handling of the six different PPI token types that represent quoted strings and quote-like operators.

would_interpolate

Determines whether a string would perform variable interpolation if placed in double quotes. This is critical for deciding between single and double quotes - strings that would interpolate variables should use single quotes to preserve literal content, while non-interpolating strings should use double quotes for consistency.

Uses PPI's authoritative parsing to detect interpolation rather than regex patterns, ensuring accurate detection of complex cases like literal variables.

would_interpolate_from_single_quotes

Tests whether a string from single quotes would interpolate if converted to double quotes. This specialised version handles the challenge that PPI provides decoded string content rather than the original source text.

When checking single-quoted strings, PPI's string() method returns the decoded content. For example, the source 'price: \\$5.00' becomes 'price: \$5.00' in the content (with one backslash). To test interpolation properly, this method reconstructs what the original escaping would have been by re-escaping backslashes and apostrophes according to single-quote rules.

This ensures accurate detection of whether converting a single-quoted string to double quotes would introduce unintended variable interpolation.

delimiter_preference_order

Establishes the preference hierarchy for bracket delimiters when multiple options handle the content equally well. The policy prefers delimiters in this order: () > [] > <> > {}.

This ordering balances readability and convention - parentheses are most familiar and commonly used, while braces are often reserved for hash references and blocks.

parse_quote_token

Extracts delimiter and content information from quote-like operators such as qw{}, q{}, qq{}, and qx{}. Handles both bracket pairs (where start and end delimiters differ) and symmetric delimiters (where they're the same).

This parsing is essential for delimiter optimisation, as it separates the operator, delimiters, and content for independent analysis.

find_optimal_delimiter

Determines the best delimiter choice for a quote-like operator by analysing the content for balanced delimiters. Implements the core logic for Rules 1 and 3: choose delimiters that handle unbalanced content gracefully and prefer bracket delimiters.

Only considers bracket delimiters (), [], <>, {} as valid options, rejecting exotic delimiters like /, |, #. When multiple delimiters work equally well, uses the preference order to break ties.

check_delimiter_optimisation

Validates that quote-like operators use optimal delimiters according to Rules 1 and 3. This method coordinates parsing the current token and finding the optimal alternative, issuing violations when the current choice is suboptimal.

Acts as a bridge between the parsing and optimisation logic, providing a clean interface for the quote-checking methods.

check_single_quoted

Enforces Rules 1 and 2 for single-quoted strings: prefer double quotes for simple strings unless the content contains literal $ or @ characters that shouldn't be interpolated, or the string contains double quotes that would require special handling.

Also detects when q() operators would be better than single quotes for complex content, promoting cleaner alternatives.

check_double_quoted

Validates double-quoted strings to ensure they genuinely need interpolation. Suggests single quotes when the content contains only literal $ or @ characters with no actual interpolation, as this indicates the developer intended literal content.

This reduces unnecessary complexity and makes the code's intent clearer.

check_q_literal

Enforces Rules 1 and 3 for q() operators. First ensures optimal delimiter choice, then evaluates whether simpler quote forms would be more appropriate.

Allows q() when the content has both single and double quotes (making it the cleanest option), but suggests simpler alternatives for basic content that could use '' or "".

check_qq_interpolate

Enforces Rules 1 and 3 for qq() operators. First ensures optimal delimiter choice, then determines whether simple double quotes would suffice.

The policy prefers "" over qq() when the content doesn't contain double quotes, as this reduces visual noise and follows common Perl conventions.

check_quote_operators

Handles qw() and qx() operators, focusing purely on delimiter optimisation according to Rules 1 and 3. These operators don't have simpler alternatives, so the policy only ensures they use the most appropriate delimiters to handle unbalanced content gracefully.

check_use_statement

Checks quoting consistency in use and no statements. Implements comprehensive argument analysis to enforce appropriate quoting based on argument types:

  • Version numbers are exempt from all quoting rules

  • Fat comma arguments should have no parentheses for readability

  • Complex expressions should have no parentheses to reduce visual noise

  • Arguments requiring interpolation follow normal string quoting rules

  • Simple string arguments should use qw() with parentheses only

This promotes consistency and clarity whilst supporting modern Perl idioms and maintaining compatibility with tools like perlimports.

_analyse_argument_types

Analyses use/no statement arguments to classify them into different types: fat comma operators, complex expressions, version numbers, simple strings, and quote operators. This classification drives the quoting rule enforcement in check_use_statement.

Also detects whether the original statement uses parentheses, which affects the violation messages for fat comma and complex expression cases.

_extract_use_arguments

Extracts and processes arguments from use/no statements, handling both bare arguments and those enclosed in parentheses. Skips whitespace, commas, and semicolons whilst preserving significant operators like fat comma (=>).

Handles nested list structures by recursively extracting their contents, ensuring all argument types are properly identified for rule enforcement.

_extract_list_arguments

Recursively processes parenthesised argument lists within use/no statements. Handles complex nested structures including expressions, statements, and hash constructors whilst filtering out structural tokens that don't affect quoting decisions.

_summarise_use_arguments

Provides summary statistics about use/no statement arguments: counts string tokens, detects qw() usage, and verifies that qw() operators use parentheses rather than other delimiters. This information drives the violation logic in check_use_statement.

AUTHOR

Paul Johnson <paul@pjcj.net>

COPYRIGHT

Copyright 2025 Paul Johnson.

LICENCE

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.