Skip to content

fix: add context code action with trailing comment #4649

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

Merged
merged 3 commits into from
Jul 8, 2025

Conversation

guibou
Copy link
Collaborator

@guibou guibou commented Jul 6, 2025

For now it is just a repro for #4648

Tested with ghc 9.12.2, it seems to fail as expected:

[guillaume@gecko:~/srcs/haskell-language-server]$ cabal run hls-refactor-plugin-tests -- -p haddock
Configuration is affected by the following files:
- cabal.project
refactor
  code actions
    add function constraint
      preexisting constraint, with haddock comment in type signature: FAIL (0.84s)
        plugins/hls-refactor-plugin/test/Main.hs:3122:
        expected: "module Testing where\ndata Pair a b = Pair a b\neq \n    :: (Eq a, Eq b)\n    =>\n   -- This is the first argument\n   Pair a b ->\n   -- And this is the second\n   Pair a b ->\n   -- And this is the result\n   Bool\neq (Pair x y) (Pair x' y') = x == x' && y == y'\n"
         but got: "module Testing where\ndata Pair a b = Pair a b\neq \n    :: (Eq a,\n\n   -- This is the first argument Eq b)\n    =>\n\n   Pair a b ->\n   -- And this is the second\n   Pair a b ->\n   -- And this is the result\n   Bool\neq (Pair x y) (Pair x' y') = x == x' && y == y'\n"

1 out of 1 tests failed (0.84s)

(see how Eq b) is introduced inside the haddock comment This is the first argument.

I'm pushing the branch for reference and to test with other GHC version (I'm too lazy to build that locally, cabal cache was hot for ghc 9.12.2).

@guibou guibou requested a review from santiweight as a code owner July 6, 2025 08:16
@guibou guibou force-pushed the 4648_invalid_add_constraint_code_action branch from 24493db to bf75a82 Compare July 6, 2025 14:12
@guibou guibou changed the title feat(test): add a repro for 4648 fix: add context code action with trailing comment Jul 6, 2025
@guibou
Copy link
Collaborator Author

guibou commented Jul 6, 2025

The current code was tested with GHC 9.12 and does fix the broken generated code.

However it can:

  • drop comments
  • move all inline comments from the inside of the context block to the end of the context block.

The difficulty comes from the way comments are stored in the AST: they are not stored "inline", interleaved with the ast nodes. Instead, they are stored in a list of comment attached to an ast node and each comment is localised with it absolute position in the stream as well as the position of the latest taken (in order to introduce whitespaces).

Consider the following example:

bar :: 
  -- BEFORE
  {- yoto -} (Monad m {- yiti -}){- yutu -} => {- yete -} -- Trailing
  -- After
  m ()
  • BEFORE and yoto are attached to the bar node
  • yiti is attached to the Monad m node
  • yutu, yete, Trailing and After are attached to the (Monad m) node (e.g. the constraint) and the pretty printer layouts everything based on the anchor and position of the items.

In order to insert a value after Monad m while keeping everything correctly linked, we need to move the anchor and span for yutu, yete, Trailing and After.

Note that I'm discovering the annotation API right now and maybe there is a solution for that. Especially, there is https://hackage-content.haskell.org/package/ghc-9.8.1/docs/GHC-Parser-Annotation.html#t:AnchorOperation AnchorOperation which seems to be done especially for this.

@guibou guibou force-pushed the 4648_invalid_add_constraint_code_action branch from 2c1799f to 7b223aa Compare July 7, 2025 10:14
@guibou
Copy link
Collaborator Author

guibou commented Jul 7, 2025

I've spent a few hours trying to understand the exact-print semantic in order to correctly handle all cases and I admit that I'm failing here. I'm not sure that exact-print is working as expected, or not or what I'm supposed to do next.

What should we do now?

@fendor, you did accept the MR, thank you. Should we merge considering that:

  • This MR does not break any test
  • This MR fixs newly introduces tests for GHC 9.10 and GHC 9.12
  • There is a risk of moving inline commen a the end of the block without user noticing it. However this won't only happen for GHC 9.10 and 9.12, for which the code action was previously generating completely wrong code.

What is better, a code action which does not work at all, or a code action which works, but in some context, can (silently) move comments ?

Note: I think I have fixed the CI issues related to formatting, however some failures may not be related to this MR.

@fendor
Copy link
Collaborator

fendor commented Jul 7, 2025

however some failures may not be related to this MR.

The bench failures seem to be caused by this PR:

plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Util.hs:40:9: error: [GHC-88464]
Error:     Variable not in scope:
      createDirectoryIfMissing :: Bool -> t0 -> IO a0
   |
40 |         createDirectoryIfMissing True "/tmp/hls"
   |     

Should we merge considering that:

Yes, I think this PR is a net-improvement over the status quo

@guibou guibou force-pushed the 4648_invalid_add_constraint_code_action branch from 7b223aa to 92158db Compare July 7, 2025 12:25
@fendor
Copy link
Collaborator

fendor commented Jul 7, 2025

CircleCI complains about a missing warning.
Counter-intuitvely, are the stack jobs the only jobs that run with the -pedantic flag:

haskell-language-server>     Pattern match(es) are non-exhaustive
haskell-language-server>     In an equation for ‘moveCommentsToTheEnd’:
haskell-language-server>         Patterns of type ‘EpAnn ann’ not matched: EpAnnNotUsed
haskell-language-server>     |
haskell-language-server> 223 | moveCommentsToTheEnd (EpAnn entry anns (EpaComments priors)) = EpAnn entry anns (EpaCommentsBalanced { priorComments = [], followingComments = priors})

haskell-language-server>     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

and it complains that moveCommentsToTheEnd is not used. So, either you need to prefix it with _ or use CPP to fix this error.

This is for GHC 9.6.6.

This closes #4648.

When adding constraint to a context which is followed by a comment, such
as:

```
foo :: (Monad m) =>
   -- | This is a comment
   m ()
```

The comment annotation is anchored to the previous token, which is `=>`
in this context. If we add a new constraint in the context, the newly
generated content goes beyond the anchor and, depending on GHC version,
or ghc-exactprint (the reason is not fully understood), the comment is
printed BEFORE the new constraint, leading to invalid syntax, such as
`(Monad m -- | This is a comment , Applicative m =>)`

This commit moves all the comment of the block at the end of the block
using the `followingComments` of `EpAnnComments`.

It seems super adhoc, but actually, consider the following example:

```haskell

bar :: 
  -- BEFORE
  {- yoto -} (Monad m {- yiti -}){- yutu -} => {- yete -} -- Trailing
  -- After
  m ()
```

Comment `BEFORE` and `yoto` are attached to the previous block. Comment
`yiti` is attached to `Monad m`.

The comments `yiti`, `yutu`, `yete`, `Trailing` and `After` are all
attached to this block and will hence be moved after the block.

However this is not an easy task, all the associated comments should be
moved by the relevant offset.

TODO: do that instead.
@guibou guibou force-pushed the 4648_invalid_add_constraint_code_action branch from 92158db to 80be44c Compare July 7, 2025 17:00
@guibou guibou merged commit 8cac5fb into master Jul 8, 2025
36 checks passed
@guibou
Copy link
Collaborator Author

guibou commented Jul 8, 2025

@fendor

I do really apologize for the unnecessary ping pong.

I first saw the warning about formatting (e.g. my import lines were not formatted correctly) and I fixed that super quickly and messed up my rebase / squash and hence pushed an invalid commit. However in the meantime I did not really understood the reason for the failure and thought it was unrelated to my current MR.

Anyway, thank you for the review, I pressed the merge button.

@guibou guibou deleted the 4648_invalid_add_constraint_code_action branch July 8, 2025 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants