scramble icon indicating copy to clipboard operation
scramble copied to clipboard

[Bug]: trailing comma issue

Open slnw opened this issue 2 months ago • 7 comments

What happened?

i get this error when i go to /docs/api.json path "Error when analyzing route 'GET api/v1/user' (App\Http\Controllers\AuthController@me): A trailing comma is not allowed here on line 3 – /var/www/html/vendor/nik ▶"

php -v
PHP 8.3.8 (cli) (built: Jun 13 2024 05:41:23) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.8, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.8, Copyright (c), by Zend Technologies
    with Xdebug v3.4.5, Copyright (c) 2002-2025, by Derick Rethan
composer show | grep dedoc
dedoc/scramble                            0.12.36  Automatic generation of API documentation for Laravel applications.
dedoc/scramble-pro                        0.7.18   Scramble PRO

How to reproduce the bug

a trailing comma issue

Package Version

.

PHP Version

.

Laravel Version

Laravel Framework 11.46.1

Which operating systems does with happen with?

macOS

Notes

No response

slnw avatar Oct 23 '25 12:10 slnw

@slnw can you please share the source of AuthController@me method?

romalytvynenko avatar Oct 23 '25 12:10 romalytvynenko

    /**
     * Get the authenticated User.
     *
     * @return UserData
     */
    public function me()
    {
        return UserData::from(auth()->user())->include('current_team.balance');
    }

UserData:

<?php

namespace App\Domain\Platform\Data;

use App\Domain\Campaigns\Domains\Models\DomainAccount;
use App\Domain\Platform\Models\User;
use App\Infra\Scramble\LaravelDataFakeInterface;
use Database\Factories\Domain\Platform\Models\UserFactory;
use Spatie\LaravelData\Attributes\Validation\Uuid;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Lazy;

class UserData extends Data implements LaravelDataFakeInterface
{
    public function __construct(
        #[Uuid]
        public string             $id,
        public string             $name,
        public string             $email,
        public string             $created_at,
        public ?string            $two_factor_confirmed_at,
        public ?string            $profile_photo_path,
        public ?string            $platform_domain_account_id,
        public TeamData|Lazy|null $current_team,
        public ?string            $role = null,
        public ?string            $platform_role = null,
        public ?array             $meta = null
    )
    {
    }

    public static function fromModel(User $user): self
    {
        $team = $user->teams()->find($user->current_team_id);

        return self::from([
            'platform_domain_account_id' => DomainAccount::getPlatformAccount()?->id,
            'current_team' => Lazy::create(
                fn() => $team !== null ? UserTeamData::fromModel($team) : null
            ),
            'role' => $user->membership?->role,
            ...$user->toArray(),
            'platform_role' => $user->platform_role
        ]);
    }

    public static function fake(): self
    {
        return self::from(UserFactory::new()->make());
    }
}
<?php

namespace App\Domain\Platform\Data;

use App\Domain\Platform\Models\Team;
use App\Infra\Scramble\LaravelDataFakeInterface;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Lazy;

class TeamData extends Data implements LaravelDataFakeInterface
{
    public function __construct(
        public string $id,
        public string $name,
        public string $created_at,
        public ?TeamMetaData $meta,
        public float|Lazy|null $balance = null
    ) {
    }

    public static function fromModel(Team $team)
    {
        return new self(
            id: $team->id,
            name: $team->name,
            created_at: $team->created_at,
            meta: TeamMetaData::from($team->meta),
            balance: Lazy::create(fn() => $team->platformBalance())
        );
    }

    public static function fake(): self
    {
        return self::from([...Team::factory()->make()->getAttributes(), 'balance' => 1000]);
    }
}

etc' etc'

slnw avatar Oct 23 '25 14:10 slnw

@slnw would you mind sharing a bit a stack trace entries as well?

romalytvynenko avatar Oct 23 '25 18:10 romalytvynenko

Error when analyzing route 'GET api/v1/user' (App\Http\Controllers\AuthController@me): A trailing comma is not allowed here on line 3 – /var/www/html/vendor/nikic/php-parser/lib/PhpParser/Parser/Php8.php on line 1382
 \ 
Parser
 \ 
Php8
 
: 1382
PhpParser\Parser\{closure}
PhpParser
 \ 
ParserAbstract
 
: 325
doParse
PhpParser
 \ 
ParserAbstract
 
: 187
parse
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Services
 \ 
FileParser
 
: 36
parseContent
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Reflector
 \ 
PropertyReflector
 
: 175
getAstNode
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Analyzer
 \ 
PropertyAnalyzer
 
: 47
getDefaultTypeFromAst
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Analyzer
 \ 
PropertyAnalyzer
 
: 36
getDefaultType
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Analyzer
 \ 
ClassAnalyzer
 
: 83
analyze
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Scope
 \ 
Index
 
: 47
getClass
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Services
 \ 
ReferenceTypeResolver
 
: 312
resolveNewCallReferenceType
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Services
 \ 
ReferenceTypeResolver
 
: 79
doResolve
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Services
 \ 
ReferenceTypeResolver
 
: 59
Dedoc\Scramble\Infer\Services\{closure}
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Type
 \ 
TypeWalker
 
: 118
map
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Services
 \ 
ReferenceTypeResolver
 
: 59
Dedoc\Scramble\Infer\Services\{closure}
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Services
 \ 
RecursionGuard
 
: 35
run
Dedoc
 \ 
Scramble
 \ 
Infer
 \ 
Services
 \ 
ReferenceTypeResolver
 
: 57
resolve
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataPropertySchemaTransformer
 
: 174
createDataType
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataPropertySchemaTransformer
 
: 148
transformLaravelDataTypeToInferType
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataPropertySchemaTransformer
 
: 81
toAllSchemas
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataToSchemaTransformer
 
: 47
transform
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
Generator
 \ 
DataSchemaExtension
 
: 61
toSchema
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 277
Dedoc\Scramble\Support\Generator\{closure}
Illuminate
 \ 
Support
 \ 
Collection
 
: 822
reduce
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 276
handleUsingExtensions
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 232
transform
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataPropertySchemaTransformer
 
: 85
toAllSchemas
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataToSchemaTransformer
 
: 47
transform
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
Generator
 \ 
DataSchemaExtension
 
: 61
toSchema
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 277
Dedoc\Scramble\Support\Generator\{closure}
Illuminate
 \ 
Support
 \ 
Collection
 
: 822
reduce
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 276
handleUsingExtensions
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 232
transform
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataPropertySchemaTransformer
 
: 85
toAllSchemas
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
DataToSchemaTransformer
 
: 47
transform
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
Generator
 \ 
DataSchemaExtension
 
: 61
toSchema
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 277
Dedoc\Scramble\Support\Generator\{closure}
Illuminate
 \ 
Support
 \ 
Collection
 
: 822
reduce
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 276
handleUsingExtensions
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 232
transform
Dedoc
 \ 
ScramblePro
 \ 
Extensions
 \ 
LaravelData
 \ 
Generator
 \ 
DataSchemaExtension
 
: 68
toResponse
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 354
Dedoc\Scramble\Support\Generator\{closure}
.unknown
0
array_reduce
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 347
handleResponseUsingExtensions
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
Generator
 \ 
TypeTransformer
 
: 303
toResponse
.unknown
0
array_map
Illuminate
 \ 
Support
 \ 
Arr
 
: 609
map
Illuminate
 \ 
Support
 \ 
Collection
 
: 799
map
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
OperationExtensions
 \ 
ResponseExtension
 
: 65
collectInferredResponses
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
OperationExtensions
 \ 
ResponseExtension
 
: 26
handle
Dedoc
 \ 
Scramble
 \ 
Support
 \ 
OperationBuilder
 
: 37
build
Dedoc
 \ 
Scramble
 \ 
Generator
 
: 234
routeToOperation
Dedoc
 \ 
Scramble
 \ 
Generator
 
: 61
Dedoc\Scramble\{closure}
.unknown
0
array_map
Illuminate
 \ 
Support
 \ 
Arr
 
: 609
map
Illuminate
 \ 
Support
 \ 
Collection
 
: 799
map
Dedoc
 \ 
Scramble
 \ 
Generator
 
: 59
__invoke
Dedoc
 \ 
Scramble
 \ 
ScrambleServiceProvider
 
: 291
Dedoc\Scramble\{closure}
Illuminate
 \ 
Routing
 \ 
CallableDispatcher
 
: 40
dispatch
Illuminate
 \ 
Routing
 \ 
Route
 
: 244
runCallable
Illuminate
 \ 
Routing
 \ 
Route
 
: 215
run
Illuminate
 \ 
Routing
 \ 
Router
 
: 808
Illuminate\Routing\{closure}
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 170
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Routing
 \ 
Middleware
 \ 
SubstituteBindings
 
: 51
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
View
 \ 
Middleware
 \ 
ShareErrorsFromSession
 
: 49
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Session
 \ 
Middleware
 \ 
StartSession
 
: 121
handleStatefulRequest
Illuminate
 \ 
Session
 \ 
Middleware
 \ 
StartSession
 
: 64
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Cookie
 \ 
Middleware
 \ 
AddQueuedCookiesToResponse
 
: 37
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Cookie
 \ 
Middleware
 \ 
EncryptCookies
 
: 75
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 127
then
Illuminate
 \ 
Routing
 \ 
Router
 
: 807
runRouteWithinStack
Illuminate
 \ 
Routing
 \ 
Router
 
: 786
runRoute
Illuminate
 \ 
Routing
 \ 
Router
 
: 750
dispatchToRoute
Illuminate
 \ 
Routing
 \ 
Router
 
: 739
dispatch
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Kernel
 
: 201
Illuminate\Foundation\Http\{closure}
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 170
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Middleware
 \ 
TransformsRequest
 
: 21
handle
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Middleware
 \ 
ConvertEmptyStringsToNull
 
: 31
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Middleware
 \ 
TransformsRequest
 
: 21
handle
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Middleware
 \ 
TrimStrings
 
: 51
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Http
 \ 
Middleware
 \ 
ValidatePostSize
 
: 27
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Middleware
 \ 
PreventRequestsDuringMaintenance
 
: 110
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Http
 \ 
Middleware
 \ 
HandleCors
 
: 62
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Http
 \ 
Middleware
 \ 
TrustProxies
 
: 58
handle
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 209
Illuminate\Pipeline\{closure}
Illuminate
 \ 
Pipeline
 \ 
Pipeline
 
: 127
then
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Kernel
 
: 176
sendRequestThroughRouter
Illuminate
 \ 
Foundation
 \ 
Http
 \ 
Kernel
 
: 145
handle
public
 / 
index
.php
 
: 51
[top]

slnw avatar Oct 24 '25 12:10 slnw

@slnw I think Scramble fails to analyze a default value of some property, but I don't see any properties in the shared code that would cause this issue.

Can you please also show the source of LaravelDataFakeInterface?

romalytvynenko avatar Oct 25 '25 07:10 romalytvynenko

it's not related, i removed all the instances of implements LaravelDataFakeInterface and it still gets this error :/

slnw avatar Oct 27 '25 15:10 slnw

@slnw Understood.

Can you please help me debug it?

For me to reproduce the core issue, I need to know the class source that causes this issue.

So dumping the class will help.

Can you please replace the line 175 in PropertyReflector (https://github.com/dedoc/scramble/blob/017e4ee31e2b93d93b58f21c08291257352f0719/src/Infer/Reflector/PropertyReflector.php#L175) to this code:

        try {
            $statements = $this->parser->parseContent($partialClass)->getStatements();
        } catch (\Throwable $e) {
            dump($e->getMessage());
            dump($this->name);
            dd($this->getClassReflector()->getSource());
        }

And then send me the output of these dumps.

The idea is to get the source of class that causes the issue.

Thanks!

romalytvynenko avatar Oct 27 '25 18:10 romalytvynenko

@slnw any updates on this? Have you been able to find the reason or get the source that causes the issue?

romalytvynenko avatar Nov 16 '25 07:11 romalytvynenko