From e5dbe97e1da8ea49a500cd223968e2298c962276 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Wed, 11 Mar 2026 09:06:05 +0000 Subject: [PATCH] spec references in code --- spec/modules/README.md | 19 ++++++++++++++----- src/Simplex/Messaging/Compression.hs | 3 +++ src/Simplex/Messaging/Encoding.hs | 1 + src/Simplex/Messaging/Parsers.hs | 4 +++- src/Simplex/Messaging/Session.hs | 3 +++ src/Simplex/Messaging/Util.hs | 6 ++++++ 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/spec/modules/README.md b/spec/modules/README.md index 1d18b32e3..9f057b903 100644 --- a/spec/modules/README.md +++ b/spec/modules/README.md @@ -119,14 +119,23 @@ See [rcv-services](../rcv-services.md) for the end-to-end service subscription f ``` ### Source → module doc -Comment above function in source: + +Add `-- spec:` comments as part of the module documentation work — when you document something non-obvious, add the link in source at the same time. Two levels: + +**Module-level** (below the module declaration): when the Overview section has value. ```haskell --- spec: spec/modules/Simplex/Messaging/Server.md#subscribeServiceMessages --- Delivers buffered messages for all service queues after SUBS (SI-SVC-07) -subscribeServiceMessages :: ... +module Simplex.Messaging.Util (...) where +-- spec: spec/modules/Simplex/Messaging/Util.md ``` -Only add `-- spec:` comments where the module doc actually has something to say. Don't add links to "No non-obvious behavior" docs. +**Function-level** (above the function): when that function has a doc entry worth pointing to. +```haskell +-- spec: spec/modules/Simplex/Messaging/Util.md#catchOwn +-- Catches all exceptions except async cancellations (misleading name) +catchOwn :: ... +``` + +Only add `-- spec:` comments where the module doc actually says something the code doesn't. Don't add links to "No non-obvious behavior" docs or to entries that merely restate the source. ## Topic candidate tracking diff --git a/src/Simplex/Messaging/Compression.hs b/src/Simplex/Messaging/Compression.hs index 20000ded3..32430bc88 100644 --- a/src/Simplex/Messaging/Compression.hs +++ b/src/Simplex/Messaging/Compression.hs @@ -1,6 +1,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +-- spec: spec/modules/Simplex/Messaging/Compression.md module Simplex.Messaging.Compression ( Compressed, maxLengthPassthrough, @@ -42,6 +43,8 @@ compress1 bs | B.length bs <= maxLengthPassthrough = Passthrough bs | otherwise = Compressed . Large $ Z1.compress compressionLevel bs +-- spec: spec/modules/Simplex/Messaging/Compression.md#decompress1 +-- Decompression bomb protection: refuses data without declared size or exceeding limit decompress1 :: Int -> Compressed -> Either String ByteString decompress1 limit = \case Passthrough bs -> Right bs diff --git a/src/Simplex/Messaging/Encoding.hs b/src/Simplex/Messaging/Encoding.hs index d069e5518..4381ff8bb 100644 --- a/src/Simplex/Messaging/Encoding.hs +++ b/src/Simplex/Messaging/Encoding.hs @@ -7,6 +7,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} +-- spec: spec/modules/Simplex/Messaging/Encoding.md module Simplex.Messaging.Encoding ( Encoding (..), Tail (..), diff --git a/src/Simplex/Messaging/Parsers.hs b/src/Simplex/Messaging/Parsers.hs index 7acbec743..3a2fd07fc 100644 --- a/src/Simplex/Messaging/Parsers.hs +++ b/src/Simplex/Messaging/Parsers.hs @@ -4,6 +4,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} +-- spec: spec/modules/Simplex/Messaging/Parsers.md module Simplex.Messaging.Parsers ( base64P, parse, @@ -105,7 +106,8 @@ enumJSON tagModifier = J.allNullaryToStringTag = True } --- used in platform-specific encoding, includes tag for single-field encoding of sum types to allow conversion to tagged objects +-- spec: spec/modules/Simplex/Messaging/Parsers.md#sumTypeJSON +-- Platform-dependent: ObjectWithSingleField on Darwin+swiftJSON, TaggedObject elsewhere sumTypeJSON :: (String -> String) -> J.Options #if defined(darwin_HOST_OS) && defined(swiftJSON) sumTypeJSON = singleFieldJSON_ $ Just SingleFieldJSONTag diff --git a/src/Simplex/Messaging/Session.hs b/src/Simplex/Messaging/Session.hs index ff5d7e0a0..bb082b1bb 100644 --- a/src/Simplex/Messaging/Session.hs +++ b/src/Simplex/Messaging/Session.hs @@ -2,6 +2,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} +-- spec: spec/modules/Simplex/Messaging/Session.md module Simplex.Messaging.Session ( SessionVar (..), getSessVar, @@ -32,6 +33,8 @@ getSessVar sessSeq sessKey vs sessionVarTs = maybe (Left <$> newSessionVar) (pur TM.insert sessKey v vs pure v +-- spec: spec/modules/Simplex/Messaging/Session.md#removeSessVar +-- Compare-and-swap: only removes if sessionVarId matches, preventing stale removal removeSessVar :: Ord k => SessionVar a -> k -> TMap k (SessionVar a) -> STM () removeSessVar v sessKey vs = TM.lookup sessKey vs >>= \case diff --git a/src/Simplex/Messaging/Util.hs b/src/Simplex/Messaging/Util.hs index 6c1937144..abbf5a3b3 100644 --- a/src/Simplex/Messaging/Util.hs +++ b/src/Simplex/Messaging/Util.hs @@ -3,6 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +-- spec: spec/modules/Simplex/Messaging/Util.md module Simplex.Messaging.Util ( AnyError (..), (<$?>), @@ -294,6 +295,7 @@ allFinally :: (AnyError e, MonadUnliftIO m) => ExceptT e m a -> ExceptT e m b -> allFinally action final = tryAllErrors action >>= \r -> final >> except r {-# INLINE allFinally #-} +-- spec: spec/modules/Simplex/Messaging/Util.md#isOwnException isOwnException :: E.SomeException -> Bool isOwnException e = case E.fromException e of Just StackOverflow -> True @@ -303,16 +305,20 @@ isOwnException e = case E.fromException e of _ -> False {-# INLINE isOwnException #-} +-- spec: spec/modules/Simplex/Messaging/Util.md#isAsyncCancellation isAsyncCancellation :: E.SomeException -> Bool isAsyncCancellation e = case E.fromException e of Just (_ :: SomeAsyncException) -> not $ isOwnException e Nothing -> False {-# INLINE isAsyncCancellation #-} +-- spec: spec/modules/Simplex/Messaging/Util.md#catchOwn +-- Catches all exceptions EXCEPT async cancellations (name is misleading) catchOwn' :: IO a -> (E.SomeException -> IO a) -> IO a catchOwn' action handleInternal = action `E.catch` \e -> if isAsyncCancellation e then E.throwIO e else handleInternal e {-# INLINE catchOwn' #-} +-- spec: spec/modules/Simplex/Messaging/Util.md#catchOwn catchOwn :: MonadUnliftIO m => m a -> (E.SomeException -> m a) -> m a catchOwn action handleInternal = withRunInIO $ \run ->