From e22ce4506564f3630e4cd63dc0795115df90be71 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 21 Jun 2020 13:28:00 +0200
Subject: [PATCH] :children_crossing: improve diagnostics

---
 include/nlohmann/detail/input/lexer.hpp |  7 ++++++-
 single_include/nlohmann/json.hpp        |  7 ++++++-
 test/src/unit-class_lexer.cpp           | 25 +++++++++++++++++++++++++
 test/src/unit-class_parser.cpp          |  6 ++++++
 4 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp
index 9dba2972..580b1c22 100644
--- a/include/nlohmann/detail/input/lexer.hpp
+++ b/include/nlohmann/detail/input/lexer.hpp
@@ -865,7 +865,10 @@ class lexer : public lexer_base<BasicJsonType>
                     {
                         case std::char_traits<char_type>::eof():
                         case '\0':
+                        {
+                            error_message = "invalid comment; missing closing '*/'";
                             return false;
+                        }
 
                         case '*':
                         {
@@ -890,7 +893,10 @@ class lexer : public lexer_base<BasicJsonType>
 
             // unexpected character after reading '/'
             default:
+            {
+                error_message = "invalid comment; expecting '/' or '*' after '/'";
                 return false;
+            }
         }
     }
 
@@ -1504,7 +1510,6 @@ scan_number_done:
         {
             if (not scan_comment())
             {
-                error_message = "invalid comment";
                 return token_type::parse_error;
             }
 
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index f4fe8642..cdc3de09 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -8932,7 +8932,10 @@ class lexer : public lexer_base<BasicJsonType>
                     {
                         case std::char_traits<char_type>::eof():
                         case '\0':
+                        {
+                            error_message = "invalid comment; missing closing '*/'";
                             return false;
+                        }
 
                         case '*':
                         {
@@ -8957,7 +8960,10 @@ class lexer : public lexer_base<BasicJsonType>
 
             // unexpected character after reading '/'
             default:
+            {
+                error_message = "invalid comment; expecting '/' or '*' after '/'";
                 return false;
+            }
         }
     }
 
@@ -9571,7 +9577,6 @@ scan_number_done:
         {
             if (not scan_comment())
             {
-                error_message = "invalid comment";
                 return token_type::parse_error;
             }
 
diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp
index aee4703f..d8304ccf 100644
--- a/test/src/unit-class_lexer.cpp
+++ b/test/src/unit-class_lexer.cpp
@@ -45,6 +45,15 @@ json::lexer::token_type scan_string(const char* s, const bool ignore_comments)
 }
 }
 
+std::string get_error_message(const char* s, const bool ignore_comments = false);
+std::string get_error_message(const char* s, const bool ignore_comments)
+{
+    auto ia = nlohmann::detail::input_adapter(s);
+    auto lexer = nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments);
+    lexer.scan();
+    return lexer.get_error_message();
+}
+
 TEST_CASE("lexer class")
 {
     SECTION("scan")
@@ -185,32 +194,48 @@ TEST_CASE("lexer class")
     SECTION("fail on comments")
     {
         CHECK((scan_string("/", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/", false) == "invalid literal");
 
         CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/!", false) == "invalid literal");
         CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/*", false) == "invalid literal");
         CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/**", false) == "invalid literal");
 
         CHECK((scan_string("//", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("//", false) == "invalid literal");
         CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/**/", false) == "invalid literal");
         CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/** /", false) == "invalid literal");
 
         CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/***/", false) == "invalid literal");
         CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/* true */", false) == "invalid literal");
         CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/*/**/", false) == "invalid literal");
         CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/*/* */", false) == "invalid literal");
     }
 
     SECTION("ignore comments")
     {
         CHECK((scan_string("/", true) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'");
 
         CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'");
         CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'");
         CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'");
 
         CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input));
         CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input));
         CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error));
+        CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'");
 
         CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input));
         CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input));
diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp
index de5c9638..da16ffca 100644
--- a/test/src/unit-class_parser.cpp
+++ b/test/src/unit-class_parser.cpp
@@ -1877,4 +1877,10 @@ TEST_CASE("parser class")
             }
         }
     }
+
+    SECTION("error messages for comments")
+    {
+        CHECK_THROWS_WITH_AS(json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error);
+        CHECK_THROWS_WITH_AS(json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*<U+0000>'", json::parse_error);
+    }
 }