NixOS / nixfmt

The official (but not yet stable) formatter for Nix code
https://nixfmt.serokell.io
Mozilla Public License 2.0
803 stars 31 forks source link

Error when carriage return used as linebreak in comment #175

Open 9999years opened 5 months ago

9999years commented 5 months ago

Description

nixfmt errors when a carriage return is used as a linebreak in a comment.

Small example input

{
  x = #^M1;
}

(Where ^M is a literal carriage return character.)

We can see that nix-instantiate parses the file OK:

$ xxd crlf.nix
00000000: 7b0a 2020 7820 3d20 230d 313b 0a7d 0a    {.  x = #.1;.}.
$ nix-instantiate --parse crlf.nix
{ x = 1; }

Actual output

crlf.nix:2:8:
  |
1;|   x = #
  |        ^^^^
unexpected "<carriage return>1;<newline>}<newline>"
expecting expression
piegamesde commented 5 months ago

Whatever kind of dark magic Nix does here, I'd say that not implementing that and giving an error is an acceptable thing to do here

tomberek commented 5 months ago

Likely the carriage return stops the one-line comment: https://github.com/NixOS/nix/blob/master/src/libexpr/lexer.l#L284

eol from Megaparsec only understands \n. Something like this might work, but change things elsewhere? position values might get messed up? Have to make sure the "\r\n" sequence is replaced first.

diff --git a/src/Nixfmt/Lexer.hs b/src/Nixfmt/Lexer.hs
index b331359..996e847 100644
--- a/src/Nixfmt/Lexer.hs
+++ b/src/Nixfmt/Lexer.hs
@@ -17,7 +17,7 @@ import Data.Text as Text
 import Text.Megaparsec
   (SourcePos(..), anySingle, chunk, getSourcePos, hidden, many, manyTill, some,
   try, unPos, (<|>))
-import Text.Megaparsec.Char (eol)
+import Text.Megaparsec.Char (string, eol)

 import Nixfmt.Types (Ann(..), Parser, TrailingComment(..), Trivia, Trivium(..))
 import Nixfmt.Util (manyP)
@@ -32,7 +32,7 @@ preLexeme :: Parser a -> Parser a
 preLexeme p = p <* manyP (\x -> isSpace x && x /= '\n' && x /= '\r')

 newlines :: Parser ParseTrivium
-newlines = PTNewlines <$> Prelude.length <$> some (preLexeme eol)
+newlines = PTNewlines <$> Prelude.length <$> some (preLexeme (eol <|> string "\r"))

 splitLines :: Text -> [Text]
 splitLines = dropWhile Text.null . dropWhileEnd Text.null