[PATCH] D83436: [clangd] Fix error handling in config.yaml parsing.

2020-07-09 Thread Sam McCall via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGf36518637d7d: [clangd] Fix error handling in config.yaml 
parsing. (authored by sammccall).

Changed prior to commit:
  https://reviews.llvm.org/D83436?vs=276574=276660#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83436/new/

https://reviews.llvm.org/D83436

Files:
  clang-tools-extra/clangd/ConfigYAML.cpp
  clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp

Index: clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
===
--- clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
@@ -98,10 +98,25 @@
 DiagKind(llvm::SourceMgr::DK_Error),
 DiagPos(YAML.point()), DiagRange(llvm::None;
 
-  ASSERT_EQ(Results.size(), 2u);
+  ASSERT_EQ(Results.size(), 1u); // invalid fragment discarded.
   EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(Val("first")));
   EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition);
-  EXPECT_THAT(Results.back().CompileFlags.Add, IsEmpty());
+}
+
+TEST(ParseYAML, Invalid) {
+  CapturedDiags Diags;
+  const char *YAML = R"yaml(
+If:
+
+horrible
+---
+- 1
+  )yaml";
+  auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());
+  EXPECT_THAT(Diags.Diagnostics,
+  ElementsAre(DiagMessage("If should be a dictionary"),
+  DiagMessage("Config should be a dictionary")));
+  ASSERT_THAT(Results, IsEmpty());
 }
 
 } // namespace
Index: clang-tools-extra/clangd/ConfigYAML.cpp
===
--- clang-tools-extra/clangd/ConfigYAML.cpp
+++ clang-tools-extra/clangd/ConfigYAML.cpp
@@ -26,49 +26,47 @@
 
 class Parser {
   llvm::SourceMgr 
+  bool HadError = false;
 
 public:
   Parser(llvm::SourceMgr ) : SM(SM) {}
 
   // Tries to parse N into F, returning false if it failed and we couldn't
-  // meaningfully recover (e.g. YAML syntax error broke the stream).
-  // The private parse() helpers follow the same pattern.
+  // meaningfully recover (YAML syntax error, or hard semantic error).
   bool parse(Fragment , Node ) {
 DictParser Dict("Config", this);
-Dict.handle("If", [&](Node ) { return parse(F.If, N); });
-Dict.handle("CompileFlags",
-[&](Node ) { return parse(F.CompileFlags, N); });
-return Dict.parse(N);
+Dict.handle("If", [&](Node ) { parse(F.If, N); });
+Dict.handle("CompileFlags", [&](Node ) { parse(F.CompileFlags, N); });
+Dict.parse(N);
+return !(N.failed() || HadError);
   }
 
 private:
-  bool parse(Fragment::IfBlock , Node ) {
+  void parse(Fragment::IfBlock , Node ) {
 DictParser Dict("If", this);
 Dict.unrecognized(
 [&](llvm::StringRef) { F.HasUnrecognizedCondition = true; });
 Dict.handle("PathMatch", [&](Node ) {
   if (auto Values = scalarValues(N))
 F.PathMatch = std::move(*Values);
-  return !N.failed();
 });
-return Dict.parse(N);
+Dict.parse(N);
   }
 
-  bool parse(Fragment::CompileFlagsBlock , Node ) {
+  void parse(Fragment::CompileFlagsBlock , Node ) {
 DictParser Dict("CompileFlags", this);
 Dict.handle("Add", [&](Node ) {
   if (auto Values = scalarValues(N))
 F.Add = std::move(*Values);
-  return !N.failed();
 });
-return Dict.parse(N);
+Dict.parse(N);
   }
 
   // Helper for parsing mapping nodes (dictionaries).
   // We don't use YamlIO as we want to control over unknown keys.
   class DictParser {
 llvm::StringRef Description;
-std::vector>> Keys;
+std::vector>> Keys;
 std::function Unknown;
 Parser *Outer;
 
@@ -79,7 +77,7 @@
 // Parse is called when Key is encountered, and passed the associated value.
 // It should emit diagnostics if the value is invalid (e.g. wrong type).
 // If Key is seen twice, Parse runs only once and an error is reported.
-void handle(llvm::StringLiteral Key, std::function Parse) {
+void handle(llvm::StringLiteral Key, std::function Parse) {
   for (const auto  : Keys) {
 (void) Entry;
 assert(Entry.first != Key && "duplicate key handler");
@@ -94,16 +92,17 @@
 }
 
 // Process a mapping node and call handlers for each key/value pair.
-bool parse(Node ) const {
+void parse(Node ) const {
   if (N.getType() != Node::NK_Mapping) {
 Outer->error(Description + " should be a dictionary", N);
-return false;
+return;
   }
   llvm::SmallSet Seen;
+  // We *must* consume all items, even on error, or the parser will assert.
   for (auto  : llvm::cast(N)) {
 auto *K = KV.getKey();
 if (!K) // YAMLParser emitted an error.
-  return false;
+  continue;
 auto Key = Outer->scalarValue(*K, "Dictionary key");
 if (!Key)
 

[PATCH] D83436: [clangd] Fix error handling in config.yaml parsing.

2020-07-09 Thread Haojian Wu via Phabricator via cfe-commits
hokein accepted this revision.
hokein added a comment.
This revision is now accepted and ready to land.

thanks.




Comment at: clang-tools-extra/clangd/ConfigYAML.cpp:36
   // meaningfully recover (e.g. YAML syntax error broke the stream).
   // The private parse() helpers follow the same pattern.
   bool parse(Fragment , Node ) {

nit: the comment is stale. 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83436/new/

https://reviews.llvm.org/D83436



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D83436: [clangd] Fix error handling in config.yaml parsing.

2020-07-08 Thread Sam McCall via Phabricator via cfe-commits
sammccall created this revision.
sammccall added a reviewer: hokein.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
MaskRay, ilya-biryukov.
Herald added a project: clang.

A few things were broken:

- use of Document::parseBlockNode() is incorrect and prevents moving to the 
next doc in error cases. Use getRoot() instead.
- bailing out in the middle of iterating over a list/dict isn't allowed, unless 
you are going to throw away the parser: the next skip() asserts. Always consume 
all items.
- There were two concepts of fatal errors: error-diagnostics and drop-fragment. 
(The latter is the "return false" case in the parser). They didn't coincide. 
Now, parser errors and explicitly emitted error diagnostics are fatal.

Fixes https://github.com/clangd/clangd/issues/452


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D83436

Files:
  clang-tools-extra/clangd/ConfigYAML.cpp
  clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp

Index: clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
===
--- clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp
@@ -98,10 +98,25 @@
 DiagKind(llvm::SourceMgr::DK_Error),
 DiagPos(YAML.point()), DiagRange(llvm::None;
 
-  ASSERT_EQ(Results.size(), 2u);
+  ASSERT_EQ(Results.size(), 1u); // invalid fragment discarded.
   EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(Val("first")));
   EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition);
-  EXPECT_THAT(Results.back().CompileFlags.Add, IsEmpty());
+}
+
+TEST(ParseYAML, Invalid) {
+  CapturedDiags Diags;
+  const char *YAML = R"yaml(
+If:
+
+horrible
+---
+- 1
+  )yaml";
+  auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());
+  EXPECT_THAT(Diags.Diagnostics,
+  ElementsAre(DiagMessage("If should be a dictionary"),
+  DiagMessage("Config should be a dictionary")));
+  ASSERT_THAT(Results, IsEmpty());
 }
 
 } // namespace
Index: clang-tools-extra/clangd/ConfigYAML.cpp
===
--- clang-tools-extra/clangd/ConfigYAML.cpp
+++ clang-tools-extra/clangd/ConfigYAML.cpp
@@ -26,6 +26,7 @@
 
 class Parser {
   llvm::SourceMgr 
+  bool HadError = false;
 
 public:
   Parser(llvm::SourceMgr ) : SM(SM) {}
@@ -35,40 +36,38 @@
   // The private parse() helpers follow the same pattern.
   bool parse(Fragment , Node ) {
 DictParser Dict("Config", this);
-Dict.handle("If", [&](Node ) { return parse(F.If, N); });
-Dict.handle("CompileFlags",
-[&](Node ) { return parse(F.CompileFlags, N); });
-return Dict.parse(N);
+Dict.handle("If", [&](Node ) { parse(F.If, N); });
+Dict.handle("CompileFlags", [&](Node ) { parse(F.CompileFlags, N); });
+Dict.parse(N);
+return !(N.failed() || HadError);
   }
 
 private:
-  bool parse(Fragment::IfBlock , Node ) {
+  void parse(Fragment::IfBlock , Node ) {
 DictParser Dict("If", this);
 Dict.unrecognized(
 [&](llvm::StringRef) { F.HasUnrecognizedCondition = true; });
 Dict.handle("PathMatch", [&](Node ) {
   if (auto Values = scalarValues(N))
 F.PathMatch = std::move(*Values);
-  return !N.failed();
 });
-return Dict.parse(N);
+Dict.parse(N);
   }
 
-  bool parse(Fragment::CompileFlagsBlock , Node ) {
+  void parse(Fragment::CompileFlagsBlock , Node ) {
 DictParser Dict("CompileFlags", this);
 Dict.handle("Add", [&](Node ) {
   if (auto Values = scalarValues(N))
 F.Add = std::move(*Values);
-  return !N.failed();
 });
-return Dict.parse(N);
+Dict.parse(N);
   }
 
   // Helper for parsing mapping nodes (dictionaries).
   // We don't use YamlIO as we want to control over unknown keys.
   class DictParser {
 llvm::StringRef Description;
-std::vector>> Keys;
+std::vector>> Keys;
 std::function Unknown;
 Parser *Outer;
 
@@ -79,7 +78,7 @@
 // Parse is called when Key is encountered, and passed the associated value.
 // It should emit diagnostics if the value is invalid (e.g. wrong type).
 // If Key is seen twice, Parse runs only once and an error is reported.
-void handle(llvm::StringLiteral Key, std::function Parse) {
+void handle(llvm::StringLiteral Key, std::function Parse) {
   for (const auto  : Keys) {
 (void) Entry;
 assert(Entry.first != Key && "duplicate key handler");
@@ -94,16 +93,17 @@
 }
 
 // Process a mapping node and call handlers for each key/value pair.
-bool parse(Node ) const {
+void parse(Node ) const {
   if (N.getType() != Node::NK_Mapping) {
 Outer->error(Description + " should be a dictionary", N);
-return false;
+return;
   }
   llvm::SmallSet Seen;
+  // We *must* consume all items, even on