aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libutil/position.cc55
-rw-r--r--src/libutil/position.hh42
-rw-r--r--tests/functional/lang/eval-fail-eol-1.err.exp6
-rw-r--r--tests/functional/lang/eval-fail-eol-1.nix3
-rw-r--r--tests/functional/lang/eval-fail-eol-2.err.exp6
-rw-r--r--tests/functional/lang/eval-fail-eol-2.nix2
-rw-r--r--tests/functional/lang/eval-fail-eol-3.err.exp6
-rw-r--r--tests/functional/lang/eval-fail-eol-3.nix3
8 files changed, 99 insertions, 24 deletions
diff --git a/src/libutil/position.cc b/src/libutil/position.cc
index b39a5a1d4..724e560b7 100644
--- a/src/libutil/position.cc
+++ b/src/libutil/position.cc
@@ -29,32 +29,17 @@ std::optional<LinesOfCode> Pos::getCodeLines() const
return std::nullopt;
if (auto source = getSource()) {
-
- std::istringstream iss(*source);
- // count the newlines.
- int count = 0;
- std::string curLine;
- int pl = line - 1;
-
+ LinesIterator lines(*source), end;
LinesOfCode loc;
- do {
- std::getline(iss, curLine);
- ++count;
- if (count < pl)
- ;
- else if (count == pl) {
- loc.prevLineOfCode = curLine;
- } else if (count == pl + 1) {
- loc.errLineOfCode = curLine;
- } else if (count == pl + 2) {
- loc.nextLineOfCode = curLine;
- break;
- }
-
- if (!iss.good())
- break;
- } while (true);
+ if (line > 1)
+ std::advance(lines, line - 2);
+ if (lines != end && line > 1)
+ loc.prevLineOfCode = *lines++;
+ if (lines != end)
+ loc.errLineOfCode = *lines++;
+ if (lines != end)
+ loc.nextLineOfCode = *lines++;
return loc;
}
@@ -109,4 +94,26 @@ std::ostream & operator<<(std::ostream & str, const Pos & pos)
return str;
}
+void Pos::LinesIterator::bump(bool atFirst)
+{
+ if (!atFirst) {
+ pastEnd = input.empty();
+ if (!input.empty() && input[0] == '\r')
+ input.remove_prefix(1);
+ if (!input.empty() && input[0] == '\n')
+ input.remove_prefix(1);
+ }
+
+ // nix line endings are not only \n as eg std::getline assumes, but also
+ // \r\n **and \r alone**. not treating them all the same causes error
+ // reports to not match with line numbers as the parser expects them.
+ auto eol = input.find_first_of("\r\n");
+
+ if (eol > input.size())
+ eol = input.size();
+
+ curLine = input.substr(0, eol);
+ input.remove_prefix(eol);
+}
+
}
diff --git a/src/libutil/position.hh b/src/libutil/position.hh
index a184997ed..9bdf3b4b5 100644
--- a/src/libutil/position.hh
+++ b/src/libutil/position.hh
@@ -67,6 +67,48 @@ struct Pos
bool operator==(const Pos & rhs) const = default;
bool operator!=(const Pos & rhs) const = default;
bool operator<(const Pos & rhs) const;
+
+ struct LinesIterator {
+ using difference_type = size_t;
+ using value_type = std::string_view;
+ using reference = const std::string_view &;
+ using pointer = const std::string_view *;
+ using iterator_category = std::input_iterator_tag;
+
+ LinesIterator(): pastEnd(true) {}
+ explicit LinesIterator(std::string_view input): input(input), pastEnd(input.empty()) {
+ if (!pastEnd)
+ bump(true);
+ }
+
+ LinesIterator & operator++() {
+ bump(false);
+ return *this;
+ }
+ LinesIterator operator++(int) {
+ auto result = *this;
+ ++*this;
+ return result;
+ }
+
+ reference operator*() const { return curLine; }
+ pointer operator->() const { return &curLine; }
+
+ bool operator!=(const LinesIterator & other) const {
+ return !(*this == other);
+ }
+ bool operator==(const LinesIterator & other) const {
+ return (pastEnd && other.pastEnd)
+ || (std::forward_as_tuple(input.size(), input.data())
+ == std::forward_as_tuple(other.input.size(), other.input.data()));
+ }
+
+ private:
+ std::string_view input, curLine;
+ bool pastEnd = false;
+
+ void bump(bool atFirst);
+ };
};
std::ostream & operator<<(std::ostream & str, const Pos & pos);
diff --git a/tests/functional/lang/eval-fail-eol-1.err.exp b/tests/functional/lang/eval-fail-eol-1.err.exp
new file mode 100644
index 000000000..3f5a5c22c
--- /dev/null
+++ b/tests/functional/lang/eval-fail-eol-1.err.exp
@@ -0,0 +1,6 @@
+error: undefined variable 'invalid'
+ at /pwd/lang/eval-fail-eol-1.nix:2:1:
+ 1| # foo
+ 2| invalid
+ | ^
+ 3| # bar
diff --git a/tests/functional/lang/eval-fail-eol-1.nix b/tests/functional/lang/eval-fail-eol-1.nix
new file mode 100644
index 000000000..476223919
--- /dev/null
+++ b/tests/functional/lang/eval-fail-eol-1.nix
@@ -0,0 +1,3 @@
+# foo
+invalid
+# bar
diff --git a/tests/functional/lang/eval-fail-eol-2.err.exp b/tests/functional/lang/eval-fail-eol-2.err.exp
new file mode 100644
index 000000000..ff13e2d55
--- /dev/null
+++ b/tests/functional/lang/eval-fail-eol-2.err.exp
@@ -0,0 +1,6 @@
+error: undefined variable 'invalid'
+ at /pwd/lang/eval-fail-eol-2.nix:2:1:
+ 1| # foo
+ 2| invalid
+ | ^
+ 3| # bar
diff --git a/tests/functional/lang/eval-fail-eol-2.nix b/tests/functional/lang/eval-fail-eol-2.nix
new file mode 100644
index 000000000..0cf92a425
--- /dev/null
+++ b/tests/functional/lang/eval-fail-eol-2.nix
@@ -0,0 +1,2 @@
+# foo invalid
+# bar
diff --git a/tests/functional/lang/eval-fail-eol-3.err.exp b/tests/functional/lang/eval-fail-eol-3.err.exp
new file mode 100644
index 000000000..ada3c5ecd
--- /dev/null
+++ b/tests/functional/lang/eval-fail-eol-3.err.exp
@@ -0,0 +1,6 @@
+error: undefined variable 'invalid'
+ at /pwd/lang/eval-fail-eol-3.nix:2:1:
+ 1| # foo
+ 2| invalid
+ | ^
+ 3| # bar
diff --git a/tests/functional/lang/eval-fail-eol-3.nix b/tests/functional/lang/eval-fail-eol-3.nix
new file mode 100644
index 000000000..33422452d
--- /dev/null
+++ b/tests/functional/lang/eval-fail-eol-3.nix
@@ -0,0 +1,3 @@
+# foo
+invalid
+# bar