Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error reporting when targetting the wrong Request class #54015

Closed
nicolas-grekas opened this issue Feb 20, 2024 · 8 comments · Fixed by #54107
Closed

Improve error reporting when targetting the wrong Request class #54015

nicolas-grekas opened this issue Feb 20, 2024 · 8 comments · Fixed by #54107
Labels
Good first issue Ideal for your first contribution! (some Symfony experience may be required) Help wanted Issues and PRs which are looking for volunteers to complete them.

Comments

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Feb 20, 2024

When I create a controller action with a Request $request argument, the IDE will suggest me to use Symfony\Component\BrowserKit\Request first. If I'm not careful and select this, I'll get an error saying this:

Cannot autowire argument $request of "App\Controller[...]()": it references class "Symfony\Component\BrowserKit\Request" but no such service exists.

That's not good enough. Instead, it'd be better to tell something like "did you mean to use Symfony\Component\HttpFoundation\Request instead?". The same could happen with any other "Request" class of course.

The current error message comes from ServiceValueResolver trying to access an errored "request" service. This looks wrong also since this prevents any other resolver (lower priority ones) from resolving the argument. The error I would expect instead is the one thrown by ArgumentResolver itself when the resolver chain is over:

Controller "App\Controller[...]" requires that you provide a value for the "$request" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or there is a non-optional argument after this one.

My proposal to fix both issues would be to introduce a special kind of exceptions that ArgumentResolver would use to compute its error message if no other resolver resolved the argument.

Then, we would make RequestValueResolver throw this exception when a type ends with Request but has the wrong namespace for example.

WDYT?

Help wanted :)

@nicolas-grekas nicolas-grekas added Good first issue Ideal for your first contribution! (some Symfony experience may be required) Help wanted Issues and PRs which are looking for volunteers to complete them. labels Feb 20, 2024
@nicolas-grekas
Copy link
Member Author

I figured out that RemoveEmptyControllerArgumentLocatorsPass could also remove all errored services in non-debug mode, so that we don't generated locators that only contain errored references.

@ilyachase
Copy link
Contributor

ilyachase commented Feb 27, 2024

Hi @nicolas-grekas I'm trying to tackle the issue and on the 7.1 branch when trying to reproduce, I'm getting this error right away:

Controller "App\Controller\LuckyController::number" requires that you provide a value for the "$request" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or there is a non-optional argument after this one.

that comes from vendor/symfony/http-kernel/Controller/ArgumentResolver.php:102. I'm not getting an error from ServiceValueResolver.

Could you please post a full trace of the error so I could trace back why it's not happening the same way for me?

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Feb 28, 2024

I'm doing these steps to reproduce:

symfony new test --webapp
cd test
symfony make:controller hello
# then add Symfony\Component\BrowserKit\Request as argument to HelloController::index()

Going to /hello yields "Cannot autowire argument $request [...]"

@ilyachase
Copy link
Contributor

ilyachase commented Feb 28, 2024

@nicolas-grekas I reproduced it, thanks. Now the question regarding this part:

My proposal to fix both issues would be to introduce a special kind of exceptions that ArgumentResolver would use to compute its error message if no other resolver resolved the argument. Then, we would make RequestValueResolver throw this exception when a type ends with Request but has the wrong namespace for example.

Problem is, ServiceValueResolver breaks the chain by throwing an exception whenever it fails to fetch a service, and there is no logic inside ArgumentResolver to handle that, while other resolvers return [] in such cases. So I see two ways:

  1. Collect exceptions from resolvers inside ArgumentResolver and then use them to somehow compute a final exception (maybe using exceptions chain). But then there is no consistency - some resolvers return [], some throw an exception.
  2. Fix ServiceValueResolver so instead of throwing a RuntimeException, it returns an empty array as the rest of the resolvers, but then make so ArgumentResolver throws a RuntimeException with the same message by default, with a special case for Request arguments.

I personally prefer second way but I may lack context since it's my first contribution.

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Feb 28, 2024

You've go it right. The first way is basically what I describe in this PR so that's my preference. It improves things by providing a way for resolvers to give more accurate feedback in case an argument could not be found but a resolver "thought" it was still the one targeted.

@ilyachase
Copy link
Contributor

@nicolas-grekas Awesome! I will follow the first way then. Quick question - in a hypothetical case when multiple resolvers throw an exception, is there a preferred way how to combine them in a single one?

@nicolas-grekas
Copy link
Member Author

Not exactly, maybe a list with an intro saying "Here are some possible hints:". We'll see :)

@ilyachase
Copy link
Contributor

ilyachase commented Feb 29, 2024

@nicolas-grekas I've created a PR. The error will look like this:

image

Let me know if you prefer another format 👍

@fabpot fabpot closed this as completed Mar 9, 2024
fabpot added a commit that referenced this issue Mar 9, 2024
…e wrong Request class (ilyachase)

This PR was squashed before being merged into the 7.1 branch.

Discussion
----------

[HttpKernel] Improve error reporting when requiring the wrong Request class

| Q             | A
| ------------- | ---
| Branch?       | 7.1
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Issues        | Fix #54015
| License       | MIT

`FailedToResolveValueException` is introduced to be used inside any implementer of `ValueResolverInterface` to specify why exactly a value cannot be resolved. It's later used in `ArgumentResolver` to compute an exception if no value resolvers can resolve the value.

Commits
-------

1cfc723 [HttpKernel] Improve error reporting when requiring the wrong Request class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Good first issue Ideal for your first contribution! (some Symfony experience may be required) Help wanted Issues and PRs which are looking for volunteers to complete them.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants