gn: Don't do an out-of-bound access if a file ends after 'else'

BUG=640857

Review-Url: https://codereview.chromium.org/2282493002
Cr-Original-Commit-Position: refs/heads/master@{#414461}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 5aba5ce9d5f3184b1e659c319e962c57274cbe03
diff --git a/tools/gn/parser.cc b/tools/gn/parser.cc
index 57a398e..41ee6b6 100644
--- a/tools/gn/parser.cc
+++ b/tools/gn/parser.cc
@@ -388,7 +388,8 @@
   if (has_error()) {
     // Don't overwrite current error, but make progress through tokens so that
     // a loop that's expecting a particular token will still terminate.
-    cur_++;
+    if (!at_end())
+      cur_++;
     return Token(Location(), Token::INVALID, base::StringPiece());
   }
   if (at_end()) {
@@ -708,7 +709,7 @@
         return stmt;
     }
     if (!has_error()) {
-      Token token = at_end() ? tokens_[tokens_.size() - 1] : cur_token();
+      Token token = cur_or_last_token();
       *err_ = Err(token, "Expecting assignment or function call.");
     }
     return std::unique_ptr<ParseNode>();
@@ -755,7 +756,7 @@
     } else if (LookAhead(Token::IF)) {
       condition->set_if_false(ParseStatement());
     } else {
-      *err_ = Err(cur_token(), "Expected '{' or 'if' after 'else'.");
+      *err_ = Err(cur_or_last_token(), "Expected '{' or 'if' after 'else'.");
       return std::unique_ptr<ParseNode>();
     }
   }
diff --git a/tools/gn/parser.h b/tools/gn/parser.h
index 29580da..22acb47 100644
--- a/tools/gn/parser.h
+++ b/tools/gn/parser.h
@@ -111,8 +111,13 @@
                 const char* error_message);
   Token Consume();
 
+  // Call this only if !at_end().
   const Token& cur_token() const { return tokens_[cur_]; }
 
+  const Token& cur_or_last_token() const {
+    return at_end() ? tokens_[tokens_.size() - 1] : cur_token();
+  }
+
   bool done() const { return at_end() || has_error(); }
   bool at_end() const { return cur_ >= tokens_.size(); }
   bool has_error() const { return err_->has_error(); }