Skip to content

Commit 97c7660

Browse files
authored
Merge pull request #509 from haskell/mpj/fix-notification-messages
Fix parsing of notifications with missing params
2 parents 8ab6604 + 11b58c4 commit 97c7660

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

lsp-types/src/Language/LSP/Protocol/Message/Types.hs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,19 @@ data TNotificationMessage (m :: Method f Notification) =
108108
deriving stock instance Eq (MessageParams m) => Eq (TNotificationMessage m)
109109
deriving stock instance Show (MessageParams m) => Show (TNotificationMessage m)
110110

111+
{- Note [Missing 'params']
112+
The 'params' field on requrests and notificaoins may be omitted according to the
113+
JSON-RPC spec, but that doesn't quite work the way we want with the generic aeson
114+
instance. Even if the 'MessageParams' type family happens to resolve to a 'Maybe',
115+
we handle it generically and so we end up asserting that it must be present.
116+
117+
We fix this in a slightly dumb way by just adding the field in if it is missing,
118+
set to null (which parses correctly for those 'Maybe' parameters also).
119+
-}
120+
111121
instance (FromJSON (MessageParams m), FromJSON (SMethod m)) => FromJSON (TNotificationMessage m) where
112-
parseJSON = genericParseJSON lspOptions
122+
-- See Note [Missing 'params']
123+
parseJSON = genericParseJSON lspOptions . addNullField "params"
113124
instance (ToJSON (MessageParams m)) => ToJSON (TNotificationMessage m) where
114125
toJSON = genericToJSON lspOptions
115126
toEncoding = genericToEncoding lspOptions
@@ -126,6 +137,7 @@ deriving stock instance Eq (MessageParams m) => Eq (TRequestMessage m)
126137
deriving stock instance Show (MessageParams m) => Show (TRequestMessage m)
127138

128139
instance (FromJSON (MessageParams m), FromJSON (SMethod m)) => FromJSON (TRequestMessage m) where
140+
-- See Note [Missing 'params']
129141
parseJSON = genericParseJSON lspOptions . addNullField "params"
130142
instance (ToJSON (MessageParams m)) => ToJSON (TRequestMessage m) where
131143
toJSON = genericToJSON lspOptions

lsp-types/test/JsonSpec.hs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ main = hspec spec
4141
spec :: Spec
4242
spec = do
4343
describe "dispatcher" jsonSpec
44-
describe "ResponseMessage" responseMessageSpec
44+
describe "RequestMessage" requestMessageSpec
45+
describe "ResponseMessage" responseMessageSpec
46+
describe "NotificationMesssage" notificationMessageSpec
4547

4648
-- ---------------------------------------------------------------------
4749

@@ -61,16 +63,20 @@ jsonSpec = do
6163
`shouldNotBe` Nothing
6264

6365

66+
requestMessageSpec :: Spec
67+
requestMessageSpec = do
68+
describe "edge cases" $ do
69+
it "handles missing params field" $ do
70+
J.eitherDecode "{ \"jsonrpc\": \"2.0\", \"id\": 15, \"method\": \"shutdown\"}"
71+
`shouldBe` Right (TRequestMessage "2.0" (IdInt 15) SMethod_Shutdown Nothing)
72+
6473
responseMessageSpec :: Spec
6574
responseMessageSpec = do
6675
describe "edge cases" $ do
6776
it "decodes result = null" $ do
6877
let input = "{\"jsonrpc\": \"2.0\", \"id\": 123, \"result\": null}"
6978
in J.decode input `shouldBe` Just
7079
((TResponseMessage "2.0" (Just (IdInt 123)) (Right $ InL J.Null)) :: TResponseMessage 'Method_WorkspaceExecuteCommand)
71-
it "handles missing params field" $ do
72-
J.eitherDecode "{ \"jsonrpc\": \"2.0\", \"id\": 15, \"method\": \"shutdown\"}"
73-
`shouldBe` Right (TRequestMessage "2.0" (IdInt 15) SMethod_Shutdown Nothing)
7480
describe "invalid JSON" $ do
7581
it "throws if neither result nor error is present" $ do
7682
(J.eitherDecode "{\"jsonrpc\":\"2.0\",\"id\":1}" :: Either String (TResponseMessage 'Method_Initialize))
@@ -82,6 +88,13 @@ responseMessageSpec = do
8288
`shouldSatisfy`
8389
(either (\err -> "Error in $: both error and result cannot be present" `isPrefixOf` err) (\_ -> False))
8490

91+
notificationMessageSpec :: Spec
92+
notificationMessageSpec = do
93+
describe "edge cases" $ do
94+
it "handles missing params field" $ do
95+
J.eitherDecode "{ \"jsonrpc\": \"2.0\", \"method\": \"exit\"}"
96+
`shouldBe` Right (TNotificationMessage "2.0" SMethod_Exit Nothing)
97+
8598
-- ---------------------------------------------------------------------
8699

87100
propertyJsonRoundtrip :: (Eq a, Show a, J.ToJSON a, J.FromJSON a) => a -> Property

0 commit comments

Comments
 (0)