diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,7 @@
 # Change log
 
-Scrod follows the [Package Versioning Policy](https://pvp.haskell.org).
+Scrod will follow the [Package Versioning Policy](https://pvp.haskell.org).
+While at major version 0, Scrod follows [CalVer](https://calver.org) instead.
+It uses the scheme `0.YYYY.M.D`.
+No stability is expected during this period.
 You can find release notes [on GitHub](https://github.com/tfausak/scrod/releases).
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -21,9 +21,10 @@
 
 | Asset | Description |
 | --- | --- |
-| `scrod-*-linux.tar.gz` | Linux binary |
-| `scrod-*-darwin.tar.gz` | macOS binary |
-| `scrod-*-win32.tar.gz` | Windows binary |
+| `scrod-*-linux-x86_64.tar.gz` | Linux x86_64 binary |
+| `scrod-*-linux-aarch64.tar.gz` | Linux AArch64 binary |
+| `scrod-*-darwin-aarch64.tar.gz` | macOS binary |
+| `scrod-*-win32-x86_64.tar.gz` | Windows binary |
 | `scrod-*.vsix` | VSCode extension for live documentation preview |
 | `scrod-*-wasm.tar.gz` | WASM build (for use in browsers) |
 | `scrod-*-wasi.tar.gz` | WASI build (for use with runtimes like Wasmtime) |
diff --git a/scrod.cabal b/scrod.cabal
--- a/scrod.cabal
+++ b/scrod.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.12
 name: scrod
-version: 0.2026.2.23
+version: 0.2026.3.4
 synopsis: Worse Haskell documentation
 description:
   Scrod generates documentation for Haskell modules, similar to Haddock. Unlike
@@ -176,6 +176,7 @@
     Scrod.Ghc.ParserOpts
     Scrod.Ghc.Platform
     Scrod.Ghc.PlatformMisc
+    Scrod.Ghc.PragmaInfo
     Scrod.Ghc.ToolSettings
     Scrod.Ghc.Uninitialized
     Scrod.Ghc.UnitId
diff --git a/source/library/Scrod/Convert/FromGhc.hs b/source/library/Scrod/Convert/FromGhc.hs
--- a/source/library/Scrod/Convert/FromGhc.hs
+++ b/source/library/Scrod/Convert/FromGhc.hs
@@ -26,6 +26,7 @@
 import qualified GHC.Parser.Annotation as Annotation
 import qualified GHC.Types.Basic as Basic
 import qualified GHC.Types.PkgQual as PkgQual
+import qualified GHC.Types.SafeHaskell as SafeHaskell
 import qualified GHC.Types.SourceText as SourceText
 import qualified GHC.Types.SrcLoc as SrcLoc
 import qualified GHC.Utils.Outputable as Outputable
@@ -72,15 +73,19 @@
 import qualified Scrod.Core.Version as Version
 import qualified Scrod.Core.Warning as Warning
 import qualified Scrod.Ghc.OnOff as OnOff
+import qualified Scrod.Ghc.PragmaInfo as PragmaInfo
 
 -- | Convert a parsed GHC module to the internal 'Module' type.
 fromGhc ::
   Bool ->
-  ( (Maybe Session.Language, [DynFlags.OnOff GhcExtension.Extension]),
+  ( PragmaInfo.PragmaInfo,
     SrcLoc.Located (Hs.HsModule Ghc.GhcPs)
   ) ->
   Either String Module.Module
-fromGhc isSignature ((language, extensions), lHsModule) = do
+fromGhc isSignature (pragmaInfo, lHsModule) = do
+  let language = PragmaInfo.language pragmaInfo
+      extensions = PragmaInfo.extensions pragmaInfo
+      safeHaskellMode = PragmaInfo.safeHaskell pragmaInfo
   version <- maybe (Left "invalid version") Right $ versionFromBase PackageInfo.version
   let (moduleDocumentation, moduleSince) = extractModuleDocAndSince lHsModule
       namedDocChunks = extractNamedDocChunks lHsModule
@@ -91,7 +96,7 @@
     Module.MkModule
       { Module.version = version,
         Module.language = languageFromGhc <$> language,
-        Module.extensions = extensionsToMap extensions,
+        Module.extensions = safeHaskellExtension safeHaskellMode <> extensionsToMap extensions,
         Module.documentation = moduleDocumentation,
         Module.since = moduleSince,
         Module.signature = isSignature,
@@ -136,6 +141,20 @@
   Map.fromListWith (\_ x -> x)
     . fmap (Tuple.swap . fmap extensionFromGhc . OnOff.onOff ((,) True) ((,) False))
 
+-- | Convert a Safe Haskell mode to an extension entry, if applicable.
+safeHaskellExtension :: SafeHaskell.SafeHaskellMode -> Map.Map Extension.Extension Bool
+safeHaskellExtension mode = case safeHaskellName mode of
+  Nothing -> Map.empty
+  Just name -> Map.singleton (Extension.MkExtension $ Text.pack name) True
+
+-- | Map a Safe Haskell mode to its extension name.
+safeHaskellName :: SafeHaskell.SafeHaskellMode -> Maybe String
+safeHaskellName mode = case mode of
+  SafeHaskell.Sf_Safe -> Just "Safe"
+  SafeHaskell.Sf_Unsafe -> Just "Unsafe"
+  SafeHaskell.Sf_Trustworthy -> Just "Trustworthy"
+  _ -> Nothing
+
 -- | Extract module name from the parsed module.
 extractModuleName ::
   SrcLoc.Located (Syntax.HsModule Ghc.GhcPs) ->
@@ -442,9 +461,8 @@
           case parentResult of
             Nothing -> pure []
             Just (parentItem, parentKey) -> do
-              argItems <- convertArguments (Just parentKey) (Annotation.getLocA lName) args
-              retItem <- convertReturnType (Just parentKey) (Annotation.getLocA lName) retType
-              pure $ [parentItem] <> argItems <> retItem
+              childItems <- convertArgumentsAndReturnType (Just parentKey) (Annotation.getLocA lName) args retType
+              pure $ parentItem : childItems
   Syntax.PatSynSig _ names _ ->
     let sigText = Names.extractSigSignature sig
         (args, retType) = SigArguments.extractSigArguments sig
@@ -461,9 +479,8 @@
           case parentResult of
             Nothing -> pure []
             Just (parentItem, parentKey) -> do
-              argItems <- convertArguments (Just parentKey) (Annotation.getLocA lName) args
-              retItem <- convertReturnType (Just parentKey) (Annotation.getLocA lName) retType
-              pure $ [parentItem] <> argItems <> retItem
+              childItems <- convertArgumentsAndReturnType (Just parentKey) (Annotation.getLocA lName) args retType
+              pure $ parentItem : childItems
   Syntax.FixSig _ (Syntax.FixitySig _ names (SyntaxBasic.Fixity prec dir)) ->
     let fixityDoc = Doc.Paragraph . Doc.String $ fixityDirectionToText dir <> Text.pack (" " <> show prec)
         combinedDoc = combineDoc doc fixityDoc
@@ -483,16 +500,20 @@
      in Maybe.maybeToList <$> Internal.mkItemM (Annotation.getLocA lDecl) Nothing Nothing doc docSince sigText ItemKind.CompletePragma
   _ -> Maybe.maybeToList <$> convertDeclWithDocM Nothing doc docSince (Names.extractSigName sig) Nothing lDecl
 
--- | Convert extracted argument data into child Argument items.
--- Returns no items if none of the arguments have documentation.
-convertArguments ::
+-- | Convert extracted argument and return-type data into child items.
+-- Shows all arguments and the return type as a group: if any argument
+-- or the return type has documentation, all are included; otherwise none.
+convertArgumentsAndReturnType ::
   Maybe ItemKey.ItemKey ->
   SrcLoc.SrcSpan ->
   [(Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs))] ->
+  Maybe (Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs)) ->
   Internal.ConvertM [Located.Located Item.Item]
-convertArguments parentKey srcSpan args
-  | any (Maybe.isJust . snd) args =
-      Maybe.catMaybes <$> traverse (convertOneArgument parentKey srcSpan) args
+convertArgumentsAndReturnType parentKey srcSpan args mRet
+  | any (Maybe.isJust . snd) args || maybe False (Maybe.isJust . snd) mRet = do
+      argItems <- Maybe.catMaybes <$> traverse (convertOneArgument parentKey srcSpan) args
+      retItem <- convertReturnType parentKey srcSpan mRet
+      pure $ argItems <> retItem
   | otherwise = pure []
 
 -- | Convert a single argument to an Argument item.
@@ -746,9 +767,8 @@
             case parentResult of
               Nothing -> pure []
               Just (methodItem, methodKey) -> do
-                argItems <- convertArguments (Just methodKey) (Annotation.getLocA lName) args
-                retItem <- convertReturnType (Just methodKey) (Annotation.getLocA lName) retType
-                pure $ [methodItem] <> argItems <> retItem
+                childItems <- convertArgumentsAndReturnType (Just methodKey) (Annotation.getLocA lName) args retType
+                pure $ methodItem : childItems
     _ -> pure []
   _ -> pure []
 
diff --git a/source/library/Scrod/Convert/FromGhc/SigArguments.hs b/source/library/Scrod/Convert/FromGhc/SigArguments.hs
--- a/source/library/Scrod/Convert/FromGhc/SigArguments.hs
+++ b/source/library/Scrod/Convert/FromGhc/SigArguments.hs
@@ -18,7 +18,8 @@
 -- | Extract argument types and their optional doc comments from a type
 -- signature. Walks the 'HsFunTy' chain, collecting each argument's
 -- pretty-printed type text and its 'LHsDoc' (if the argument was wrapped
--- in 'HsDocTy'). The return type is included only when it has documentation.
+-- in 'HsDocTy'). The return type is always included when the signature has
+-- function arrows.
 --
 -- Returns a pair of (arguments, optional return type).
 --
@@ -40,7 +41,7 @@
 
 -- | Skip through 'HsForAllTy' and 'HsQualTy' to reach the arrow chain,
 -- then extract arguments from the 'HsFunTy' chain. Returns the arguments
--- and the optional documented return type separately.
+-- and the return type separately.
 extractArgsFromBody ::
   Syntax.LHsType Ghc.GhcPs ->
   ( [(Text.Text, Maybe (HsDoc.LHsDoc Ghc.GhcPs))],
@@ -57,7 +58,7 @@
       let (args, ret) = extractArgsFromBody res
        in (extractArg arg : args, ret)
     _ -> ([], Just (extractArg lTy))
-  _ -> ([], Nothing)
+  _ -> ([], Just (extractArg lTy))
 
 -- | Extract the type text and optional doc comment from a single argument.
 -- If the argument is wrapped in 'HsDocTy', the doc is extracted and the
diff --git a/source/library/Scrod/Convert/ToHtml.hs b/source/library/Scrod/Convert/ToHtml.hs
--- a/source/library/Scrod/Convert/ToHtml.hs
+++ b/source/library/Scrod/Convert/ToHtml.hs
@@ -610,13 +610,13 @@
                         Just n -> [element "code" [("class", "text-break")] [Xml.text $ ItemName.unwrap n]]
                     )
                 ]
-                <> earlySignature
                 <> [ element
                        "div"
                        [("class", "mx-1")]
                        [ element "span" [("class", "badge " <> badgeColor)] [Xml.string $ kindToString kind]
                        ]
                    ]
+                <> earlySignature
                 <> lateSignature
                 <> [ element
                        "div"
diff --git a/source/library/Scrod/Extra/Builder.hs b/source/library/Scrod/Extra/Builder.hs
--- a/source/library/Scrod/Extra/Builder.hs
+++ b/source/library/Scrod/Extra/Builder.hs
@@ -1,5 +1,3 @@
-{- hlint ignore "Avoid restricted extensions" -}
-{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskellQuotes #-}
 
 module Scrod.Extra.Builder where
@@ -23,49 +21,57 @@
 spec :: (Applicative m, Monad n) => Spec.Spec m n -> n ()
 spec s = do
   Spec.named s 'toByteString $ do
+    let f = toByteString . Builder.stringUtf8
+    let b = ByteString.pack
+
     Spec.it s "works with an empty builder" $ do
-      Spec.assertEq s (toByteString "") ""
+      Spec.assertEq s (f "") $ b []
 
     Spec.it s "works with one byte" $ do
-      Spec.assertEq s (toByteString " \x0 ") " \x0 "
+      Spec.assertEq s (f " \x0 ") $ b [0x20, 0x00, 0x20]
 
     Spec.it s "works with two bytes" $ do
-      Spec.assertEq s (toByteString " \x80 ") " \xc2\x80 "
+      Spec.assertEq s (f " \x80 ") $ b [0x20, 0xc2, 0x80, 0x20]
 
     Spec.it s "works with three bytes" $ do
-      Spec.assertEq s (toByteString " \x800 ") " \xe0\xa0\x80 "
+      Spec.assertEq s (f " \x800 ") $ b [0x20, 0xe0, 0xa0, 0x80, 0x20]
 
     Spec.it s "works with four bytes" $ do
-      Spec.assertEq s (toByteString " \x10000 ") " \xf0\x90\x80\x80 "
+      Spec.assertEq s (f " \x10000 ") $ b [0x20, 0xf0, 0x90, 0x80, 0x80, 0x20]
 
   Spec.named s 'toText $ do
+    let f = toText . Builder.stringUtf8
+    let t = Text.pack
+
     Spec.it s "works with an empty builder" $ do
-      Spec.assertEq s (toText "") ""
+      Spec.assertEq s (f "") $ t ""
 
     Spec.it s "works with one byte" $ do
-      Spec.assertEq s (toText " \x0 ") " \x0 "
+      Spec.assertEq s (f " \x0 ") $ t " \x0 "
 
     Spec.it s "works with two bytes" $ do
-      Spec.assertEq s (toText " \x80 ") " \x80 "
+      Spec.assertEq s (f " \x80 ") $ t " \x80 "
 
     Spec.it s "works with three bytes" $ do
-      Spec.assertEq s (toText " \x800 ") " \x800 "
+      Spec.assertEq s (f " \x800 ") $ t " \x800 "
 
     Spec.it s "works with four bytes" $ do
-      Spec.assertEq s (toText " \x10000 ") " \x10000 "
+      Spec.assertEq s (f " \x10000 ") $ t " \x10000 "
 
   Spec.named s 'toString $ do
+    let f = toString . Builder.stringUtf8
+
     Spec.it s "works with an empty builder" $ do
-      Spec.assertEq s (toString "") ""
+      Spec.assertEq s (f "") ""
 
     Spec.it s "works with one byte" $ do
-      Spec.assertEq s (toString " \x0 ") " \x0 "
+      Spec.assertEq s (f " \x0 ") " \x0 "
 
     Spec.it s "works with two bytes" $ do
-      Spec.assertEq s (toString " \x80 ") " \x80 "
+      Spec.assertEq s (f " \x80 ") " \x80 "
 
     Spec.it s "works with three bytes" $ do
-      Spec.assertEq s (toString " \x800 ") " \x800 "
+      Spec.assertEq s (f " \x800 ") " \x800 "
 
     Spec.it s "works with four bytes" $ do
-      Spec.assertEq s (toString " \x10000 ") " \x10000 "
+      Spec.assertEq s (f " \x10000 ") " \x10000 "
diff --git a/source/library/Scrod/Ghc/DynFlags.hs b/source/library/Scrod/Ghc/DynFlags.hs
--- a/source/library/Scrod/Ghc/DynFlags.hs
+++ b/source/library/Scrod/Ghc/DynFlags.hs
@@ -159,7 +159,7 @@
       DynFlags.rtsOptsEnabled = Uninitialized.throw 'DynFlags.rtsOptsEnabled,
       DynFlags.rtsOptsSuggestions = Uninitialized.throw 'DynFlags.rtsOptsSuggestions,
       DynFlags.ruleCheck = Uninitialized.throw 'DynFlags.ruleCheck,
-      DynFlags.safeHaskell = SafeHaskell.Sf_Ignore,
+      DynFlags.safeHaskell = SafeHaskell.Sf_None,
       DynFlags.safeInfer = False,
       DynFlags.safeInferred = Uninitialized.throw 'DynFlags.safeInferred,
       DynFlags.simplPhases = Uninitialized.throw 'DynFlags.simplPhases,
diff --git a/source/library/Scrod/Ghc/Parse.hs b/source/library/Scrod/Ghc/Parse.hs
--- a/source/library/Scrod/Ghc/Parse.hs
+++ b/source/library/Scrod/Ghc/Parse.hs
@@ -24,6 +24,7 @@
 import qualified GHC.Parser as Parser
 import qualified GHC.Parser.Header as Header
 import qualified GHC.Parser.Lexer as Lexer
+import qualified GHC.Types.SafeHaskell as SafeHaskell
 import qualified GHC.Types.SourceError as SourceError
 import qualified GHC.Types.SrcLoc as SrcLoc
 import qualified GHC.Utils.Logger as Logger
@@ -35,6 +36,7 @@
 import qualified Scrod.Ghc.DynFlags as DynFlags
 import qualified Scrod.Ghc.OnOff as OnOff
 import qualified Scrod.Ghc.ParserOpts as ParserOpts
+import qualified Scrod.Ghc.PragmaInfo as PragmaInfo
 import qualified Scrod.Spec as Spec
 import qualified System.IO.Unsafe as Unsafe
 
@@ -44,14 +46,14 @@
   String ->
   Either
     String
-    ( (Maybe Session.Language, [DynFlags.OnOff Extension.Extension]),
+    ( PragmaInfo.PragmaInfo,
       SrcLoc.Located (Hs.HsModule Ghc.GhcPs)
     )
 parse isSignature extraOptions string = do
   let originalStringBuffer = StringBuffer.stringToStringBuffer string
   let cabalOptions = cabalExtensionOptions string
   let options = fmap SrcLoc.noLoc $ extraOptions <> cabalOptions
-  languageAndExtensions <- Bifunctor.first Exception.displayException $ discoverExtensions options originalStringBuffer
+  (languageAndExtensions, safeHaskellMode) <- Bifunctor.first Exception.displayException $ discoverExtensions options originalStringBuffer
   let extensions = uncurry resolveExtensions languageAndExtensions
   source <-
     if EnumSet.member Extension.Cpp extensions
@@ -65,7 +67,15 @@
   let parser = if isSignature then Parser.parseSignature else Parser.parseModule
   case Lexer.unP parser pState of
     Lexer.PFailed newPState -> Left . Outputable.showSDocUnsafe . Outputable.ppr $ Lexer.getPsErrorMessages newPState
-    Lexer.POk _ lHsModule -> pure (languageAndExtensions, lHsModule)
+    Lexer.POk _ lHsModule ->
+      let (lang, exts) = languageAndExtensions
+          pragmaInfo =
+            PragmaInfo.MkPragmaInfo
+              { PragmaInfo.language = lang,
+                PragmaInfo.extensions = exts,
+                PragmaInfo.safeHaskell = safeHaskellMode
+              }
+       in pure (pragmaInfo, lHsModule)
 
 cabalExtensionOptions :: String -> [String]
 cabalExtensionOptions = fmap ("-X" <>) . Cabal.discoverExtensions
@@ -75,7 +85,7 @@
   StringBuffer.StringBuffer ->
   Either
     SourceError.SourceError
-    (Maybe Flags.Language, [DynFlags.OnOff Extension.Extension])
+    ((Maybe Flags.Language, [DynFlags.OnOff Extension.Extension]), SafeHaskell.SafeHaskellMode)
 discoverExtensions cabalOptions =
   Unsafe.unsafePerformIO
     . Exception.try
@@ -84,11 +94,11 @@
 discoverExtensionsIO ::
   [SrcLoc.Located String] ->
   StringBuffer.StringBuffer ->
-  IO (Maybe Flags.Language, [DynFlags.OnOff Extension.Extension])
+  IO ((Maybe Flags.Language, [DynFlags.OnOff Extension.Extension]), SafeHaskell.SafeHaskellMode)
 discoverExtensionsIO cabalOptions stringBuffer = do
   logger <- Logger.initLogger
   (dynFlags, _, _) <- Session.parseDynamicFilePragma logger DynFlags.empty $ cabalOptions <> discoverOptions stringBuffer
-  pure (DynFlags.language dynFlags, DynFlags.extensions dynFlags)
+  pure ((DynFlags.language dynFlags, DynFlags.extensions dynFlags), DynFlags.safeHaskell dynFlags)
 
 discoverOptions :: StringBuffer.StringBuffer -> [SrcLoc.Located String]
 discoverOptions stringBuffer =
@@ -112,8 +122,10 @@
 spec :: (Applicative m, Monad n) => Spec.Spec m n -> n ()
 spec s = do
   Spec.named s 'parse $ do
+    let f = PragmaInfo.MkPragmaInfo
+
     Spec.it s "succeeds with empty input" $ do
-      Spec.assertEq s (fst <$> parse False [] "") $ Right (Nothing, [])
+      Spec.assertEq s (fst <$> parse False [] "") $ Right (f Nothing [] SafeHaskell.Sf_None)
 
     Spec.it s "fails with invalid input" $ do
       Spec.assertEq s (fst <$> parse False [] "!") $ Left "{Resolved: ErrorWithoutFlag\n ErrorWithoutFlag\n   parse error on input `!'}"
@@ -122,22 +134,31 @@
       Spec.assertEq s (fst <$> parse False [] "{-# language Unknown #-}") $ Left "<interactive>:1:14: error: [GHC-46537]\n    Unsupported extension: Unknown"
 
     Spec.it s "succeeds with a language" $ do
-      Spec.assertEq s (fst <$> parse False [] "{-# language Haskell98 #-}") $ Right (Just Session.Haskell98, [])
+      Spec.assertEq s (fst <$> parse False [] "{-# language Haskell98 #-}") $ Right (f (Just Session.Haskell98) [] SafeHaskell.Sf_None)
 
     Spec.it s "succeeds with an enabled extension" $ do
-      Spec.assertEq s (fst <$> parse False [] "{-# language CPP #-}") $ Right (Nothing, [Session.On Extension.Cpp])
+      Spec.assertEq s (fst <$> parse False [] "{-# language CPP #-}") $ Right (f Nothing [Session.On Extension.Cpp] SafeHaskell.Sf_None)
 
     Spec.it s "succeeds with a disabled extension" $ do
-      Spec.assertEq s (fst <$> parse False [] "{-# language NoCPP #-}") $ Right (Nothing, [Session.Off Extension.Cpp])
+      Spec.assertEq s (fst <$> parse False [] "{-# language NoCPP #-}") $ Right (f Nothing [Session.Off Extension.Cpp] SafeHaskell.Sf_None)
 
     Spec.it s "succeeds with a signature" $ do
-      Spec.assertEq s (fst <$> parse True [] "signature Foo where") $ Right (Nothing, [])
+      Spec.assertEq s (fst <$> parse True [] "signature Foo where") $ Right (f Nothing [] SafeHaskell.Sf_None)
 
     Spec.it s "succeeds with cabal script header extension" $ do
-      Spec.assertEq s (fst <$> parse False [] "{- cabal:\ndefault-extensions: CPP\n-}") $ Right (Nothing, [Session.On Extension.Cpp])
+      Spec.assertEq s (fst <$> parse False [] "{- cabal:\ndefault-extensions: CPP\n-}") $ Right (f Nothing [Session.On Extension.Cpp] SafeHaskell.Sf_None)
 
     Spec.it s "succeeds with extra options" $ do
-      Spec.assertEq s (fst <$> parse False ["-XOverloadedStrings"] "") $ Right (Nothing, [Session.On Extension.OverloadedStrings])
+      Spec.assertEq s (fst <$> parse False ["-XOverloadedStrings"] "") $ Right (f Nothing [Session.On Extension.OverloadedStrings] SafeHaskell.Sf_None)
 
     Spec.it s "succeeds with cabal script header and LANGUAGE pragma" $ do
-      Spec.assertEq s (fst <$> parse False [] "{- cabal:\ndefault-extensions: CPP\n-}\n{-# language OverloadedStrings #-}") $ Right (Nothing, [Session.On Extension.OverloadedStrings, Session.On Extension.Cpp])
+      Spec.assertEq s (fst <$> parse False [] "{- cabal:\ndefault-extensions: CPP\n-}\n{-# language OverloadedStrings #-}") $ Right (f Nothing [Session.On Extension.OverloadedStrings, Session.On Extension.Cpp] SafeHaskell.Sf_None)
+
+    Spec.it s "succeeds with Safe" $ do
+      Spec.assertEq s (fst <$> parse False [] "{-# language Safe #-}") $ Right (f Nothing [] SafeHaskell.Sf_Safe)
+
+    Spec.it s "succeeds with Unsafe" $ do
+      Spec.assertEq s (fst <$> parse False [] "{-# language Unsafe #-}") $ Right (f Nothing [] SafeHaskell.Sf_Unsafe)
+
+    Spec.it s "succeeds with Trustworthy" $ do
+      Spec.assertEq s (fst <$> parse False [] "{-# language Trustworthy #-}") $ Right (f Nothing [] SafeHaskell.Sf_Trustworthy)
diff --git a/source/library/Scrod/Ghc/PragmaInfo.hs b/source/library/Scrod/Ghc/PragmaInfo.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Scrod/Ghc/PragmaInfo.hs
@@ -0,0 +1,13 @@
+module Scrod.Ghc.PragmaInfo where
+
+import qualified GHC.Driver.DynFlags as DynFlags
+import qualified GHC.Driver.Session as Session
+import qualified GHC.LanguageExtensions.Type as Extension
+import qualified GHC.Types.SafeHaskell as SafeHaskell
+
+data PragmaInfo = MkPragmaInfo
+  { language :: Maybe Session.Language,
+    extensions :: [DynFlags.OnOff Extension.Extension],
+    safeHaskell :: SafeHaskell.SafeHaskellMode
+  }
+  deriving (Eq, Show)
diff --git a/source/library/Scrod/TestSuite/Integration.hs b/source/library/Scrod/TestSuite/Integration.hs
--- a/source/library/Scrod/TestSuite/Integration.hs
+++ b/source/library/Scrod/TestSuite/Integration.hs
@@ -153,6 +153,15 @@
         "{-# language NoOverloadedStrings #-}"
         [("/extensions/OverloadedStrings", "false")]
 
+    Spec.it s "works with Safe" $ do
+      check s "{-# language Safe #-}" [("/extensions/Safe", "true")]
+
+    Spec.it s "works with Unsafe" $ do
+      check s "{-# language Unsafe #-}" [("/extensions/Unsafe", "true")]
+
+    Spec.it s "works with Trustworthy" $ do
+      check s "{-# language Trustworthy #-}" [("/extensions/Trustworthy", "true")]
+
   Spec.describe s "documentation" $ do
     Spec.it s "defaults to Empty" $ do
       check s "" [("/documentation/type", "\"Empty\"")]
@@ -2655,11 +2664,34 @@
           ("/items/0/value/name", "\"f\""),
           ("/items/0/value/signature", "\"a -> a\""),
           ("/items/0/value/documentation/type", "\"Empty\""),
-          ("/items/1/value/kind/type", "\"ReturnType\""),
+          ("/items/1/value/kind/type", "\"Argument\""),
           ("/items/1/value/parentKey", "0"),
           ("/items/1/value/signature", "\"a\""),
+          ("/items/1/value/documentation/type", "\"Empty\""),
+          ("/items/2/value/kind/type", "\"ReturnType\""),
+          ("/items/2/value/parentKey", "0"),
+          ("/items/2/value/signature", "\"a\""),
+          ("/items/2/value/documentation/type", "\"Paragraph\""),
+          ("/items/2/value/documentation/value/value", "\"lost\"")
+        ]
+
+    Spec.it s "function with arg doc but no return doc" $ do
+      check
+        s
+        "f :: a {- ^ x -} -> b"
+        [ ("/items/0/value/kind/type", "\"Function\""),
+          ("/items/0/value/name", "\"f\""),
+          ("/items/0/value/signature", "\"a -> b\""),
+          ("/items/0/value/documentation/type", "\"Empty\""),
+          ("/items/1/value/kind/type", "\"Argument\""),
+          ("/items/1/value/parentKey", "0"),
+          ("/items/1/value/signature", "\"a\""),
           ("/items/1/value/documentation/type", "\"Paragraph\""),
-          ("/items/1/value/documentation/value/value", "\"lost\"")
+          ("/items/1/value/documentation/value/value", "\"x \""),
+          ("/items/2/value/kind/type", "\"ReturnType\""),
+          ("/items/2/value/parentKey", "0"),
+          ("/items/2/value/signature", "\"b\""),
+          ("/items/2/value/documentation/type", "\"Empty\"")
         ]
 
     Spec.it s "data constructor with arg doc has argument children" $ do
