about summary refs log tree commit diff stats
path: root/011load.cc
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-10-21 01:08:09 -0700
committerKartik K. Agaram <vc@akkartik.com>2016-10-21 01:13:27 -0700
commit66abe7c1bd54ca227b9e035d52a1c2f1ea387b5e (patch)
tree9fc885faa5b4878247d4411bd0d72c1c59cc5f92 /011load.cc
parent22d93b76718a9e260c1969adf53fc0559cf24355 (diff)
downloadmu-66abe7c1bd54ca227b9e035d52a1c2f1ea387b5e.tar.gz
3539
Always check if next_word() returned an empty string (if it hit eof).

Thanks Rebecca Allard for running into a crash when a .mu file ends with
'{' (without a following newline).

Open question: how to express the constraint that next_word() should
always check if its result is empty? Can *any* type system do that?!
Even the usual constraint that we must use a result isn't iron-clad: you
could save the result in a variable but then ignore it. Unless you go to
Go's extraordinary lengths of considering any dead code an error.
Diffstat (limited to '011load.cc')
-rw-r--r--011load.cc21
1 files changed, 18 insertions, 3 deletions
diff --git a/011load.cc b/011load.cc
index 9dc54922..23126647 100644
--- a/011load.cc
+++ b/011load.cc
@@ -26,6 +26,10 @@ vector<recipe_ordinal> load(istream& in) {
     skip_whitespace_and_comments(in);
     if (!has_data(in)) break;
     string command = next_word(in);
+    if (command.empty()) {
+      assert(!has_data(in));
+      break;
+    }
     // Command Handlers
     if (command == "recipe" || command == "def") {
       recipe_ordinal r = slurp_recipe(in);
@@ -50,6 +54,11 @@ vector<recipe_ordinal> load(istream& in) {
 int slurp_recipe(istream& in) {
   recipe result;
   result.name = next_word(in);
+  if (result.name.empty()) {
+    assert(!has_data(in));
+    raise << "file ended with 'recipe'\n" << end();
+    return -1;
+  }
   // End Load Recipe Name
   skip_whitespace_but_not_newline(in);
   // End Recipe Refinements
@@ -89,7 +98,7 @@ bool next_instruction(istream& in, instruction* curr) {
   curr->clear();
   skip_whitespace_and_comments(in);
   if (!has_data(in)) {
-    raise << "0: unbalanced '[' for recipe\n" << end();
+    raise << "incomplete recipe at end of file (0)\n" << end();
     return false;
   }
 
@@ -97,10 +106,15 @@ bool next_instruction(istream& in, instruction* curr) {
   while (has_data(in) && in.peek() != '\n') {
     skip_whitespace_but_not_newline(in);
     if (!has_data(in)) {
-      raise << "1: unbalanced '[' for recipe\n" << end();
+      raise << "incomplete recipe at end of file (1)\n" << end();
       return false;
     }
     string word = next_word(in);
+    if (word.empty()) {
+      assert(!has_data(in));
+      raise << "incomplete recipe at end of file (2)\n" << end();
+      return false;
+    }
     words.push_back(word);
     skip_whitespace_but_not_newline(in);
   }
@@ -113,7 +127,7 @@ bool next_instruction(istream& in, instruction* curr) {
     curr->label = words.at(0);
     trace(9993, "parse") << "label: " << curr->label << end();
     if (!has_data(in)) {
-      raise << "7: unbalanced '[' for recipe\n" << end();
+      raise << "incomplete recipe at end of file (3)\n" << end();
       return false;
     }
     return true;
@@ -149,6 +163,7 @@ bool next_instruction(istream& in, instruction* curr) {
   return true;
 }
 
+// can return empty string -- only if `in` has no more data
 string next_word(istream& in) {
   skip_whitespace_but_not_newline(in);
   // End next_word Special-cases