packages feed

nix-lang-0.1.0.0: test/Parser.hs

module Parser where

import Control.Monad (filterM)
import qualified Data.List as List
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Nix.Lang.Annotation
import Nix.Lang.Parser
import Nix.Lang.Span
import Nix.Lang.Types
import Nix.Lang.Types.Ps
import Nix.Lang.Utils
import System.Directory (doesDirectoryExist, doesFileExist, listDirectory)
import System.Exit (ExitCode (ExitSuccess))
import System.FilePath (makeRelative, takeExtension, (</>))
import System.Process (readProcessWithExitCode)
import Test.Tasty
import Test.Tasty.HUnit
import Text.Megaparsec (eof, errorBundlePretty)
import Utils

tests :: TestTree
tests =
  testGroup
    "parser"
    [ testCase "sample fixture parses" sampleFixtureParses,
      nixpkgsParseTest,
      referenceParserFixtureTests,
      testGroup
        "literals and atoms"
        [ testCase "integer literal" integerLiteralParses,
          testCase "float literal" floatLiteralParses,
          testCase "boolean literal" booleanLiteralParses,
          testCase "null literal" nullLiteralParses,
          testCase "uri literal" uriLiteralParses,
          testCase "variable" variableParses,
          testCase "parenthesized expression" parensParse,
          testCase "environment path" envPathParses,
          testCase "literal path" literalPathParses,
          testCase "interpolated path" interpolatedPathParses,
          testCase "underscore-starting path" underscorePathParses,
          testCase "alpha-starting path" alphaPathParses,
          testCase "rec-starting path" recPathParses
        ],
      testGroup
        "strings"
        [ testCase "double quoted string with interpolation" doubleQuotedStringParses,
          testCase "indented string with interpolation" indentedStringParses
        ],
      testGroup
        "structures and bindings"
        [ testCase "empty set" emptySetParses,
          testCase "recursive set" recursiveSetParses,
          testCase "inherit binding" inheritBindingParses,
          testCase "inherit binding carries typed annotation" inheritBindingCarriesTypedAnn,
          testCase "normal binding carries typed annotation" normalBindingCarriesTypedAnn,
          testCase "dynamic attribute binding" dynamicAttrBindingParses,
          testCase "list expression" listParses
        ],
      testGroup
        "functions and control"
        [ testCase "assert expression parses to NixAssert" assertParsesToAssert,
          testCase "assert carries typed annotation" assertCarriesTypedAnn,
          testCase "assert carries common absolute position" assertCarriesCommonAbsPosition,
          testCase "with expression" withParses,
          testCase "if expression" ifParses,
          testCase "let expression" letParses,
          testCase "lambda variable pattern" lambdaVarPatParses,
          testCase "lambda variable pattern carries typed annotation" lambdaVarPatCarriesTypedAnn,
          testCase "lambda set pattern" lambdaSetPatParses,
          testCase "lambda set pattern carries typed annotation" lambdaSetPatCarriesTypedAnn,
          testCase "lambda set pattern leading as owns at token" lambdaLeadingAsCarriesTypedAnn,
          testCase "lambda trailing as-pattern" lambdaTrailingAsParses
        ],
      testGroup
        "selection and operators"
        [ testCase "selection with default" selectOrParses,
          testCase "has-attr operator" hasAttrParses,
          testCase "has-attr carries typed annotation" hasAttrCarriesTypedAnn,
          testCase "application precedence" applicationParses,
          testCase "binary operator carries common absolute position" binaryOperatorCarriesCommonAbsPosition,
          testCase "binary operator precedence" operatorPrecedenceParses,
          testCase "unary operators" unaryOperatorsParse
        ],
      testGroup
        "unsupported syntax"
        [ testCase "legacy let is rejected" legacyLetRejected,
          testCase "leading-decimal float is rejected" leadingDecimalRejected,
          testCase "multiple has-attr is rejected" multiHasAttrRejected,
          testCase "operator without whitespace is rejected" operatorWithoutWhitespaceRejected
        ]
    ]

sampleFixtureParses :: Assertion
sampleFixtureParses = do
  src <- T.readFile "test/sample.nix"
  case runNixParser nixFile "test/sample.nix" src of
    (Right _, _) -> pure ()
    (Left err, _) -> assertFailure $ errorBundlePretty err

nixpkgsParseTest :: TestTree
nixpkgsParseTest =
  testCase "nixpkgs tree parses (if found)" $ do
    mRoot <- findNixpkgsRoot
    case mRoot of
      Nothing -> putStrLn "nixpkgs not found"
      Just root -> do
        exists <- doesDirectoryExist root
        if exists
          then assertNixpkgsCorpusParses root
          else assertFailure $ "<nixpkgs> path does not exist: " <> root

findNixpkgsRoot :: IO (Maybe FilePath)
findNixpkgsRoot = do
  (exitCode, stdout, _) <- readProcessWithExitCode "nix-instantiate" ["--find-file", "nixpkgs"] ""
  pure $ case exitCode of
    ExitSuccess ->
      case lines stdout of
        path : _ | not (null path) -> Just path
        _ -> Nothing
    _ -> Nothing

assertNixpkgsCorpusParses :: FilePath -> Assertion
assertNixpkgsCorpusParses root = do
  nixFiles <- listNixFiles root
  failures <- go [] nixFiles
  case failures of
    [] -> pure ()
    _ ->
      assertFailure . unlines $
        ["failed to parse " <> show (length failures) <> " nixpkgs files out of " <> show (length nixFiles)]
          <> fmap renderFailure (take 20 failures)
  where
    go !failures [] = pure (reverse failures)
    go !failures (fp : rest) = do
      src <- T.readFile fp
      case runNixParser nixFile fp src of
        (Right _, _) -> go failures rest
        (Left err, _) ->
          go ((makeRelative root fp, errorBundlePretty err) : failures) rest

    renderFailure (fp, err) = "FAIL " <> fp <> "\n" <> err

listNixFiles :: FilePath -> IO [FilePath]
listNixFiles root = do
  paths <- walk root
  pure $ List.sort [path | path <- paths, takeExtension path == ".nix"]
  where
    walk dir = do
      entries <- listDirectory dir
      let paths = fmap (dir </>) entries
      files <- filterM doesFileExist paths
      dirs <- filterM doesDirectoryExist paths
      nested <- go [] dirs
      pure (files <> nested)

    go acc [] = pure acc
    go acc (dir : rest) = do
      nested <- walk dir
      let acc' = nested <> acc
      acc' `seq` go acc' rest

integerLiteralParses :: Assertion
integerLiteralParses = do
  expr <- parseExprOrFail "233"
  case expr of
    NixLit _ (L _ (NixInteger _ 233)) -> pure ()
    _ -> assertFailure $ "expected integer literal, got: " <> show expr

floatLiteralParses :: Assertion
floatLiteralParses = do
  expr <- parseExprOrFail "2.33"
  case expr of
    NixLit _ (L _ (NixFloat _ f)) | abs (f - 2.33) < 0.0001 -> pure ()
    _ -> assertFailure $ "expected float literal, got: " <> show expr

booleanLiteralParses :: Assertion
booleanLiteralParses = do
  t <- parseExprOrFail "true"
  f <- parseExprOrFail "false"
  case (t, f) of
    (NixLit _ (L _ (NixBoolean _ True)), NixLit _ (L _ (NixBoolean _ False))) -> pure ()
    _ -> assertFailure "expected true/false boolean literals"

nullLiteralParses :: Assertion
nullLiteralParses = do
  expr <- parseExprOrFail "null"
  case expr of
    NixLit _ (L _ (NixNull _)) -> pure ()
    _ -> assertFailure $ "expected null literal, got: " <> show expr

uriLiteralParses :: Assertion
uriLiteralParses = do
  expr <- parseExprOrFail "https://www.example.com"
  case expr of
    NixLit _ (L _ (NixUri _ uri)) | uri == "https://www.example.com" -> pure ()
    _ -> assertFailure $ "expected uri literal, got: " <> show expr

variableParses :: Assertion
variableParses = do
  expr <- parseExprOrFail "hello_world"
  case expr of
    NixVar _ (L _ name) | name == "hello_world" -> pure ()
    _ -> assertFailure $ "expected variable, got: " <> show expr

parensParse :: Assertion
parensParse = do
  expr <- parseExprOrFail "(1)"
  case expr of
    NixPar _ (L _ (NixLit _ (L _ (NixInteger _ 1)))) -> pure ()
    _ -> assertFailure $ "expected parenthesized integer, got: " <> show expr

envPathParses :: Assertion
envPathParses = do
  expr <- parseExprOrFail "<nixpkgs/nixos>"
  case expr of
    NixEnvPath _ (L _ path) | path == "nixpkgs/nixos" -> pure ()
    _ -> assertFailure $ "expected environment path, got: " <> show expr

literalPathParses :: Assertion
literalPathParses = do
  expr <- parseExprOrFail "./nix"
  case expr of
    NixPath _ (L _ (NixLiteralPath _ path)) | path == "./nix" -> pure ()
    _ -> assertFailure $ "expected literal path, got: " <> show expr

interpolatedPathParses :: Assertion
interpolatedPathParses = do
  expr <- parseExprOrFail "./${a}-${b}/c/d${e}"
  case expr of
    NixPath _ (L _ (NixInterpolPath NoExtF parts)) -> do
      length parts @?= 6
    _ -> assertFailure $ "expected interpolated path, got: " <> show expr

underscorePathParses :: Assertion
underscorePathParses = do
  expr <- parseExprOrFail "_/foo"
  case expr of
    NixPath _ (L _ (NixLiteralPath _ path)) | path == "_/foo" -> pure ()
    _ -> assertFailure $ "expected underscore-starting path, got: " <> show expr

alphaPathParses :: Assertion
alphaPathParses = do
  expr <- parseExprOrFail "abc/foo"
  case expr of
    NixPath _ (L _ (NixLiteralPath _ path)) | path == "abc/foo" -> pure ()
    _ -> assertFailure $ "expected alpha-starting path, got: " <> show expr

recPathParses :: Assertion
recPathParses = do
  expr <- parseExprOrFail "rec/foo"
  case expr of
    NixPath _ (L _ (NixLiteralPath _ path)) | path == "rec/foo" -> pure ()
    _ -> assertFailure $ "expected rec-starting path, got: " <> show expr

doubleQuotedStringParses :: Assertion
doubleQuotedStringParses = do
  expr <- parseExprOrFail "\"a${b}c\""
  case expr of
    NixString _ (L _ (NixDoubleQuotesString (SourceText src) parts)) -> do
      src @?= "a${b}c"
      length parts @?= 3
    _ -> assertFailure $ "expected double quoted string, got: " <> show expr

indentedStringParses :: Assertion
indentedStringParses = do
  expr <- parseExprOrFail "''a${b}c''"
  case expr of
    NixString _ (L _ (NixDoubleSingleQuotesString (SourceText src) parts)) -> do
      src @?= "a${b}c"
      length parts @?= 3
    _ -> assertFailure $ "expected indented string, got: " <> show expr

emptySetParses :: Assertion
emptySetParses = do
  expr <- parseExprOrFail "{ }"
  case expr of
    NixSet _ NixSetNonRecursive (L _ []) -> pure ()
    _ -> assertFailure $ "expected empty set, got: " <> show expr

recursiveSetParses :: Assertion
recursiveSetParses = do
  expr <- parseExprOrFail "rec { k = 233; }"
  case expr of
    NixSet _ NixSetRecursive (L _ [L _ (NixNormalBinding _ _ (L _ (NixLit _ (L _ (NixInteger _ 233)))))]) -> pure ()
    _ -> assertFailure $ "expected recursive set, got: " <> show expr

inheritBindingParses :: Assertion
inheritBindingParses = do
  expr <- parseExprOrFail "{ inherit (s) m g; }"
  case expr of
    NixSet _ _ (L _ [L _ (NixInheritBinding _ (Just (L _ (NixPar _ _))) keys)]) -> length keys @?= 2
    _ -> assertFailure $ "expected inherit binding set, got: " <> show expr

inheritBindingCarriesTypedAnn :: Assertion
inheritBindingCarriesTypedAnn = do
  expr <- parseExprOrFail "{ inherit (s) m g; }"
  case expr of
    NixSet _ _ (L _ [L _ (NixInheritBinding ann _ _)]) -> do
      fmap srcSpanStartColumn (annTokenSrcSpan (aibInherit ann)) @?= Just 3
      fmap srcSpanStartColumn (annTokenSrcSpan (aibSemicolon ann)) @?= Just 18
    _ -> assertFailure $ "expected inherit binding set, got: " <> show expr

dynamicAttrBindingParses :: Assertion
dynamicAttrBindingParses = do
  expr <- parseExprOrFail "{ \"${dynamic}\".${attr} = g; }"
  case expr of
    NixSet _ _ (L _ [L _ (NixNormalBinding _ (L _ (NixAttrPath _ [L _ (NixDynamicStringAttrKey NoExtF parts), L _ (NixDynamicInterpolAttrKey NoExtF (L _ (NixVar _ (L _ attr))))])) (L _ (NixVar _ (L _ "g"))))]) -> do
      length parts @?= 1
      attr @?= "attr"
    _ -> assertFailure $ "expected dynamic attr binding, got: " <> show expr

normalBindingCarriesTypedAnn :: Assertion
normalBindingCarriesTypedAnn = do
  expr <- parseExprOrFail "{ x = 1; }"
  case expr of
    NixSet _ _ (L _ [L _ (NixNormalBinding ann _ _)]) -> do
      fmap srcSpanStartColumn (annTokenSrcSpan (anbEqual ann)) @?= Just 5
      fmap srcSpanStartColumn (annTokenSrcSpan (anbSemicolon ann)) @?= Just 8
    _ -> assertFailure $ "expected normal binding set, got: " <> show expr

listParses :: Assertion
listParses = do
  expr <- parseExprOrFail "[1 2 3]"
  case expr of
    NixList _ xs -> length xs @?= 3
    _ -> assertFailure $ "expected list, got: " <> show expr

assertParsesToAssert :: Assertion
assertParsesToAssert =
  case runNixParser (nixExpr <* eof) "<assert>" (T.pack "assert a == 1; 2") of
    (Right (NixAssert _ _ _), _) -> pure ()
    (Right expr, _) -> assertFailure $ "expected NixAssert, got: " <> show expr
    (Left err, _) -> assertFailure $ errorBundlePretty err

assertCarriesTypedAnn :: Assertion
assertCarriesTypedAnn =
  case runNixParser (nixExpr <* eof) "<assert>" (T.pack "assert a == 1; 2") of
    (Right (NixAssert ann _ _), _) -> do
      fmap srcSpanStartColumn (annTokenSrcSpan (aaAssert ann)) @?= Just 1
      fmap srcSpanStartColumn (annTokenSrcSpan (aaSemicolon ann)) @?= Just 14
      acComments (aaCommon ann) @?= emptyComments
    (Right expr, _) -> assertFailure $ "expected NixAssert, got: " <> show expr
    (Left err, _) -> assertFailure $ errorBundlePretty err

assertCarriesCommonAbsPosition :: Assertion
assertCarriesCommonAbsPosition =
  case runNixParser (nixExpr <* eof) "<assert>" (T.pack "assert a == 1; 2") of
    (Right (NixAssert ann _ _), _) -> do
      annPos ann @?= AnnSpan (mkSrcSpan "<assert>" (1, 1) (1, 17))
      annSrcSpan ann @?= Just (mkSrcSpan "<assert>" (1, 1) (1, 17))
    (Right expr, _) -> assertFailure $ "expected NixAssert, got: " <> show expr
    (Left err, _) -> assertFailure $ errorBundlePretty err

withParses :: Assertion
withParses = do
  expr <- parseExprOrFail "with l; 1"
  case expr of
    NixWith _ (L _ (NixVar _ (L _ "l"))) (L _ (NixLit _ (L _ (NixInteger _ 1)))) -> pure ()
    _ -> assertFailure $ "expected with expression, got: " <> show expr

ifParses :: Assertion
ifParses = do
  expr <- parseExprOrFail "if a then b else c"
  case expr of
    NixIf _ (L _ (NixVar _ (L _ "a"))) (L _ (NixVar _ (L _ "b"))) (L _ (NixVar _ (L _ "c"))) -> pure ()
    _ -> assertFailure $ "expected if expression, got: " <> show expr

letParses :: Assertion
letParses = do
  expr <- parseExprOrFail "let x = 1; in x"
  case expr of
    NixLet _ (L _ [L _ (NixNormalBinding _ _ _)]) (L _ (NixVar _ (L _ "x"))) -> pure ()
    _ -> assertFailure $ "expected let expression, got: " <> show expr

lambdaVarPatParses :: Assertion
lambdaVarPatParses = do
  expr <- parseExprOrFail "x: x"
  case expr of
    NixLam _ (L _ (NixVarPat _ (L _ "x"))) (L _ (NixVar _ (L _ "x"))) -> pure ()
    _ -> assertFailure $ "expected lambda with var pattern, got: " <> show expr

lambdaVarPatCarriesTypedAnn :: Assertion
lambdaVarPatCarriesTypedAnn = do
  expr <- parseExprOrFail "x: x"
  case expr of
    NixLam ann (L _ (NixVarPat patAnn _)) _ -> do
      fmap srcSpanStartColumn (annTokenSrcSpan (alamColon ann)) @?= Just 2
      srcSpanStartColumn (avpId patAnn) @?= 1
    _ -> assertFailure $ "expected lambda with var pattern, got: " <> show expr

lambdaSetPatParses :: Assertion
lambdaSetPatParses = do
  expr <- parseExprOrFail "{x ? 1, y ? {}, ...}: x"
  case expr of
    NixLam _ (L _ (NixSetPat _ NixSetPatIsEllipses Nothing bindings)) _ -> length bindings @?= 2
    _ -> assertFailure $ "expected lambda with set pattern, got: " <> show expr

lambdaSetPatCarriesTypedAnn :: Assertion
lambdaSetPatCarriesTypedAnn = do
  expr <- parseExprOrFail "{x ? 1, y ? {}, ...}: x"
  case expr of
    NixLam lamAnn (L _ (NixSetPat _ _ _ bindings)) _ -> do
      fmap srcSpanStartColumn (annTokenSrcSpan (alamColon lamAnn)) @?= Just 21
      case bindings of
        L _ NixSetPatBinding {nspbAnn = AnnSetPatBinding {aspbQuestion = Just question}} : _ ->
          fmap srcSpanStartColumn (annTokenSrcSpan question) @?= Just 4
        _ -> assertFailure "expected first set-pattern binding to own question token"
    _ -> assertFailure $ "expected lambda with set pattern, got: " <> show expr

lambdaTrailingAsParses :: Assertion
lambdaTrailingAsParses = do
  expr <- parseExprOrFail "{x}@a: x"
  case expr of
    NixLam _ (L _ (NixSetPat _ _ (Just (L _ NixSetPatAs {nspaLocation = NixSetPatAsTrailing, nspaVar = L _ "a"})) _)) _ -> pure ()
    _ -> assertFailure $ "expected lambda with trailing as-pattern, got: " <> show expr

lambdaLeadingAsCarriesTypedAnn :: Assertion
lambdaLeadingAsCarriesTypedAnn = do
  expr <- parseExprOrFail "a@{x}: x"
  case expr of
    NixLam _ (L _ (NixSetPat _ _ (Just (L _ NixSetPatAs {nspaAnn = AnnSetPatAs {aspaAt = atTok}, nspaLocation = NixSetPatAsLeading, nspaVar = L _ "a"})) _)) _ ->
      fmap srcSpanStartColumn (annTokenSrcSpan atTok) @?= Just 2
    _ -> assertFailure $ "expected lambda with leading as-pattern token ownership, got: " <> show expr

selectOrParses :: Assertion
selectOrParses = do
  expr <- parseExprOrFail "a.b.c or d.e"
  case expr of
    NixSelect _ (L _ (NixVar _ (L _ "a"))) (L _ (NixAttrPath _ keys)) (Just (L _ (NixSelect _ _ _ Nothing))) ->
      length keys @?= 2
    _ -> assertFailure $ "expected selection with default, got: " <> show expr

hasAttrParses :: Assertion
hasAttrParses = do
  expr <- parseExprOrFail "a ? b"
  case expr of
    NixHasAttr _ (L _ (NixVar _ (L _ "a"))) (L _ (NixAttrPath _ keys)) -> length keys @?= 1
    _ -> assertFailure $ "expected has-attr expression, got: " <> show expr

hasAttrCarriesTypedAnn :: Assertion
hasAttrCarriesTypedAnn = do
  expr <- parseExprOrFail "a ? b"
  case expr of
    NixHasAttr ann _ _ -> fmap srcSpanStartColumn (annTokenSrcSpan (ahaQuestion ann)) @?= Just 3
    _ -> assertFailure $ "expected has-attr expression, got: " <> show expr

applicationParses :: Assertion
applicationParses = do
  expr <- parseExprOrFail "a b c"
  case expr of
    NixApp _ (L _ (NixApp _ (L _ (NixVar _ (L _ "a"))) (L _ (NixVar _ (L _ "b"))))) (L _ (NixVar _ (L _ "c"))) -> pure ()
    _ -> assertFailure $ "expected left-associative application, got: " <> show expr

binaryOperatorCarriesCommonAbsPosition :: Assertion
binaryOperatorCarriesCommonAbsPosition = do
  expr <- parseExprOrFail "a + b"
  case expr of
    NixBinApp ann OpAdd _ _ -> do
      annPos ann @?= AnnSpan (mkSrcSpan "<expr>" (1, 1) (1, 6))
      annSrcSpan ann @?= Just (mkSrcSpan "<expr>" (1, 1) (1, 6))
    _ -> assertFailure $ "expected binary application, got: " <> show expr

operatorPrecedenceParses :: Assertion
operatorPrecedenceParses = do
  expr <- parseExprOrFail "a >= b || c < d && e == f"
  case expr of
    NixBinApp _ OpOr (L _ (NixBinApp _ OpGE _ _)) (L _ (NixBinApp _ OpAnd (L _ (NixBinApp _ OpLT _ _)) (L _ (NixBinApp _ OpEq _ _)))) -> pure ()
    _ -> assertFailure $ "expected precedence tree, got: " <> show expr

unaryOperatorsParse :: Assertion
unaryOperatorsParse = do
  expr <- parseExprOrFail "!true || -5 == -5"
  case expr of
    NixBinApp _ OpOr (L _ (NixNotApp _ _)) (L _ (NixBinApp _ OpEq (L _ (NixNegApp _ _)) (L _ (NixNegApp _ _)))) -> pure ()
    _ -> assertFailure $ "expected unary operators, got: " <> show expr

legacyLetRejected :: Assertion
legacyLetRejected = parseExprFails "let { x = 233; body = x; }"

leadingDecimalRejected :: Assertion
leadingDecimalRejected = parseExprFails ".233"

multiHasAttrRejected :: Assertion
multiHasAttrRejected = parseExprFails "a ? b ? c ? d"

operatorWithoutWhitespaceRejected :: Assertion
operatorWithoutWhitespaceRejected = parseExprFails "1+-1"

referenceParserFixtureTests :: TestTree
referenceParserFixtureTests =
  testGroup
    "reference parser fixtures"
    [ testGroup "valid parse" (fixtureParseCases <$> validFixtures),
      testGroup "invalid parse" (fixtureRejectCases <$> invalidFixtures),
      testGroup "known divergence" (fixtureAcceptedCases <$> acceptedInvalidFixtures)
    ]

fixtureParseCases :: FilePath -> TestTree
fixtureParseCases relPath =
  testCase relPath $ do
    src <- T.readFile fullPath
    case runNixParser nixFile fullPath src of
      (Right _, _) -> pure ()
      (Left err, _) -> assertFailure $ errorBundlePretty err
  where
    fullPath = fixtureRoot <> "/" <> relPath

fixtureRejectCases :: FilePath -> TestTree
fixtureRejectCases relPath =
  testCase relPath $ do
    src <- T.readFile fullPath
    parseFileFails fullPath src
  where
    fullPath = fixtureRoot <> "/" <> relPath

fixtureAcceptedCases :: FilePath -> TestTree
fixtureAcceptedCases relPath =
  testCase relPath $ do
    src <- T.readFile fullPath
    case runNixParser nixFile fullPath src of
      (Right _, _) -> pure ()
      (Left err, _) -> assertFailure $ errorBundlePretty err
  where
    fullPath = fixtureRoot <> "/" <> relPath

fixtureRoot :: FilePath
fixtureRoot = "test/fixtures/nixfmt"

validFixtures :: [FilePath]
validFixtures =
  [ "correct/standalone-comments.nix",
    "correct/blank-line-in-interpolation.nix",
    "correct/indented-string.nix",
    "correct/string-with-single-quote-at-end.nix",
    "correct/paths-with-interpolations.nix",
    "correct/quotes-in-inherit.nix",
    "correct/if-with-comments.nix",
    "correct/commented-list.nix",
    "correct/commented-params-list.nix",
    "correct/final-comments-in-sets.nix",
    "diff/comment/in.nix",
    "diff/string/in.nix",
    "diff/string_interpol/in.nix",
    "diff/leading_blank/in.nix",
    "diff/inherit_comment/in.nix",
    "diff/strip_space/in.nix"
  ]

invalidFixtures :: [FilePath]
invalidFixtures =
  [ "invalid/naked-interpolation.nix",
    "invalid/path-starting-with-interpolation.nix",
    "invalid/path-with-interpolation-before-slash.nix",
    "invalid/path-with-escaped-interpolation.nix",
    "invalid/interpolation-in-env-path.nix",
    "invalid/smiley.nix"
  ]

acceptedInvalidFixtures :: [FilePath]
acceptedInvalidFixtures =
  [ "invalid/interpolation-in-inherit-1.nix",
    "invalid/interpolation-in-inherit-2.nix"
  ]