NAME

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

VERSION

version v0.3.0

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 Quux ( $VERSION );                  # 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 Quux $VERSION;                      # no parentheses for complex
                                        # expressions

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 ""

The one exception is qx with a single-quote delimiter. perlop defines qx'' as non-interpolating, so that delimiter is semantic rather than stylistic and is always left alone.

# Good - qx'' deliberately suppresses interpolation
my $pid = qx'echo $$';                  # the shell sees $$, not Perl

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

  • Pragmas (lowercase module names, as PPI defines them) with a single argument also allow quoted strings, with normal quoting rules applied

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 );

# Good - pragmas with a single argument allow quotes
use feature "class";                    # pragma, single arg, double quotes
use strict "refs";                      # pragma, single arg, double quotes
no warnings "experimental";             # no pragma, single arg, double quotes
use feature qw( class );                # qw() is still fine too

# 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
use feature 'class';                    # pragma single arg prefers ""

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.

This exemption applies to string tokens only ('', "", q(), qq()). qw() and qx() keep their delimiter checks, because there a newline is merely a word separator or command formatting and does not affect the delimiter choice.

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

Scope

This policy covers string literals ("", ''), quote operators (q(), qq()), word lists (qw()), command execution (qx()), and use/no statement import lists.

The following quote-like constructs are not checked, as they have fundamentally different quoting semantics:

  • Regular expressions: m//, qr//

  • Substitutions: s///

  • Transliterations: tr///, y///

  • Heredocs: <<EOF

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
                                        # (escaped @)
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
use feature 'class';                    # pragma single arg prefers ""

# 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

# Pragma single-argument examples
use feature "class";                    # pragma, single arg, double quotes
use strict "refs";                      # pragma, single arg, double quotes
no warnings "experimental";             # no pragma, single arg, double quotes
no warnings ( "experimental" );         # parentheses are also exempt here
use feature qw( class );                # qw() is still fine too

# 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

desc_double

desc_single

desc_use_qw

desc_remove_parens

desc_optimal ($display)

The suggestion wording exported for tests, so the strings live in one place instead of being duplicated as literals. Each returns the description a violation carries: use "", use '', use qw(), remove parentheses and use $display respectively. They are available via @EXPORT_OK.

_operator_fix ($op, $delim)

Builds an operator fix structure for operator $op and the delimiter hashref $delim (with start and end keys).

_render_fix ($fix)

Renders a fix structure as its user-visible suggestion string, which becomes the violation's description (e.g. use "", use q[]). This is the single place the wording of a suggestion is produced.

_fix_violation ($fix, $elem)

Builds a Perl::Critic::PJCJ::Violation for $elem whose description is _render_fix of $fix, whose explanation is the static rationale, and which carries $fix directly via ->fix.

supported_parameters

This policy has no configurable parameters.

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.

escape_single_quoted ($string)

Encodes a string as the inner content of a single-quoted literal by backslash-escaping backslashes and apostrophes. This is the single source of single-quote encoding knowledge, shared with Perl::Critic::PJCJ::Fixer.

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.

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, ties are broken by the preference order () > [] > <> > {}.

_known_fixes

Returns the list of every fix structure the policy can produce: the three plain fixes plus one operator fix per supported delimiter of q, qq, qw and qx. fix_data is generated from this list.

fix_data

Maps a violation's description string to structured fix data, so that tools such as Perl::Critic::PJCJ::Fixer can resolve a fix without a live violation object. The map is generated from _known_fixes via _render_fix, so it cannot drift from the wording the policy actually emits. Violations from this policy also carry their fix directly, so this lookup is only a fallback.

Returns a hashref describing the fix, or undef for an unknown description:

{ type => "double" }         # use ""
{ type => "single" }         # use ''
{ type => "remove_parens" }  # remove parentheses
{ type  => "operator",       # use qw(), use q[], use qq<> ...
  op    => "qw",
  start => "(",
  end   => ")",
}

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.

has_quote_sensitive_escapes ($string)

Tests whether a string contains escape sequences (such as \n, \x1b or \F) that mean different things in single and double quotes. Such strings keep their current quote style. This is the single source of the double-quote escape list, shared with Perl::Critic::PJCJ::Fixer, which must not decode double-quoted content containing these escapes.

_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.

_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.

_any_arg_interpolates

Checks whether any string argument in a list would interpolate if placed in double quotes. Used by both check_use_statement and _is_in_use_statement to determine whether a use/no statement's arguments require interpolation, which affects whether qw() can be suggested and whether individual tokens should be checked under normal quoting rules.

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.

Two shared guards are applied here, before dispatch, rather than in each checking method: tokens inside a use/no statement (handled instead by check_use_statement) and string tokens containing literal newlines are both left alone.

prepare_to_scan_document

Clears the per-document use/no statement verdict cache before each document is scanned, and returns true. Perl::Critic reuses one policy instance for every file in a run, so this override stops a cached verdict from one document being read against another.

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. When that literal content also contains an apostrophe, q() is suggested instead of single quotes, so that no escaping is added.

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.

statement_level_list ($elem)

Returns the PPI::Structure::List wrapping a use/no statement's whole argument list (the parentheses in use Foo ( ... );), or undef when there is none. A list nested inside the arguments, such as the parentheses of a function or method call, does not qualify. Shared with Perl::Critic::PJCJ::Fixer so the policy and the fixer can never disagree about which parentheses a "remove parentheses" violation means.

_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.

collect_qw_words ($words, @elements)

Decodes the values of use/no statement arguments into $words, recursing into parenthesised lists. Returns false when any element cannot become a qw() word: an interpolating or escape-bearing string, a plain bareword, an operator other than a comma, or any other expression. Shared with Perl::Critic::PJCJ::Fixer, so the policy suggests use qw() exactly when the fixer can rewrite the statement.

qw_word_ok ($word)

Tests whether a single decoded word can be written inside qw( ): it must be non-empty and free of whitespace, parentheses and backslashes. This is the single word-representability predicate, shared with Perl::Critic::PJCJ::Fixer.

_use_args_qw_representable

Tests whether every use/no statement argument can be represented as a word inside qw( ), combining collect_qw_words and qw_word_ok. The use qw() suggestion is only emitted when this holds.

_use_qw_violation

Emits the use qw() violation for a use/no statement, or nothing when the arguments are not qw-representable.

_summarise_use_arguments

Detects qw() usage among use/no statement arguments and whether those qw() operators use parentheses rather than other delimiters. This information drives the violation logic in check_use_statement.

check_use_statement

Checks quoting consistency in use and no statements. Analyses every argument type to enforce appropriate quoting:

  • 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.

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.