Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Perl Live LinterNew to Visual Studio Code? Get it now.
Perl Live Linter

Perl Live Linter

Nicholas Nash

|
2 installs
| (0) | Free
Live Perl diagnostics for undeclared and unused variables, shadowing, and common syntax errors.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Perl Live Linter

Live diagnostics for .pl, .pm, .t, and untitled Perl documents, including unsaved edits. Hover over an underline or open the Problems panel for details.

Rules

Rule Appearance Behavior
Undeclared variable Error underline Flags a use without a preceding declaration in the current or enclosing scope.
Unused variable Warning underline Flags lexical variables and named parameters that are never read. Plain assignments, including element and list assignments, do not count as reads.
Shadowed variable Warning underline Flags a declaration that hides an enclosing declaration; includes a link to the original declaration.
Redeclared variable Warning underline Flags repeated declarations in the same scope, including duplicate names in a declaration list.
Syntax error Error underline Detects unmatched delimiters, unterminated quotes and heredocs, missing operands, and common missing-semicolon cases.
use feature 'signatures';

sub greet($name, $_ignored = undef) {
    my $unused = 42;          # Warning: never read
    print "Hello, $name";     # Signature parameter is recognized
    {
        my $name = 'Other';   # Warning: hides the parameter
        print $name;
    }
    print $naem;              # Warning: undeclared variable
}

Prefix an intentionally unused variable or parameter with _ (for example, $_ignored). Package variables declared with our and imported variables are exempt from unused checks because other files may read them. Reads in nested scopes, closures, interpolation, and default parameter expressions count. Compound assignments and increments count as reads; the rule does not perform whole-program data-flow analysis or report individual unused assignments.

Run locally

  1. Open this folder in VS Code.
  2. Press F5 and select Run Perl Live Linter if prompted.
  3. In the Extension Development Host, open examples/features.pl, examples/syntax-errors.pl, or another Perl file. Diagnostics update while typing.

To install in your regular VS Code editor, package and install from this directory:

npx @vscode/vsce package
code --install-extension perl-live-linter-0.5.0.vsix --force

Reload the VS Code window after replacing a previously installed version.

The extension itself requires no build step, npm dependencies, or Perl installation. It reads document text and never runs the file, loads modules, or invokes perl -c (which can execute compile-time code).

Hash, array, and scalar checks

For example, this initializer gets a red underline:

my %hash = [key1 => 123];
print $hash{key1};

[...] creates one array reference. A hash assignment expects a list of key/value pairs. The second line uses valid hash-access syntax; fix the initializer:

my %hash = (key1 => 123);
print $hash{key1};

Or use a scalar holding a hash reference:

my $hash = {key1 => 123};
print $hash->{key1};

The checks cover simple declarations and reassignments:

Rule Default Checks
hash-initialization Error A single reference assigned to a hash, or a statically known odd number of hash items.
array-initialization Error A bare reference assigned to an array. This is legal Perl but creates a one-element array. An explicit list such as ([1, 2]) is accepted as intentional.
scalar-list-assignment Warning A statically recognizable multi-item parenthesized expression assigned in scalar context. Scalars do not store lists; list destructuring and array-length assignments are accepted.
collection-access Error Confusion between a declared array, hash, and scalar of the same name, including missing or unnecessary ->.
reference-type Error A directly known array/hash/scalar reference accessed with incompatible ->[...] or ->{...} syntax.

Each diagnostic explains the difference and suggests the appropriate Perl syntax. Try examples/collections.pl.

These are conservative checks, not a runtime type system. Function return types, unknown list expansion, complex expressions, and nested reference contents are not inferred. Reference assumptions are discarded across control-flow boundaries and unknown calls; references are tracked only in simple straight-line code. The checker does not claim that every legal but suspicious expression is an error.

Duplicate hash keys

Literal hash lists and hash references warn when a key is repeated:

my %person = (name => 'Alice', name => 'Bob'); # Duplicate key: name
my $options = { timeout => 10, timeout => 30 }; # Duplicate key: timeout

The warning links to the first occurrence. Bareword keys before =>, simple unescaped single/double-quoted keys, and ordinary nonnegative decimal integer keys are compared. Nested hash references are checked independently. Unknown list expansion and complex expressions are skipped; interpolated or escaped string keys are not evaluated. Ordinary array lists and function argument lists are not checked.

Configure perlLiveLinter.rules.duplicate-hash-key or suppress a warning with # perl-ignore duplicate.

Completion and subroutine outline

  • Type $, @, or % to suggest declared variables visible at the cursor. Suggestions respect declaration order, shadowing, signatures, and scalar/array/hash namespaces, including element accesses and $#array syntax. Completion replaces only the name, preserving sigils, braces, and any following index.
  • Named subroutines and packages appear in Outline and Go to Symbol in Editor (Ctrl+Shift+O). Subroutine entries show their package-qualified names and select the declaration name.
  • F12, Ctrl-click, and Go to Declaration also resolve explicit local subroutine calls such as greet(), &greet, and Example::greet(). Forward declarations resolve to a unique body when available.

These features use the current document, including unsaved edits, and remain available when diagnostics are disabled. Completion covers tracked declarations, not module exports or dynamically created variables. Subroutine navigation does not infer method dispatch, bareword calls without parentheses, lexical subs, or definitions in other files. Multiple competing bodies are left unresolved.

Navigation, rename, and typo fixes

  • F12 or Ctrl-click a variable to jump to its declaration. Go to Declaration is also available from the context menu. These resolve declarations in the current document, including parameters and the collections behind element accesses.
  • F2 renames the selected lexical variable or parameter and its bound references in the current document. Enter the name without $, @, or %. Sigils, braces, slices, and interpolation are preserved. Comments and literal strings are left alone.
  • Ctrl+. on an undeclared variable offers up to three nearby spellings from declarations visible at that position. Suggestions stay in the same scalar/array/hash namespace. Selecting a suggestion changes that occurrence only.

Rename checks the proposed text again and rejects collisions that change reference bindings, including capture of an existing unresolved reference. It also rejects same-scope name collisions, Perl special names, package/imported variables, and files with detected syntax errors. Because this is a partial parser, rename is conservatively unavailable in files containing eval, no strict, or declarations inside embedded interpolation. It does not rename external symbols or dynamically constructed names. Review VS Code's rename preview when using syntax outside the documented support.

Navigation and rename remain available when diagnostics are disabled. Typo fixes respect disabled rules and suppression comments.

Inline suppression

Use actual Perl comments to suppress selected diagnostics on the same line or the immediately following physical line:

my $unused; # perl-ignore unused

# perl-ignore-next undeclared -- provided by the framework
print $external;

my $temporary; print $external; # perl-ignore unused, undeclared

Use all (or omit the rule list) to suppress every rule for that line. Suppression uses the line where the diagnostic starts; for an unused variable, that is the declaration line. Short rule names are unused, undeclared, shadowed, redeclared, syntax, hash, array, scalar, access, reference, and duplicate. Full rule names and the older perl-linter-disable-line / perl-linter-disable-next-line comments also remain supported. Unknown rule names suppress nothing. Directives inside strings, regexes, POD, and heredoc text do not suppress diagnostics. Suppression hides diagnostics without changing scope analysis or rename collision checks.

Perl support

  • my, our, and state, including declaration lists, declaration order, nested scopes, loops, and conditional branches.
  • Named and anonymous subroutine signatures, including defaults referring to earlier parameters and slurpy array/hash parameters. Traditional my (...) = @_ arguments and ordinary prototypes are also recognized. Signature recognition does not validate which Perl version/features the file enables.
  • Explicit variable imports, such as use Config qw(%Config) or use Example '$value', and use vars qw($value @items). Names are tracked per package; package-qualified variables are allowed, including legacy apostrophe separators. Explicit imports are assumed to be provided by the named module, without inspecting or executing it.
  • Separate scalar, array, and hash namespaces; element/slice accesses; common prefix/postfix reference dereferences.
  • Variable interpolation in strings, regexes and heredocs, including indexed variables and embedded expressions such as "@{[ $value->method() ]}". Heredoc initializer references use the declaration visibility at the initializer.
  • Comments, POD, literal quote forms, and data/end sections are excluded from code checks. Perl special variables are allowed. local does not introduce a lexical declaration.

Limits

This remains a tolerant static checker, not a full Perl compiler. Syntax diagnostics cover the cases above, not every syntax error. Context-sensitive bareword calls, regex-versus-division ambiguities, unparenthesized control conditions, unusual prototypes/attributes, and complex interpolated code can still be misinterpreted. Imported variables supplied through default exports, export tags, computed import lists or dynamic symbol manipulation cannot be inferred. Declarations inside embedded interpolation expressions are not fully scope-analyzed. Syntax errors and incomplete edits may cause provisional or missing variable diagnostics.

Unused checks track references within this document, not runtime reachability or external evaluation. The checker does not validate module availability, signature ordering/arity, regex grammar, or version-specific syntax. These limits keep the extension independent of a local Perl runtime and avoid executing project code while typing.

Settings

Setting Default Purpose
perlLiveLinter.enabled true Enable or disable all diagnostics.
perlLiveLinter.delay 200 Debounce delay in milliseconds, from 0 to 2000.
perlLiveLinter.rules.undeclared-variable error Undeclared variable severity.
perlLiveLinter.rules.unused-variable warning Unused variable severity; underlined by default.
perlLiveLinter.rules.shadowed-variable warning Shadowing severity.
perlLiveLinter.rules.redeclared-variable warning Same-scope redeclaration severity.
perlLiveLinter.rules.syntax-error error Syntax diagnostic severity.
perlLiveLinter.rules.duplicate-hash-key warning Repeated literal keys in hash lists and hash references.
perlLiveLinter.rules.hash-initialization error Hash initialization and assignment.
perlLiveLinter.rules.array-initialization error Bare reference assigned to an array.
perlLiveLinter.rules.scalar-list-assignment warning List expression assigned to a scalar.
perlLiveLinter.rules.collection-access error Incorrect collection/reference access syntax.
perlLiveLinter.rules.reference-type error Incompatible reference dereferencing.

Each rule accepts off, error, warning, information, or hint. Configure these in VS Code Settings or workspace .vscode/settings.json; changes take effect immediately. For example:

{
  "perlLiveLinter.rules.unused-variable": "warning",
  "perlLiveLinter.rules.shadowed-variable": "off"
}

Development

Use Node.js 20 or newer:

npm test
npm run check

src/lexer.js tokenizes source and quoted expressions. src/analyzer.js tracks declarations, references, and scope and produces diagnostics with UTF-16 offsets. src/extension.js maps them to VS Code diagnostics, severity, and related declaration locations. It also registers navigation, rename, completion, outline, and quick-fix providers.

Tests cover the original rule, new syntax and variable rules, and editor lifecycle using a mocked VS Code API. They do not launch a real Extension Development Host.

References: VS Code diagnostics, Perl signatures and lexical scope, the vars pragma.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft