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

Better plugin error infrastructure #3717

Merged
merged 30 commits into from
Jul 29, 2023

Conversation

joyfulmantis
Copy link
Collaborator

@joyfulmantis joyfulmantis commented Jul 14, 2023

This PR is based on fendors, he did a lot of the initial work originally in PR #3659. I finished up what he started, and then tried a slightly different approach, where I deemphasize hierarchical errors and instead try to define the common "errors" that occur in plugins centrally in the hls-plugin-api.
What I have also done, is have all method handlers now return PluginError instead of ResponseError, this allows us to define specific error types that correspond to concrete error responses we want to send to the client and specific ways we want to log the error.
This also allows us to keep all the ExceptT infrastructure the fendor has written, give much more accurate error response codes to the server, and log non-serious errors such as rule failures with info or even debug, while still being able to log more serious errors such as uncaught exceptions or bugs in the client or plugin with error.

@joyfulmantis joyfulmantis changed the title Draft: Better plugin logger infrastructure Better plugin error infrastructure Jul 22, 2023
Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR looks great to me already!

Lots of code clean-ups as well, thank you very much!

@@ -154,7 +116,7 @@ getSelectionRanges file positions = do
in fromMaybe defaultSelectionRange . findPosition pos $ codeRange

-- 'positionMapping' should be applied to the output ranges before returning them
maybeToExceptT SelectionRangeOutputPositionMappingFailure . MaybeT . pure $
maybeToExceptT (PluginDependencyFailed "toCurrentSelectionRange") . MaybeT . pure $
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I think PluginHandlerFailed is a good name.

@@ -48,6 +48,7 @@ selectionRangeGoldenTest testName positions = goldenGitDiff testName (testDataDi
let res = resp ^. result
pure $ fmap (showSelectionRangesForTest . absorbNull) res
case res of
Left (ResponseError (InL LSPErrorCodes_ContentModified) _ _) -> pure ""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand, why do we produce a LSPErrorCodes_ContentModified for the empty result in this plugin? Is it because the only reason why we might produce an empty result is because we have modified the content before resolving SMethod_TextDocumentSelectionRange ?

@fendor
Copy link
Collaborator

fendor commented Jul 28, 2023

I do think we may be forced to accept the presence of exceptions in Action, but I'm not sure.

I think Actions use AsyncExceptions for cutting off stale request, which is likely unavoidable. I don't think we should/have to remove all exceptions from Action, but removing them from the plugin code is sensible.

@joyfulmantis
Copy link
Collaborator Author

I do think we may be forced to accept the presence of exceptions in Action, but I'm not sure.

I think Actions use AsyncExceptions for cutting off stale request, which is likely unavoidable. I don't think we should/have to remove all exceptions from Action, but removing them from the plugin code is sensible code.

Do you know if the underscore functions and returning Nothing in the rules are functionally equivalent? At least if that's the case we can dump the underscore functions and convert rule users of them to MaybeT

@VeryMilkyJoe VeryMilkyJoe removed their request for review July 28, 2023 14:46
@fendor
Copy link
Collaborator

fendor commented Jul 28, 2023

Do you know if the underscore functions and returning Nothing in the rules are functionally equivalent?

Sorry, I do not

@joyfulmantis joyfulmantis added the merge me Label to trigger pull request merge label Jul 29, 2023
@joyfulmantis joyfulmantis enabled auto-merge (squash) July 29, 2023 15:27
@joyfulmantis joyfulmantis enabled auto-merge (squash) July 29, 2023 15:27
Copy link
Collaborator

@michaelpj michaelpj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking pretty good now!

ghcide/test/exe/ExceptionTests.hs Show resolved Hide resolved
ghcide/test/exe/ExceptionTests.hs Show resolved Hide resolved
ghcide/test/exe/ExceptionTests.hs Show resolved Hide resolved
ghcide/src/Development/IDE/Core/Shake.hs Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/HLS.hs Show resolved Hide resolved

-- See Note [PluginError]
data PluginError
= -- |PluginInternalError should be used if something has gone horribly wrong.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not because of the user's invalid state, then arguably it's a bug, and should never happen

I feel like this is a pretty strong statement and I don't know that I would want to commit to it. I think I'd feel a bit more comfortable with a generic "error but not a bug" error, especially since I think being more prescriptive here isn't really buying us anything.

It also seems like "is this a bug in our code or not" is the dimension that we actually care about?

toErrorCode (PluginInvalidUserState _) = InL LSPErrorCodes_RequestFailed
-- PluginRequestRefused should never be a argument to `toResponseError`, as
-- it should be dealt with in `extensiblePlugins`, but this is here to make
-- this function complete
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... but also I think this is pretty logical

toErrorCode (PluginRuleFailed _) = InL LSPErrorCodes_RequestFailed
toErrorCode PluginStaleResolve = InL LSPErrorCodes_ContentModified

toPriority :: PluginError -> Priority
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haddock! good to explain when this should be used

-- ----------------------------------------------------------------------------

-- |ExceptT version of `runAction`, takes a ExceptT Action
runActionE :: MonadIO m => String -> IdeState -> ExceptT e Action a -> ExceptT e m a
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could consider generalizing these functions to MonadError rather than ExceptT. Not sure that buys us much, I probably would instinctively do it, but I'm used to programming in a very mtl-y way!

@joyfulmantis joyfulmantis merged commit 8d7555c into haskell:master Jul 29, 2023
41 of 43 checks passed
@fendor fendor mentioned this pull request Aug 8, 2023
19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge me Label to trigger pull request merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants