diff --git a/cabal-gild.cabal b/cabal-gild.cabal
--- a/cabal-gild.cabal
+++ b/cabal-gild.cabal
@@ -11,7 +11,7 @@
 maintainer: Taylor Fausak
 name: cabal-gild
 synopsis: Formats package descriptions.
-version: 1.4.0.1
+version: 1.5.0.0
 
 source-repository head
   type: git
@@ -82,6 +82,7 @@
     CabalGild.Unstable.Class.MonadWalk
     CabalGild.Unstable.Class.MonadWrite
     CabalGild.Unstable.Exception.CheckFailure
+    CabalGild.Unstable.Exception.DuplicateOption
     CabalGild.Unstable.Exception.InvalidLeniency
     CabalGild.Unstable.Exception.InvalidMode
     CabalGild.Unstable.Exception.InvalidOption
@@ -95,6 +96,7 @@
     CabalGild.Unstable.Extra.Either
     CabalGild.Unstable.Extra.Field
     CabalGild.Unstable.Extra.FieldLine
+    CabalGild.Unstable.Extra.FilePath
     CabalGild.Unstable.Extra.List
     CabalGild.Unstable.Extra.ModuleName
     CabalGild.Unstable.Extra.Name
diff --git a/source/library/CabalGild/Unstable/Action/AttachComments.hs b/source/library/CabalGild/Unstable/Action/AttachComments.hs
--- a/source/library/CabalGild/Unstable/Action/AttachComments.hs
+++ b/source/library/CabalGild/Unstable/Action/AttachComments.hs
@@ -55,7 +55,7 @@
 
 -- | Attaches comments to a section argument. Note that section arguments
 -- cannot actually have comments attached. That's because section arguments
--- must be on -- the same line as the section name, so all comments will end up
+-- must be on the same line as the section name, so all comments will end up
 -- attached to the name.
 sectionArg ::
   (Ord p) =>
diff --git a/source/library/CabalGild/Unstable/Action/EvaluatePragmas.hs b/source/library/CabalGild/Unstable/Action/EvaluatePragmas.hs
--- a/source/library/CabalGild/Unstable/Action/EvaluatePragmas.hs
+++ b/source/library/CabalGild/Unstable/Action/EvaluatePragmas.hs
@@ -4,6 +4,7 @@
 import qualified CabalGild.Unstable.Exception.InvalidOption as InvalidOption
 import qualified CabalGild.Unstable.Exception.UnknownOption as UnknownOption
 import qualified CabalGild.Unstable.Extra.FieldLine as FieldLine
+import qualified CabalGild.Unstable.Extra.FilePath as FilePath
 import qualified CabalGild.Unstable.Extra.ModuleName as ModuleName
 import qualified CabalGild.Unstable.Extra.Name as Name
 import qualified CabalGild.Unstable.Extra.String as String
@@ -26,7 +27,6 @@
 import qualified Distribution.Utils.Generic as Utils
 import qualified System.Console.GetOpt as GetOpt
 import qualified System.FilePath as FilePath
-import qualified System.FilePath.Windows as FilePath.Windows
 
 -- | High level wrapper around 'field' that makes this action easier to compose
 -- with other actions.
@@ -76,15 +76,15 @@
   let (excs, incs) = Either.partitionEithers flgs
   mapM_ (Exception.throwM . UnknownOption.fromString) opts
   mapM_ (Exception.throwM . InvalidOption.fromString) errs
-  let root = FilePath.dropTrailingPathSeparator . normalize $ FilePath.takeDirectory p
+  let root = FilePath.dropTrailingPathSeparator . clean $ FilePath.takeDirectory p
       directories =
         List.nubOrd
-          . fmap normalize
+          . fmap clean
           $ if null args then ["."] else args
-      exclusions = List.nubOrd $ fmap normalize excs
+      exclusions = List.nubOrd $ fmap clean excs
       inclusions =
         List.nubOrd
-          . fmap normalize
+          . fmap clean
           $ if null incs then ["**"] else incs
   files <- Trans.lift $ MonadWalk.walk root inclusions exclusions
   let comments = concatMap (snd . FieldLine.annotation) fls
@@ -95,7 +95,7 @@
         DiscoverTarget.Modules ->
           zipWith ModuleName.toFieldLine ((,) position <$> comments : repeat [])
             . Maybe.mapMaybe (toModuleName directories)
-            $ Maybe.mapMaybe (stripAnyExtension extensions . normalize) files
+            $ Maybe.mapMaybe (stripAnyExtension extensions . clean) files
         DiscoverTarget.Files ->
           zipWith
             (\a -> Fields.FieldLine a . String.toUtf8)
@@ -108,11 +108,9 @@
           else n
   pure $ Fields.Field name fieldLines
 
-normalize :: FilePath -> FilePath
-normalize =
-  FilePath.normalise
-    . FilePath.joinPath
-    . FilePath.Windows.splitDirectories
+-- | Converts separators into POSIX format and then normalizes the result.
+clean :: FilePath -> FilePath
+clean = FilePath.normalise . FilePath.toPosixSeparators
 
 -- | These are the names of the fields that can have this action applied to
 -- them.
diff --git a/source/library/CabalGild/Unstable/Action/FormatFields.hs b/source/library/CabalGild/Unstable/Action/FormatFields.hs
--- a/source/library/CabalGild/Unstable/Action/FormatFields.hs
+++ b/source/library/CabalGild/Unstable/Action/FormatFields.hs
@@ -77,6 +77,8 @@
             else sas
      in Fields.Section n newSas $ fmap (field csv) fs
 
+-- | Returns 'True' if the field name is a conditional. @if@ is always one, and
+-- @elif@ is one for Cabal versions 2.2 and later.
 isConditional :: CabalSpecVersion.CabalSpecVersion -> Fields.Name p -> Bool
 isConditional csv n =
   Name.isIf n
@@ -102,6 +104,8 @@
         . PrettyPrint.renderStyle style
         $ pretty csv r
 
+-- | Collects comments from the given field lines (see 'collectComments') and
+-- attaches them all to the first one.
 floatComments ::
   p ->
   [Fields.FieldLine (p, [c])] ->
@@ -112,6 +116,8 @@
     (True : repeat False)
     fls
 
+-- | Collects all comments from the given field lines. Their relative order
+-- will be maintained.
 collectComments :: [Fields.FieldLine (p, [c])] -> [c]
 collectComments = concatMap (snd . FieldLine.annotation)
 
diff --git a/source/library/CabalGild/Unstable/Class/MonadWalk.hs b/source/library/CabalGild/Unstable/Class/MonadWalk.hs
--- a/source/library/CabalGild/Unstable/Class/MonadWalk.hs
+++ b/source/library/CabalGild/Unstable/Class/MonadWalk.hs
@@ -3,9 +3,14 @@
 import qualified System.FilePattern as FilePattern
 import qualified System.FilePattern.Directory as FilePattern
 
--- | A wrapper around 'FilePattern.getDirectoryFilesIgnore'.
+-- | A 'Monad' that can also "walk" a directory to discover files within it.
 class (Monad m) => MonadWalk m where
+  -- | This has the same signature and semantics as
+  -- 'FilePattern.getDirectoryFilesIgnore'. The first argument is the directory
+  -- to walk. The second argument is a list of patterns to include. The third
+  -- argument is a list of patterns to exclude.
   walk :: FilePath -> [FilePattern.FilePattern] -> [FilePattern.FilePattern] -> m [FilePath]
 
+-- | Uses 'FilePattern.getDirectoryFilesIgnore'.
 instance MonadWalk IO where
   walk = FilePattern.getDirectoryFilesIgnore
diff --git a/source/library/CabalGild/Unstable/Exception/DuplicateOption.hs b/source/library/CabalGild/Unstable/Exception/DuplicateOption.hs
new file mode 100644
--- /dev/null
+++ b/source/library/CabalGild/Unstable/Exception/DuplicateOption.hs
@@ -0,0 +1,22 @@
+module CabalGild.Unstable.Exception.DuplicateOption where
+
+import qualified Control.Monad.Catch as Exception
+
+-- | This exception is thrown when an option is specified more than once.
+data DuplicateOption
+  = DuplicateOption
+  { option :: String,
+    old :: String,
+    new :: String
+  }
+  deriving (Eq, Show)
+
+instance Exception.Exception DuplicateOption where
+  displayException x =
+    "overriding option --"
+      <> option x
+      <> " (before: "
+      <> old x
+      <> ", after: "
+      <> new x
+      <> ")"
diff --git a/source/library/CabalGild/Unstable/Exception/SpecifiedOutputWithCheckMode.hs b/source/library/CabalGild/Unstable/Exception/SpecifiedOutputWithCheckMode.hs
--- a/source/library/CabalGild/Unstable/Exception/SpecifiedOutputWithCheckMode.hs
+++ b/source/library/CabalGild/Unstable/Exception/SpecifiedOutputWithCheckMode.hs
@@ -1,5 +1,6 @@
 module CabalGild.Unstable.Exception.SpecifiedOutputWithCheckMode where
 
+import qualified CabalGild.Unstable.Type.Flag as Flag
 import qualified Control.Monad.Catch as Exception
 
 -- | This exception is thrown when the user specifies the output while using
@@ -9,4 +10,10 @@
   deriving (Eq, Show)
 
 instance Exception.Exception SpecifiedOutputWithCheckMode where
-  displayException = const "cannot use --output when --mode is check"
+  displayException =
+    const $
+      "cannot use --"
+        <> Flag.outputOption
+        <> " when --"
+        <> Flag.modeOption
+        <> " is check"
diff --git a/source/library/CabalGild/Unstable/Exception/SpecifiedStdinWithFileInput.hs b/source/library/CabalGild/Unstable/Exception/SpecifiedStdinWithFileInput.hs
--- a/source/library/CabalGild/Unstable/Exception/SpecifiedStdinWithFileInput.hs
+++ b/source/library/CabalGild/Unstable/Exception/SpecifiedStdinWithFileInput.hs
@@ -1,5 +1,6 @@
 module CabalGild.Unstable.Exception.SpecifiedStdinWithFileInput where
 
+import qualified CabalGild.Unstable.Type.Flag as Flag
 import qualified Control.Monad.Catch as Exception
 
 -- | This exception is thrown when the user specifies the STDIN name while
@@ -10,4 +11,10 @@
   deriving (Eq, Show)
 
 instance Exception.Exception SpecifiedStdinWithFileInput where
-  displayException = const "cannot use --stdin when --input is a file"
+  displayException =
+    const $
+      "cannot use --"
+        <> Flag.stdinOption
+        <> " when --"
+        <> Flag.inputOption
+        <> " is a file"
diff --git a/source/library/CabalGild/Unstable/Extra/CharParsing.hs b/source/library/CabalGild/Unstable/Extra/CharParsing.hs
--- a/source/library/CabalGild/Unstable/Extra/CharParsing.hs
+++ b/source/library/CabalGild/Unstable/Extra/CharParsing.hs
@@ -3,8 +3,10 @@
 import qualified Distribution.Compat.CharParsing as Parse
 import qualified Distribution.Parsec as Parsec
 
+-- | Wraps the given parser in parentheses.
 parens :: (Parsec.CabalParsing m) => m a -> m a
 parens = Parse.between (token "(") (token ")")
 
+-- | Parses the given string followed by any amount of blank space.
 token :: (Parsec.CabalParsing m) => String -> m ()
 token s = Parse.string s *> Parse.spaces
diff --git a/source/library/CabalGild/Unstable/Extra/FilePath.hs b/source/library/CabalGild/Unstable/Extra/FilePath.hs
new file mode 100644
--- /dev/null
+++ b/source/library/CabalGild/Unstable/Extra/FilePath.hs
@@ -0,0 +1,9 @@
+module CabalGild.Unstable.Extra.FilePath where
+
+import qualified System.FilePath.Posix as Posix
+import qualified System.FilePath.Windows as Windows
+
+-- | Converts a 'FilePath' with potential Windows-style separators in it to one
+-- with only POSIX-style separators.
+toPosixSeparators :: FilePath -> FilePath
+toPosixSeparators = Posix.joinPath . Windows.splitDirectories
diff --git a/source/library/CabalGild/Unstable/Type/Condition.hs b/source/library/CabalGild/Unstable/Type/Condition.hs
--- a/source/library/CabalGild/Unstable/Type/Condition.hs
+++ b/source/library/CabalGild/Unstable/Type/Condition.hs
@@ -9,6 +9,8 @@
 import qualified Text.Parsec.Expr as PE
 import qualified Text.PrettyPrint as PrettyPrint
 
+-- | Similar to 'Distribution.Types.Condition.Condition', but retains
+-- information about parentheses.
 data Condition a
   = Par (Condition a)
   | Not (Condition a)
@@ -18,6 +20,9 @@
   | Var a
   deriving (Eq, Show)
 
+-- | Similar to 'Distribution.Fields.ConfVar.parseConditionConfVar', but
+-- parameterized on the variable parser. Also it's a normal parser rather than
+-- a function on section arguments.
 parseCondition :: Parsec.ParsecParser a -> Parsec.ParsecParser (Condition a)
 parseCondition parseVariable = Parsec.PP $ \csv -> do
   let operators :: (P.Stream s m Char) => PE.OperatorTable s u m (Condition b)
@@ -38,6 +43,7 @@
       )
       csv
 
+-- | Parses a literal 'Condition'.
 parseLit :: (Parsec.CabalParsing m) => m Bool
 parseLit =
   Parse.choice
@@ -47,6 +53,8 @@
       False <$ Parse.token "false"
     ]
 
+-- | Pretty-prints a 'Condition' using the given pretty-printer for the
+-- variables.
 prettyCondition :: (a -> PrettyPrint.Doc) -> Condition a -> PrettyPrint.Doc
 prettyCondition f x =
   case x of
diff --git a/source/library/CabalGild/Unstable/Type/Context.hs b/source/library/CabalGild/Unstable/Type/Context.hs
--- a/source/library/CabalGild/Unstable/Type/Context.hs
+++ b/source/library/CabalGild/Unstable/Type/Context.hs
@@ -14,6 +14,7 @@
 import qualified Control.Monad.Catch as Exception
 import qualified Data.Char as Char
 import qualified Data.List as List
+import qualified Data.Maybe as Maybe
 import qualified Data.Version as Version
 import qualified Paths_cabal_gild as This
 import qualified System.Console.GetOpt as GetOpt
@@ -40,7 +41,7 @@
 fromConfig config = do
   let version = Version.showVersion This.version
 
-  Monad.when (Optional.withDefault False $ Config.help config) $ do
+  Monad.when (Maybe.fromMaybe False . Optional.toMaybe $ Config.help config) $ do
     let header =
           unlines
             [ "cabal-gild version " <> version,
@@ -52,7 +53,7 @@
       $ GetOpt.usageInfo header Flag.options
     Exception.throwM Exit.ExitSuccess
 
-  Monad.when (Optional.withDefault False $ Config.version config) $ do
+  Monad.when (Maybe.fromMaybe False . Optional.toMaybe $ Config.version config) $ do
     MonadLog.logLn version
     Exception.throwM Exit.ExitSuccess
 
@@ -66,15 +67,15 @@
       Exception.throwM SpecifiedOutputWithCheckMode.SpecifiedOutputWithCheckMode
     _ -> pure ()
 
-  let theInput = Optional.withDefault Input.Stdin $ Config.input config
+  let theInput = Maybe.fromMaybe Input.Stdin . Optional.toMaybe $ Config.input config
       filePath = case theInput of
         Input.Stdin -> "."
         Input.File f -> f
   pure
     Context
-      { crlf = Optional.withDefault Leniency.Lenient $ Config.crlf config,
+      { crlf = Maybe.fromMaybe Leniency.Lenient . Optional.toMaybe $ Config.crlf config,
         input = theInput,
-        mode = Optional.withDefault Mode.Format $ Config.mode config,
-        output = Optional.withDefault Output.Stdout $ Config.output config,
-        stdin = Optional.withDefault filePath $ Config.stdin config
+        mode = Maybe.fromMaybe Mode.Format . Optional.toMaybe $ Config.mode config,
+        output = Maybe.fromMaybe Output.Stdout . Optional.toMaybe $ Config.output config,
+        stdin = Maybe.fromMaybe filePath . Optional.toMaybe $ Config.stdin config
       }
diff --git a/source/library/CabalGild/Unstable/Type/DiscoverTarget.hs b/source/library/CabalGild/Unstable/Type/DiscoverTarget.hs
--- a/source/library/CabalGild/Unstable/Type/DiscoverTarget.hs
+++ b/source/library/CabalGild/Unstable/Type/DiscoverTarget.hs
@@ -1,6 +1,11 @@
 module CabalGild.Unstable.Type.DiscoverTarget where
 
+-- | This type determines the type of thing to search for when using the
+-- @discover@ pragma.
 data DiscoverTarget
-  = Files
-  | Modules
+  = -- | Discovers any file at all.
+    Files
+  | -- | Discovers only Haskell modules, which also includes signatures and
+    -- various pre-processors.
+    Modules
   deriving (Eq, Show)
diff --git a/source/library/CabalGild/Unstable/Type/Flag.hs b/source/library/CabalGild/Unstable/Type/Flag.hs
--- a/source/library/CabalGild/Unstable/Type/Flag.hs
+++ b/source/library/CabalGild/Unstable/Type/Flag.hs
@@ -1,10 +1,13 @@
 module CabalGild.Unstable.Type.Flag where
 
+import qualified CabalGild.Unstable.Exception.DuplicateOption as DuplicateOption
 import qualified CabalGild.Unstable.Exception.InvalidOption as InvalidOption
 import qualified CabalGild.Unstable.Exception.UnexpectedArgument as UnexpectedArgument
 import qualified CabalGild.Unstable.Exception.UnknownOption as UnknownOption
 import qualified Control.Monad.Catch as Exception
 import qualified Data.Foldable as Foldable
+import qualified Data.Map as Map
+import qualified Data.Maybe as Maybe
 import qualified System.Console.GetOpt as GetOpt
 
 -- | A flag, which represents a command line option. The values associated with
@@ -46,36 +49,54 @@
       "",
     GetOpt.Option
       []
-      ["crlf"]
+      [crlfOption]
       (GetOpt.ReqArg CRLF "LENIENCY")
       "Sets the CRLF handling mode. Must be either 'lenient' or 'strict'.\nDefault: 'lenient'",
     GetOpt.Option
       ['i']
-      ["input"]
+      [inputOption]
       (GetOpt.ReqArg Input "FILE")
       "Sets the input file. Use '-' for standard input (STDIN).\nDefault: '-'",
     GetOpt.Option
       []
-      ["io"]
+      [ioOption]
       (GetOpt.ReqArg IO "FILE")
       "Shortcut for setting both the input and output files.",
     GetOpt.Option
       ['m']
-      ["mode"]
+      [modeOption]
       (GetOpt.ReqArg Mode "MODE")
       "Sets the mode. Must be either 'check' or 'format'.\nDefault: 'format'",
     GetOpt.Option
       ['o']
-      ["output"]
+      [outputOption]
       (GetOpt.ReqArg Output "FILE")
       "Sets the output file. Use '-' for standard output (STDOUT).\nDefault: '-'",
     GetOpt.Option
       ['s']
-      ["stdin"]
+      [stdinOption]
       (GetOpt.ReqArg Stdin "FILE")
       "Sets the path to the input file when using STDIN.\nDefault: '.'"
   ]
 
+crlfOption :: String
+crlfOption = "crlf"
+
+inputOption :: String
+inputOption = "input"
+
+ioOption :: String
+ioOption = "io"
+
+modeOption :: String
+modeOption = "mode"
+
+outputOption :: String
+outputOption = "output"
+
+stdinOption :: String
+stdinOption = "stdin"
+
 -- | Converts a list of command line arguments into a list of flags. If there
 -- are any unexpected arguments, invalid options, or unknown options, an
 -- exception will be thrown.
@@ -85,4 +106,26 @@
   Foldable.traverse_ (Exception.throwM . UnexpectedArgument.fromString) args
   Foldable.traverse_ (Exception.throwM . InvalidOption.fromString) errs
   Foldable.traverse_ (Exception.throwM . UnknownOption.fromString) opts
+  detectDuplicateOptions flgs
   pure flgs
+
+detectDuplicateOptions :: (Exception.MonadThrow m) => [Flag] -> m ()
+detectDuplicateOptions =
+  let toWarnings o l =
+        fmap (uncurry . flip $ DuplicateOption.DuplicateOption o)
+          . reverse
+          . filter (uncurry (/=))
+          $ zip l (drop 1 l)
+      fromFlag f = case f of
+        CRLF s -> Just (crlfOption, [s])
+        Input s -> Just (inputOption, [s])
+        IO s -> Just (ioOption, [s])
+        Mode s -> Just (modeOption, [s])
+        Output s -> Just (outputOption, [s])
+        Stdin s -> Just (stdinOption, [s])
+        _ -> Nothing
+   in Foldable.traverse_ Exception.throwM
+        . concatMap (uncurry toWarnings)
+        . Map.toAscList
+        . Map.fromListWith (<>)
+        . Maybe.mapMaybe fromFlag
diff --git a/source/library/CabalGild/Unstable/Type/Mixin.hs b/source/library/CabalGild/Unstable/Type/Mixin.hs
--- a/source/library/CabalGild/Unstable/Type/Mixin.hs
+++ b/source/library/CabalGild/Unstable/Type/Mixin.hs
@@ -24,6 +24,7 @@
 instance Pretty.Pretty Mixin where
   pretty = Pretty.pretty . unwrap
 
+-- | Sorts the 'Mixin.mixinIncludeRenaming' field using 'sortIncludeRenaming'.
 sortMixin :: Mixin.Mixin -> Mixin.Mixin
 sortMixin x =
   Mixin.Mixin
@@ -32,6 +33,8 @@
       Mixin.mixinIncludeRenaming = sortIncludeRenaming $ Mixin.mixinIncludeRenaming x
     }
 
+-- | Sorts both 'IncludeRenaming.includeProvidesRn' and
+-- 'IncludeRenaming.includeRequiresRn' fields using 'sortModuleRenaming'.
 sortIncludeRenaming :: IncludeRenaming.IncludeRenaming -> IncludeRenaming.IncludeRenaming
 sortIncludeRenaming x =
   IncludeRenaming.IncludeRenaming
@@ -39,6 +42,8 @@
       IncludeRenaming.includeRequiresRn = sortModuleRenaming $ IncludeRenaming.includeRequiresRn x
     }
 
+-- | Sorts both 'ModuleRenaming.HidingRenaming' and
+-- 'ModuleRenaming.ModuleRenaming' variants.
 sortModuleRenaming :: ModuleRenaming.ModuleRenaming -> ModuleRenaming.ModuleRenaming
 sortModuleRenaming x = case x of
   ModuleRenaming.DefaultRenaming -> ModuleRenaming.DefaultRenaming
diff --git a/source/library/CabalGild/Unstable/Type/Optional.hs b/source/library/CabalGild/Unstable/Type/Optional.hs
--- a/source/library/CabalGild/Unstable/Type/Optional.hs
+++ b/source/library/CabalGild/Unstable/Type/Optional.hs
@@ -8,13 +8,12 @@
   | Specific a
   deriving (Eq, Show)
 
--- | Uses the provided default value if the optional is 'Default', otherwise
--- gets the value out of the 'Specific'. Similar to 'Data.Maybe.fromMaybe'.
-withDefault :: a -> Optional a -> a
-withDefault = flip optional id
+-- | Converts a 'Maybe' value into an 'Optional' one.
+fromMaybe :: Maybe a -> Optional a
+fromMaybe = maybe Default Specific
 
--- | Basic destructor, similar to 'maybe'.
-optional :: b -> (a -> b) -> Optional a -> b
-optional b f o = case o of
-  Default -> b
-  Specific s -> f s
+-- | Converts an 'Optional' value into a 'Maybe' one.
+toMaybe :: Optional a -> Maybe a
+toMaybe o = case o of
+  Default -> Nothing
+  Specific s -> Just s
diff --git a/source/library/CabalGild/Unstable/Type/Variable.hs b/source/library/CabalGild/Unstable/Type/Variable.hs
--- a/source/library/CabalGild/Unstable/Type/Variable.hs
+++ b/source/library/CabalGild/Unstable/Type/Variable.hs
@@ -11,6 +11,8 @@
 import qualified Distribution.Types.VersionRange as VersionRange
 import qualified Text.PrettyPrint as PrettyPrint
 
+-- | Similar to 'Distribution.Types.ConfVar.ConfVar', but with different
+-- parsing and pretty-printing behavior.
 data Variable
   = Arch System.Arch
   | Flag Flag.FlagName
@@ -18,6 +20,7 @@
   | Os System.OS
   deriving (Eq, Show)
 
+-- | Parses a 'Variable'. This is generally as permissive as possible.
 parseVariable :: (Parsec.CabalParsing m) => m Variable
 parseVariable =
   Parse.choice
@@ -27,14 +30,17 @@
       Os <$> parseOs
     ]
 
+-- | Parses an 'Arch'.
 parseArch :: (Parsec.CabalParsing m) => m System.Arch
 parseArch =
   fmap (System.classifyArch System.Permissive) $
     Parse.token "arch" *> Parse.parens parseIdent
 
+-- | Parses a 'Flag'.
 parseFlag :: (Parsec.CabalParsing m) => m Flag.FlagName
 parseFlag = Parse.token "flag" *> Parse.parens (Parsec.parsec <* Parse.spaces)
 
+-- | Parses an 'Impl'.
 parseImpl :: (Parsec.CabalParsing m) => m (Compiler.CompilerFlavor, VersionRange.VersionRange)
 parseImpl = do
   Parse.token "impl"
@@ -45,16 +51,21 @@
       <*> Parse.option VersionRange.anyVersion Parsec.parsec
       <* Parse.spaces
 
+-- | Parses an 'Os'.
 parseOs :: (Parsec.CabalParsing m) => m System.OS
 parseOs =
   fmap (System.classifyOS System.Permissive) $
     Parse.token "os" *> Parse.parens parseIdent
 
+-- | Parses an identifier. This is more permissive than anything provided by
+-- Cabal. Any run of alphanumeric characters, underscores, or hyphens is
+-- considered an identifier.
 parseIdent :: (Parsec.CabalParsing m) => m String
 parseIdent =
   let isIdent c = Char.isAlphaNum c || c == '_' || c == '-'
    in Parse.munch1 isIdent <* Parse.spaces
 
+-- | Pretty-prints a 'Variable'.
 prettyVariable :: Variable -> PrettyPrint.Doc
 prettyVariable x =
   case x of
diff --git a/source/library/CabalGild/Unstable/Type/VersionRange.hs b/source/library/CabalGild/Unstable/Type/VersionRange.hs
--- a/source/library/CabalGild/Unstable/Type/VersionRange.hs
+++ b/source/library/CabalGild/Unstable/Type/VersionRange.hs
@@ -16,6 +16,9 @@
   | Intersect (VersionRange a) (VersionRange a)
   deriving (Eq, Ord, Show)
 
+-- | Converts a 'PkgconfigVersionRange.PkgconfigVersionRange' into a
+-- 'VersionRange'. Note that the former is more expressive, so things like
+-- 'PkgconfigVersionRange.PcOrLaterVersion' will be converted into a 'Union'.
 fromPkgconfigVersionRange ::
   PkgconfigVersionRange.PkgconfigVersionRange ->
   VersionRange PkgconfigVersion.PkgconfigVersion
@@ -29,6 +32,8 @@
   PkgconfigVersionRange.PcUnionVersionRanges v w -> Union (fromPkgconfigVersionRange v) (fromPkgconfigVersionRange w)
   PkgconfigVersionRange.PcIntersectVersionRanges v w -> Intersect (fromPkgconfigVersionRange v) (fromPkgconfigVersionRange w)
 
+-- | Converts a 'VersionRange.VersionRange' into a 'VersionRange'. These are
+-- isomorphic, so no information is lost.
 fromVersionRange :: VersionRange.VersionRange -> VersionRange Version.Version
 fromVersionRange =
   VersionRange.foldVersionRange
diff --git a/source/test-suite/Main.hs b/source/test-suite/Main.hs
--- a/source/test-suite/Main.hs
+++ b/source/test-suite/Main.hs
@@ -1,4 +1,3 @@
-{- hlint ignore "Redundant bracket" -}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 
 import qualified CabalGild.Unstable.Class.MonadLog as MonadLog
@@ -6,6 +5,7 @@
 import qualified CabalGild.Unstable.Class.MonadWalk as MonadWalk
 import qualified CabalGild.Unstable.Class.MonadWrite as MonadWrite
 import qualified CabalGild.Unstable.Exception.CheckFailure as CheckFailure
+import qualified CabalGild.Unstable.Exception.DuplicateOption as DuplicateOption
 import qualified CabalGild.Unstable.Exception.InvalidOption as InvalidOption
 import qualified CabalGild.Unstable.Exception.SpecifiedOutputWithCheckMode as SpecifiedOutputWithCheckMode
 import qualified CabalGild.Unstable.Exception.SpecifiedStdinWithFileInput as SpecifiedStdinWithFileInput
@@ -47,23 +47,55 @@
     s `Hspec.shouldBe` Map.empty
 
   Hspec.it "fails with an unknown option" $ do
-    let (a, s, w) = runGild ["--unknown"] [] (".", [])
-    a `shouldBeFailure` UnknownOption.UnknownOption "--unknown"
-    w `Hspec.shouldBe` []
-    s `Hspec.shouldBe` Map.empty
+    expectException ["--unknown"] $
+      UnknownOption.fromString "--unknown"
 
   Hspec.it "fails with an invalid option" $ do
-    let (a, s, w) = runGild ["--help=invalid"] [] (".", [])
-    a `shouldBeFailure` InvalidOption.InvalidOption "option `--help' doesn't allow an argument"
-    w `Hspec.shouldBe` []
-    s `Hspec.shouldBe` Map.empty
+    expectException ["--help=invalid"] $
+      InvalidOption.fromString "option `--help' doesn't allow an argument"
 
   Hspec.it "fails with an unexpected argument" $ do
-    let (a, s, w) = runGild ["unexpected"] [] (".", [])
-    a `shouldBeFailure` UnexpectedArgument.UnexpectedArgument "unexpected"
+    expectException ["unexpected"] $
+      UnexpectedArgument.fromString "unexpected"
+
+  Hspec.it "fails when --crlf is given twice" $ do
+    expectException ["--crlf=strict", "--crlf=lenient"] $
+      DuplicateOption.DuplicateOption "crlf" "strict" "lenient"
+
+  Hspec.it "does not fail when a flag is given twice with the same value" $ do
+    let (a, s, w) =
+          runGild
+            ["--crlf=strict", "--crlf=strict"]
+            [(Input.Stdin, String.toUtf8 "")]
+            (".", [])
+    a `Hspec.shouldSatisfy` Either.isRight
     w `Hspec.shouldBe` []
-    s `Hspec.shouldBe` Map.empty
+    s `Hspec.shouldBe` Map.singleton Output.Stdout (String.toUtf8 "")
 
+  Hspec.it "fails when a flag is repeatedly overriden" $ do
+    expectException ["-ia", "-ib", "-i-"] $
+      DuplicateOption.DuplicateOption "input" "a" "b"
+
+  Hspec.it "fails when --input is given twice" $ do
+    expectException
+      ["--input=f", "--input=-"]
+      $ DuplicateOption.DuplicateOption "input" "f" "-"
+
+  Hspec.it "fails when --mode is given twice" $ do
+    expectException
+      ["--mode=check", "--mode=format"]
+      $ DuplicateOption.DuplicateOption "mode" "check" "format"
+
+  Hspec.it "fails when --output is given twice" $ do
+    expectException
+      ["--output=f", "--output=-"]
+      $ DuplicateOption.DuplicateOption "output" "f" "-"
+
+  Hspec.it "fails when --stdin is given twice" $ do
+    expectException
+      ["--stdin=f", "--stdin=g"]
+      $ DuplicateOption.DuplicateOption "stdin" "f" "g"
+
   Hspec.it "reads from an input file" $ do
     let (a, s, w) =
           runGild
@@ -1625,6 +1657,15 @@
       fmap FilePath.joinPath <$> uncurry Map.singleton files
     )
     Map.empty
+
+expectException ::
+  (Stack.HasCallStack, Eq e, Exception.Exception e) =>
+  [String] -> e -> Hspec.Expectation
+expectException flags exception = do
+  let (a, s, w) = runGild flags [] (".", [])
+  a `shouldBeFailure` exception
+  w `Hspec.shouldBe` []
+  s `Hspec.shouldBe` Map.empty
 
 type Test = TestT Identity.Identity
 
