Skip to content

Linter rules crash on dynamic method calls: Undefined property PhpParser\Node\Expr\BinaryOp\Concat::$name #382

@loganhenson

Description

@loganhenson

Summary

Several linter rules crash when the parser encounters a dynamic method call where the method name is a concatenation expression, e.g.:

$value = \strval($item->{'get' . $p}());

The specific failure is Undefined property: PhpParser\Node\Expr\BinaryOp\Concat::$name thrown from RequestHelperFunctionWherePossible.php:20 (and a matching line in RequestValidation.php:30).

Reproducer

Put this in any .php file under a Laravel app and run tlint lint (or duster lint --using=tlint) against the containing directory:

<?php

namespace App\Test;

class Probe
{
    public function foo($item, string $p): string
    {
        return (string) $item->{'get' . $p}();
    }
}

Running against the file directly does not crash; only directory mode reliably triggers it (presumably because of internal node-state accumulation across parsed files).

Root cause (best guess)

Linters/RequestHelperFunctionWherePossible.php:20:

$node instanceof Node\Expr\MethodCall
    && $node->name->name === 'get'
    ...

Node\Expr\MethodCall::$name is typed Identifier | Expr. For dynamic method calls ($obj->{expr}()) the parser yields an Expr such as BinaryOp\Concat, which has no ->name property. The rule needs to guard with $node->name instanceof Node\Identifier before reaching into ->name.

The same pattern appears in RequestValidation.php:30.

Impact

  • tlint lint <dir> / duster lint --using=tlint fails hard instead of skipping the problematic node
  • The directory-mode crash blocks full-repo lint runs in tooling that invokes tlint against project directories
  • Targeted flows (--dirty, --diff=<branch>) are unaffected because they feed tlint individual files

Environment

  • Duster 3.4.2 (bundles TLint)
  • PHP 8.3.27
  • Laravel 12 app

Happy to open a PR adding the instanceof Node\Identifier guard if that's the accepted fix direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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