[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-07-01 Thread Sam McCall via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
sammccall marked an inline comment as done.
Closed by commit rGf12cd99c440a: [clangd] Config: compile Fragment - 
CompiledFragment - Config (authored by sammccall).

Changed prior to commit:
  https://reviews.llvm.org/D82612?vs=274623=274713#toc

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigFragment.h
  clang-tools-extra/clangd/ConfigProvider.h
  clang-tools-extra/clangd/ConfigYAML.cpp
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigTesting.h
  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
@@ -8,14 +8,13 @@
 
 #include "Annotations.h"
 #include "ConfigFragment.h"
-#include "Matchers.h"
+#include "ConfigTesting.h"
 #include "Protocol.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "gtest/internal/gtest-internal.h"
 
 namespace clang {
 namespace clangd {
@@ -36,55 +35,6 @@
   return false;
 }
 
-Position toPosition(llvm::SMLoc L, const llvm::SourceMgr ) {
-  auto LineCol = SM.getLineAndColumn(L);
-  Position P;
-  P.line = LineCol.first - 1;
-  P.character = LineCol.second - 1;
-  return P;
-}
-
-Range toRange(llvm::SMRange R, const llvm::SourceMgr ) {
-  return Range{toPosition(R.Start, SM), toPosition(R.End, SM)};
-}
-
-struct CapturedDiags {
-  std::function callback() {
-return [this](const llvm::SMDiagnostic ) {
-  Diagnostics.emplace_back();
-  Diag  = Diagnostics.back();
-  Out.Message = D.getMessage().str();
-  Out.Kind = D.getKind();
-  Out.Pos.line = D.getLineNo() - 1;
-  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
-  if (!D.getRanges().empty()) {
-const auto  = D.getRanges().front();
-Out.Rng.emplace();
-Out.Rng->start.line = Out.Rng->end.line = Out.Pos.line;
-Out.Rng->start.character = R.first;
-Out.Rng->end.character = R.second;
-  }
-};
-  }
-  struct Diag {
-std::string Message;
-llvm::SourceMgr::DiagKind Kind;
-Position Pos;
-llvm::Optional Rng;
-
-friend void PrintTo(const Diag , std::ostream *OS) {
-  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
-  << D.Message << "@" << llvm::to_string(D.Pos);
-}
-  };
-  std::vector Diagnostics;
-};
-
-MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }
-MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }
-MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }
-MATCHER_P(DiagRange, R, "") { return arg.Rng == R; }
-
 TEST(ParseYAML, SyntacticForms) {
   CapturedDiags Diags;
   const char *YAML = R"yaml(
@@ -101,8 +51,8 @@
   auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());
   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
   ASSERT_EQ(Results.size(), 2u);
-  EXPECT_FALSE(Results.front().Condition.HasUnrecognizedCondition);
-  EXPECT_THAT(Results.front().Condition.PathMatch, ElementsAre(Val("abc")));
+  EXPECT_FALSE(Results.front().If.HasUnrecognizedCondition);
+  EXPECT_THAT(Results.front().If.PathMatch, ElementsAre(Val("abc")));
   EXPECT_THAT(Results.front().CompileFlags.Add,
   ElementsAre(Val("foo"), Val("bar")));
 
@@ -120,7 +70,7 @@
   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
   ASSERT_EQ(Results.size(), 1u);
   ASSERT_NE(Results.front().Source.Manager, nullptr);
-  EXPECT_EQ(toRange(Results.front().Condition.PathMatch.front().Range,
+  EXPECT_EQ(toRange(Results.front().If.PathMatch.front().Range,
 *Results.front().Source.Manager),
 YAML.range());
 }
@@ -140,7 +90,7 @@
 
   ASSERT_THAT(
   Diags.Diagnostics,
-  ElementsAre(AllOf(DiagMessage("Unknown Condition key UnknownCondition"),
+  ElementsAre(AllOf(DiagMessage("Unknown If key UnknownCondition"),
 DiagKind(llvm::SourceMgr::DK_Warning),
 DiagPos(YAML.range().start), DiagRange(YAML.range())),
   AllOf(DiagMessage("Unexpected token. Expected Key, Flow "
@@ -150,7 +100,7 @@
 
   ASSERT_EQ(Results.size(), 2u);
   EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(Val("first")));
-  EXPECT_TRUE(Results.front().Condition.HasUnrecognizedCondition);
+  EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition);
   EXPECT_THAT(Results.back().CompileFlags.Add, IsEmpty());
 }
 
Index: 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-07-01 Thread Sam McCall via Phabricator via cfe-commits
sammccall marked 8 inline comments as done.
sammccall added a comment.

Thanks a lot for the review, I think this is a lot better now.
And particularly on where docs are lacking - I'm happy to write comments but 
it's hard for me to know which bits are least clear.




Comment at: clang-tools-extra/clangd/ConfigFragment.h:100
   /// Each separate condition must match (combined with AND).
   /// When one condition has multiple values, any may match (combined with OR).
   ///

hokein wrote:
> I think this comment is important, but it is a bit vague (not quite friendly 
> to readers without much config context), we might want to refine it -- would 
> be nice to clearly state what's a condition, e.g. "PathMatch" is a condition, 
> "Platform" is a condition. 
> 
> 
> maybe separate the rename to a separate patch, but up to you.
Updated comment to tie the concept of conditions to the contents of the If 
block, and added an example for the OR matching.



Comment at: clang-tools-extra/clangd/ConfigFragment.h:108
+  struct IfBlock {
 /// The file being processed must fully match a regular expression.
 std::vector> PathMatch;

hokein wrote:
> nit: maybe explicitly spell this is an `OR` match.
I'd prefer not to do this explicitly in each condition, as it's hard to get 
across in a couple of words and I think it'd hurt readability overall to repeat 
it so much.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612



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


[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-07-01 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, looks good.




Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:65
+if (F.HasUnrecognizedCondition)
+  Conditions.push_back([&](const Params &) { return false; });
+

sammccall wrote:
> hokein wrote:
> > sammccall wrote:
> > > hokein wrote:
> > > > I think if this case happened, we might just bail out directly -- 
> > > > adding the regex conditions seems unnecessary, we never execute them on 
> > > > Line 127 (but we might still need computing the regex for diagnostics).
> > > > 
> > > > and I'm not sure the motivation of using `vector` for `Conditions` and 
> > > > `Apply`, it looks like Apply is just 1 element (if we do the bailout 
> > > > for conditions, `Conditions` will be at most 1 element).
> > > > 
> > > > I feel like using a single unique_function for `Conditions` and `Apply` 
> > > > might be a better fit with `Fragment`. There is a comment in 
> > > > `ConfigFragment.h`:
> > > > 
> > > > >  Each separate condition must match (combined with AND).
> > > > >  When one condition has multiple values, any may match (combined with 
> > > > > OR).
> > > >  
> > > > so my reading is that:
> > > > 
> > > > - `Fragment` and `CompiledFragment` is a 1:1 mapping
> > > > - A `Fragment::Condition` represents a single condition, so the AND 
> > > > match applies for different `Fragment`s, which is in ConfigProvider?
> > > > - A single condition can have multiple values (`PathMatch`), we use OR 
> > > > match
> > > > 
> > > > Is that correct?
> > > > I think if this case happened, we might just bail out directly -- 
> > > > adding the regex conditions seems unnecessary, we never execute them on 
> > > > Line 127 (but we might still need computing the regex for diagnostics).
> > > >
> > > > and I'm not sure the motivation of using vector for Conditions and 
> > > > Apply, it looks like Apply is just 1 element (if we do the bailout for 
> > > > conditions, Conditions will be at most 1 element).
> > > 
> > > So the explanation for both of these things is that this file is going to 
> > > grow a lot, and in particular these sections handling PathMatch etc are 
> > > going to occur again and again, so we need a scalable pattern. I've 
> > > deliberately limited the scope of the first patchset to only support one 
> > > condition, one setting to apply etc, but you have to imagine there are 
> > > likely to be 5 conditions and maybe 25 settings.
> > > 
> > > So with that in mind:
> > >  - yes, actually saving the conditions isn't necessary, but we do need to 
> > > evaluate them for side-effects (diagnostics) and it's simpler not to keep 
> > > track of them. Optimizing performance of broken configs is not a big win 
> > > :-)
> > >  - it's marginally simpler to have only one Apply function, but much 
> > > simpler if separate parts of compile() can push settings independently. 
> > > Same goes for condition in fact...
> > > 
> > > > Fragment and CompiledFragment is a 1:1 mapping
> > > 
> > > Yes. I think this is somewhere where the naming is helpful at least :-)
> > > 
> > > > A Fragment::Condition represents a single condition, so the AND match 
> > > > applies for different Fragments, which is in ConfigProvider?
> > > 
> > > The AND match applies to the multiple Conditions in a single 
> > > CompiledFragment. Each fragment only considers its own conditions. 
> > > So given:
> > > ```
> > > If:
> > >   Platform: win
> > >   PathMatch: [foo1/.*, foo2/.*]
> > > CompileFlags:
> > >   Add: -foo
> > > ---
> > > CompileFlags:
> > >   Add: -bar
> > > ```
> > > 
> > > We end up with... one Provider, suppling two CompiledFragments (each 
> > > compiled from a separate Fragment).
> > > 
> > > The first CompiledFragment has two Conditions, conceptually:
> > >  - `Params.Platform == "win"`
> > >  - `Regex("foo1/.*").matches(Params.File) || 
> > > Regex("foo2/.*").matches(Params.File)`
> > > 
> > > The second CompiledFragment has no Conditions. Each fragment has one 
> > > Apply function.
> > > 
> > > The CompiledFragments are applied independently. They each consider their 
> > > own Conditions, ANDing them together.
> > > 
> > > So CompiledFragment #1 checks both conditions. If any fails, it bails 
> > > out. Otherwise it runs its Apply function, adding `-foo`.
> > > 
> > > Then CompiledFragment #2 runs. It has no conditions, so never bails out. 
> > > It adds `-bar`.
> > oh, thanks for the explanation, this helps me a lot to understand the code.
> > 
> > so the member `Condition` in `Fragment` will be enhanced to something like 
> > `std::vector Conditions` in the future? If so, can we defer that for 
> > `CompiledFragment` as well? 
> > 
> > 
> > Since `Fragment` and `CompiledFragment` is 1:1 mapping, I think it is 
> > important to keep their implementation aligned, it'd help a lot for readers 
> > to understand the code. The condition logic (AND, OR) is subtle and 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Sam McCall via Phabricator via cfe-commits
sammccall added inline comments.



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:65
+if (F.HasUnrecognizedCondition)
+  Conditions.push_back([&](const Params &) { return false; });
+

hokein wrote:
> sammccall wrote:
> > hokein wrote:
> > > I think if this case happened, we might just bail out directly -- adding 
> > > the regex conditions seems unnecessary, we never execute them on Line 127 
> > > (but we might still need computing the regex for diagnostics).
> > > 
> > > and I'm not sure the motivation of using `vector` for `Conditions` and 
> > > `Apply`, it looks like Apply is just 1 element (if we do the bailout for 
> > > conditions, `Conditions` will be at most 1 element).
> > > 
> > > I feel like using a single unique_function for `Conditions` and `Apply` 
> > > might be a better fit with `Fragment`. There is a comment in 
> > > `ConfigFragment.h`:
> > > 
> > > >  Each separate condition must match (combined with AND).
> > > >  When one condition has multiple values, any may match (combined with 
> > > > OR).
> > >  
> > > so my reading is that:
> > > 
> > > - `Fragment` and `CompiledFragment` is a 1:1 mapping
> > > - A `Fragment::Condition` represents a single condition, so the AND match 
> > > applies for different `Fragment`s, which is in ConfigProvider?
> > > - A single condition can have multiple values (`PathMatch`), we use OR 
> > > match
> > > 
> > > Is that correct?
> > > I think if this case happened, we might just bail out directly -- adding 
> > > the regex conditions seems unnecessary, we never execute them on Line 127 
> > > (but we might still need computing the regex for diagnostics).
> > >
> > > and I'm not sure the motivation of using vector for Conditions and Apply, 
> > > it looks like Apply is just 1 element (if we do the bailout for 
> > > conditions, Conditions will be at most 1 element).
> > 
> > So the explanation for both of these things is that this file is going to 
> > grow a lot, and in particular these sections handling PathMatch etc are 
> > going to occur again and again, so we need a scalable pattern. I've 
> > deliberately limited the scope of the first patchset to only support one 
> > condition, one setting to apply etc, but you have to imagine there are 
> > likely to be 5 conditions and maybe 25 settings.
> > 
> > So with that in mind:
> >  - yes, actually saving the conditions isn't necessary, but we do need to 
> > evaluate them for side-effects (diagnostics) and it's simpler not to keep 
> > track of them. Optimizing performance of broken configs is not a big win :-)
> >  - it's marginally simpler to have only one Apply function, but much 
> > simpler if separate parts of compile() can push settings independently. 
> > Same goes for condition in fact...
> > 
> > > Fragment and CompiledFragment is a 1:1 mapping
> > 
> > Yes. I think this is somewhere where the naming is helpful at least :-)
> > 
> > > A Fragment::Condition represents a single condition, so the AND match 
> > > applies for different Fragments, which is in ConfigProvider?
> > 
> > The AND match applies to the multiple Conditions in a single 
> > CompiledFragment. Each fragment only considers its own conditions. 
> > So given:
> > ```
> > If:
> >   Platform: win
> >   PathMatch: [foo1/.*, foo2/.*]
> > CompileFlags:
> >   Add: -foo
> > ---
> > CompileFlags:
> >   Add: -bar
> > ```
> > 
> > We end up with... one Provider, suppling two CompiledFragments (each 
> > compiled from a separate Fragment).
> > 
> > The first CompiledFragment has two Conditions, conceptually:
> >  - `Params.Platform == "win"`
> >  - `Regex("foo1/.*").matches(Params.File) || 
> > Regex("foo2/.*").matches(Params.File)`
> > 
> > The second CompiledFragment has no Conditions. Each fragment has one Apply 
> > function.
> > 
> > The CompiledFragments are applied independently. They each consider their 
> > own Conditions, ANDing them together.
> > 
> > So CompiledFragment #1 checks both conditions. If any fails, it bails out. 
> > Otherwise it runs its Apply function, adding `-foo`.
> > 
> > Then CompiledFragment #2 runs. It has no conditions, so never bails out. It 
> > adds `-bar`.
> oh, thanks for the explanation, this helps me a lot to understand the code.
> 
> so the member `Condition` in `Fragment` will be enhanced to something like 
> `std::vector Conditions` in the future? If so, can we defer that for 
> `CompiledFragment` as well? 
> 
> 
> Since `Fragment` and `CompiledFragment` is 1:1 mapping, I think it is 
> important to keep their implementation aligned, it'd help a lot for readers 
> to understand the code. The condition logic (AND, OR) is subtle and 
> complicated,  I was reading this part of code back and forth, still inferred 
> it in an incorrect way.
> so the member Condition in Fragment will be enhanced to something like 
> std::vector Conditions in the future?

Ah, not quite...
Fragment is supposed to closely follow the YAML format, which is:
`If: { 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 274623.
sammccall marked 13 inline comments as done.
sammccall added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigFragment.h
  clang-tools-extra/clangd/ConfigProvider.h
  clang-tools-extra/clangd/ConfigYAML.cpp
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigTesting.h
  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
@@ -8,14 +8,13 @@
 
 #include "Annotations.h"
 #include "ConfigFragment.h"
-#include "Matchers.h"
+#include "ConfigTesting.h"
 #include "Protocol.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "gtest/internal/gtest-internal.h"
 
 namespace clang {
 namespace clangd {
@@ -36,55 +35,6 @@
   return false;
 }
 
-Position toPosition(llvm::SMLoc L, const llvm::SourceMgr ) {
-  auto LineCol = SM.getLineAndColumn(L);
-  Position P;
-  P.line = LineCol.first - 1;
-  P.character = LineCol.second - 1;
-  return P;
-}
-
-Range toRange(llvm::SMRange R, const llvm::SourceMgr ) {
-  return Range{toPosition(R.Start, SM), toPosition(R.End, SM)};
-}
-
-struct CapturedDiags {
-  std::function callback() {
-return [this](const llvm::SMDiagnostic ) {
-  Diagnostics.emplace_back();
-  Diag  = Diagnostics.back();
-  Out.Message = D.getMessage().str();
-  Out.Kind = D.getKind();
-  Out.Pos.line = D.getLineNo() - 1;
-  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
-  if (!D.getRanges().empty()) {
-const auto  = D.getRanges().front();
-Out.Rng.emplace();
-Out.Rng->start.line = Out.Rng->end.line = Out.Pos.line;
-Out.Rng->start.character = R.first;
-Out.Rng->end.character = R.second;
-  }
-};
-  }
-  struct Diag {
-std::string Message;
-llvm::SourceMgr::DiagKind Kind;
-Position Pos;
-llvm::Optional Rng;
-
-friend void PrintTo(const Diag , std::ostream *OS) {
-  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
-  << D.Message << "@" << llvm::to_string(D.Pos);
-}
-  };
-  std::vector Diagnostics;
-};
-
-MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }
-MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }
-MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }
-MATCHER_P(DiagRange, R, "") { return arg.Rng == R; }
-
 TEST(ParseYAML, SyntacticForms) {
   CapturedDiags Diags;
   const char *YAML = R"yaml(
@@ -101,8 +51,8 @@
   auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());
   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
   ASSERT_EQ(Results.size(), 2u);
-  EXPECT_FALSE(Results.front().Condition.HasUnrecognizedCondition);
-  EXPECT_THAT(Results.front().Condition.PathMatch, ElementsAre(Val("abc")));
+  EXPECT_FALSE(Results.front().If.HasUnrecognizedCondition);
+  EXPECT_THAT(Results.front().If.PathMatch, ElementsAre(Val("abc")));
   EXPECT_THAT(Results.front().CompileFlags.Add,
   ElementsAre(Val("foo"), Val("bar")));
 
@@ -120,7 +70,7 @@
   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
   ASSERT_EQ(Results.size(), 1u);
   ASSERT_NE(Results.front().Source.Manager, nullptr);
-  EXPECT_EQ(toRange(Results.front().Condition.PathMatch.front().Range,
+  EXPECT_EQ(toRange(Results.front().If.PathMatch.front().Range,
 *Results.front().Source.Manager),
 YAML.range());
 }
@@ -140,7 +90,7 @@
 
   ASSERT_THAT(
   Diags.Diagnostics,
-  ElementsAre(AllOf(DiagMessage("Unknown Condition key UnknownCondition"),
+  ElementsAre(AllOf(DiagMessage("Unknown If key UnknownCondition"),
 DiagKind(llvm::SourceMgr::DK_Warning),
 DiagPos(YAML.range().start), DiagRange(YAML.range())),
   AllOf(DiagMessage("Unexpected token. Expected Key, Flow "
@@ -150,7 +100,7 @@
 
   ASSERT_EQ(Results.size(), 2u);
   EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(Val("first")));
-  EXPECT_TRUE(Results.front().Condition.HasUnrecognizedCondition);
+  EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition);
   EXPECT_THAT(Results.back().CompileFlags.Add, IsEmpty());
 }
 
Index: clang-tools-extra/clangd/unittests/ConfigTesting.h
===
--- /dev/null
+++ clang-tools-extra/clangd/unittests/ConfigTesting.h
@@ -0,0 +1,77 @@
+//===-- 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Haojian Wu via Phabricator via cfe-commits
hokein added inline comments.



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:65
+if (F.HasUnrecognizedCondition)
+  Conditions.push_back([&](const Params &) { return false; });
+

sammccall wrote:
> hokein wrote:
> > I think if this case happened, we might just bail out directly -- adding 
> > the regex conditions seems unnecessary, we never execute them on Line 127 
> > (but we might still need computing the regex for diagnostics).
> > 
> > and I'm not sure the motivation of using `vector` for `Conditions` and 
> > `Apply`, it looks like Apply is just 1 element (if we do the bailout for 
> > conditions, `Conditions` will be at most 1 element).
> > 
> > I feel like using a single unique_function for `Conditions` and `Apply` 
> > might be a better fit with `Fragment`. There is a comment in 
> > `ConfigFragment.h`:
> > 
> > >  Each separate condition must match (combined with AND).
> > >  When one condition has multiple values, any may match (combined with OR).
> >  
> > so my reading is that:
> > 
> > - `Fragment` and `CompiledFragment` is a 1:1 mapping
> > - A `Fragment::Condition` represents a single condition, so the AND match 
> > applies for different `Fragment`s, which is in ConfigProvider?
> > - A single condition can have multiple values (`PathMatch`), we use OR match
> > 
> > Is that correct?
> > I think if this case happened, we might just bail out directly -- adding 
> > the regex conditions seems unnecessary, we never execute them on Line 127 
> > (but we might still need computing the regex for diagnostics).
> >
> > and I'm not sure the motivation of using vector for Conditions and Apply, 
> > it looks like Apply is just 1 element (if we do the bailout for conditions, 
> > Conditions will be at most 1 element).
> 
> So the explanation for both of these things is that this file is going to 
> grow a lot, and in particular these sections handling PathMatch etc are going 
> to occur again and again, so we need a scalable pattern. I've deliberately 
> limited the scope of the first patchset to only support one condition, one 
> setting to apply etc, but you have to imagine there are likely to be 5 
> conditions and maybe 25 settings.
> 
> So with that in mind:
>  - yes, actually saving the conditions isn't necessary, but we do need to 
> evaluate them for side-effects (diagnostics) and it's simpler not to keep 
> track of them. Optimizing performance of broken configs is not a big win :-)
>  - it's marginally simpler to have only one Apply function, but much simpler 
> if separate parts of compile() can push settings independently. Same goes for 
> condition in fact...
> 
> > Fragment and CompiledFragment is a 1:1 mapping
> 
> Yes. I think this is somewhere where the naming is helpful at least :-)
> 
> > A Fragment::Condition represents a single condition, so the AND match 
> > applies for different Fragments, which is in ConfigProvider?
> 
> The AND match applies to the multiple Conditions in a single 
> CompiledFragment. Each fragment only considers its own conditions. 
> So given:
> ```
> If:
>   Platform: win
>   PathMatch: [foo1/.*, foo2/.*]
> CompileFlags:
>   Add: -foo
> ---
> CompileFlags:
>   Add: -bar
> ```
> 
> We end up with... one Provider, suppling two CompiledFragments (each compiled 
> from a separate Fragment).
> 
> The first CompiledFragment has two Conditions, conceptually:
>  - `Params.Platform == "win"`
>  - `Regex("foo1/.*").matches(Params.File) || 
> Regex("foo2/.*").matches(Params.File)`
> 
> The second CompiledFragment has no Conditions. Each fragment has one Apply 
> function.
> 
> The CompiledFragments are applied independently. They each consider their own 
> Conditions, ANDing them together.
> 
> So CompiledFragment #1 checks both conditions. If any fails, it bails out. 
> Otherwise it runs its Apply function, adding `-foo`.
> 
> Then CompiledFragment #2 runs. It has no conditions, so never bails out. It 
> adds `-bar`.
oh, thanks for the explanation, this helps me a lot to understand the code.

so the member `Condition` in `Fragment` will be enhanced to something like 
`std::vector Conditions` in the future? If so, can we defer that for 
`CompiledFragment` as well? 


Since `Fragment` and `CompiledFragment` is 1:1 mapping, I think it is important 
to keep their implementation aligned, it'd help a lot for readers to understand 
the code. The condition logic (AND, OR) is subtle and complicated,  I was 
reading this part of code back and forth, still inferred it in an incorrect way.



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:40
+
+struct Impl {
+  std::vector> Conditions;

nit: I'd use a more verbose name, `CompiledFragmentImpl`.



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:46
+  if (!C(P)) {
+dlog("config::Fragment {0}: condition not met", this);
+return false;

maybe `Impl::applying config ...` to allow 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 274503.
sammccall added a comment.

CompiledFragment becomes a std::function, compile interface moved to Fragment.h


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigFragment.h
  clang-tools-extra/clangd/ConfigProvider.h
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigTesting.h
  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
@@ -8,14 +8,13 @@
 
 #include "Annotations.h"
 #include "ConfigFragment.h"
-#include "Matchers.h"
+#include "ConfigTesting.h"
 #include "Protocol.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "gtest/internal/gtest-internal.h"
 
 namespace clang {
 namespace clangd {
@@ -36,55 +35,6 @@
   return false;
 }
 
-Position toPosition(llvm::SMLoc L, const llvm::SourceMgr ) {
-  auto LineCol = SM.getLineAndColumn(L);
-  Position P;
-  P.line = LineCol.first - 1;
-  P.character = LineCol.second - 1;
-  return P;
-}
-
-Range toRange(llvm::SMRange R, const llvm::SourceMgr ) {
-  return Range{toPosition(R.Start, SM), toPosition(R.End, SM)};
-}
-
-struct CapturedDiags {
-  std::function callback() {
-return [this](const llvm::SMDiagnostic ) {
-  Diagnostics.emplace_back();
-  Diag  = Diagnostics.back();
-  Out.Message = D.getMessage().str();
-  Out.Kind = D.getKind();
-  Out.Pos.line = D.getLineNo() - 1;
-  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
-  if (!D.getRanges().empty()) {
-const auto  = D.getRanges().front();
-Out.Rng.emplace();
-Out.Rng->start.line = Out.Rng->end.line = Out.Pos.line;
-Out.Rng->start.character = R.first;
-Out.Rng->end.character = R.second;
-  }
-};
-  }
-  struct Diag {
-std::string Message;
-llvm::SourceMgr::DiagKind Kind;
-Position Pos;
-llvm::Optional Rng;
-
-friend void PrintTo(const Diag , std::ostream *OS) {
-  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
-  << D.Message << "@" << llvm::to_string(D.Pos);
-}
-  };
-  std::vector Diagnostics;
-};
-
-MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }
-MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }
-MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }
-MATCHER_P(DiagRange, R, "") { return arg.Rng == R; }
-
 TEST(ParseYAML, SyntacticForms) {
   CapturedDiags Diags;
   const char *YAML = R"yaml(
Index: clang-tools-extra/clangd/unittests/ConfigTesting.h
===
--- /dev/null
+++ clang-tools-extra/clangd/unittests/ConfigTesting.h
@@ -0,0 +1,77 @@
+//===-- ConfigTesting.h - Helpers for configuration tests ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+
+#include "Protocol.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gmock/gmock.h"
+#include 
+
+namespace clang {
+namespace clangd {
+namespace config {
+
+// Provides a DiagnosticsCallback that records diganostics.
+// Unlike just pushing them into a vector, underlying storage need not survive.
+struct CapturedDiags {
+  std::function callback() {
+return [this](const llvm::SMDiagnostic ) {
+  Diagnostics.emplace_back();
+  Diag  = Diagnostics.back();
+  Out.Message = D.getMessage().str();
+  Out.Kind = D.getKind();
+  Out.Pos.line = D.getLineNo() - 1;
+  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
+  if (!D.getRanges().empty()) {
+const auto  = D.getRanges().front();
+Out.Range.emplace();
+Out.Range->start.line = Out.Range->end.line = Out.Pos.line;
+Out.Range->start.character = R.first;
+Out.Range->end.character = R.second;
+  }
+};
+  }
+  struct Diag {
+std::string Message;
+llvm::SourceMgr::DiagKind Kind;
+Position Pos;
+llvm::Optional Range;
+
+friend void PrintTo(const Diag , std::ostream *OS) {
+  *OS << (D.Kind == 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Sam McCall via Phabricator via cfe-commits
sammccall marked an inline comment as done.
sammccall added inline comments.



Comment at: clang-tools-extra/clangd/ConfigProvider.h:47
+  /// This always produces a usable compiled fragment (errors are recovered).
+  explicit CompiledFragment(Fragment, DiagnosticCallback);
+

sammccall wrote:
> hokein wrote:
> > sammccall wrote:
> > > hokein wrote:
> > > > Would it be more nature to have a function `compile`(or so) to do the 
> > > > actual compile (Fragment -> CompiledFragment) rather than doing it in a 
> > > > constructor?
> > > I'm not sure. The idea that the input/result is 
> > > Fragment/CompiledFragment, and that the compilation cannot fail, suggests 
> > > to me that a constructor is OK here.
> > > On the other hand, the DiangosticCallback param (which we don't keep a 
> > > reference to) is a bit weird.
> > > 
> > > So I don't really feel strongly about it one way or the other, happy to 
> > > change it if you do think it would be significantly better.
> > You point is fair. 
> > I'm not sure using `compile` is *significantly* better, but it is better 
> > IMO -- conceptually, the class name `CompiledFragment` implies that there 
> > should be a `compile` API (this was my impression when reading the code at 
> > the first time)
> > 
> > it might be ok to keep as-is.
> Argh, you convinced me this would be better, but I can't come up with a form 
> I like.
> 
> - `static CompiledFragment::compile` repeats itself in a weird way. But any 
> other option requires messing around with friend functions, which feels off
> - `Fragment::compile` is maybe the most natural, but the dependencies are 
> backwards (needs the CompiledFragment type, it gets implemented in the wrong 
> file...).
> - standalone `compile()` is maybe the most palatable, but seems a little 
> undiscoverable
> - having a function return CompiledFragment means that putting it into a 
> shared_ptr is annoying work, and breaks our pointer-logging trick, but having 
> the factory function return a pointer also seems odd.
> 
> I'm starting to think maybe the CompiledFragment class shouldn't be public at 
> all, and we should just `using CompiledFragment = unique_function Params&, Config&) const>`. WDYT?
> I'm starting to think maybe the CompiledFragment class shouldn't be public at 
> all

Thinking more about this:

 - making it std::function<...> instead lets us wrap the shared_ptr inside 
which makes a few APIs a bit cleaner
 - as its an interface type, this leaves config providers free to use other 
ways of manipulating config rather than going via fragments, which is 
potentially nice for embedders
 - the main downside is we're committing to not exposing any more structure 
(e.g. querying a fragment for which directory it applies to)

starting to quite like this idea.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612



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


[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 274470.
sammccall marked an inline comment as done.
sammccall added a comment.

address comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigProvider.h
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigTesting.h
  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
@@ -8,14 +8,13 @@
 
 #include "Annotations.h"
 #include "ConfigFragment.h"
-#include "Matchers.h"
+#include "ConfigTesting.h"
 #include "Protocol.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "gtest/internal/gtest-internal.h"
 
 namespace clang {
 namespace clangd {
@@ -36,55 +35,6 @@
   return false;
 }
 
-Position toPosition(llvm::SMLoc L, const llvm::SourceMgr ) {
-  auto LineCol = SM.getLineAndColumn(L);
-  Position P;
-  P.line = LineCol.first - 1;
-  P.character = LineCol.second - 1;
-  return P;
-}
-
-Range toRange(llvm::SMRange R, const llvm::SourceMgr ) {
-  return Range{toPosition(R.Start, SM), toPosition(R.End, SM)};
-}
-
-struct CapturedDiags {
-  std::function callback() {
-return [this](const llvm::SMDiagnostic ) {
-  Diagnostics.emplace_back();
-  Diag  = Diagnostics.back();
-  Out.Message = D.getMessage().str();
-  Out.Kind = D.getKind();
-  Out.Pos.line = D.getLineNo() - 1;
-  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
-  if (!D.getRanges().empty()) {
-const auto  = D.getRanges().front();
-Out.Rng.emplace();
-Out.Rng->start.line = Out.Rng->end.line = Out.Pos.line;
-Out.Rng->start.character = R.first;
-Out.Rng->end.character = R.second;
-  }
-};
-  }
-  struct Diag {
-std::string Message;
-llvm::SourceMgr::DiagKind Kind;
-Position Pos;
-llvm::Optional Rng;
-
-friend void PrintTo(const Diag , std::ostream *OS) {
-  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
-  << D.Message << "@" << llvm::to_string(D.Pos);
-}
-  };
-  std::vector Diagnostics;
-};
-
-MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }
-MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }
-MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }
-MATCHER_P(DiagRange, R, "") { return arg.Rng == R; }
-
 TEST(ParseYAML, SyntacticForms) {
   CapturedDiags Diags;
   const char *YAML = R"yaml(
Index: clang-tools-extra/clangd/unittests/ConfigTesting.h
===
--- /dev/null
+++ clang-tools-extra/clangd/unittests/ConfigTesting.h
@@ -0,0 +1,77 @@
+//===-- ConfigTesting.h - Helpers for configuration tests ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+
+#include "Protocol.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gmock/gmock.h"
+#include 
+
+namespace clang {
+namespace clangd {
+namespace config {
+
+// Provides a DiagnosticsCallback that records diganostics.
+// Unlike just pushing them into a vector, underlying storage need not survive.
+struct CapturedDiags {
+  std::function callback() {
+return [this](const llvm::SMDiagnostic ) {
+  Diagnostics.emplace_back();
+  Diag  = Diagnostics.back();
+  Out.Message = D.getMessage().str();
+  Out.Kind = D.getKind();
+  Out.Pos.line = D.getLineNo() - 1;
+  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
+  if (!D.getRanges().empty()) {
+const auto  = D.getRanges().front();
+Out.Range.emplace();
+Out.Range->start.line = Out.Range->end.line = Out.Pos.line;
+Out.Range->start.character = R.first;
+Out.Range->end.character = R.second;
+  }
+};
+  }
+  struct Diag {
+std::string Message;
+llvm::SourceMgr::DiagKind Kind;
+Position Pos;
+llvm::Optional Range;
+
+friend void PrintTo(const Diag , std::ostream *OS) {
+  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
+  << 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Sam McCall via Phabricator via cfe-commits
sammccall marked 8 inline comments as done.
sammccall added inline comments.



Comment at: clang-tools-extra/clangd/CMakeLists.txt:41
+  ConfigCompile.cpp
   ConfigYAML.cpp
   Diagnostics.cpp

hokein wrote:
> sammccall wrote:
> > hokein wrote:
> > > we have a few config-related files now, I wonder would it make sense to 
> > > create a config/ subdir?
> > It seems marginal to me - could go either way. We have a small handful (2 
> > headers and 3 impl files). There may be 1-2 more, but I'm not sure yet. 
> > (config-files-on-disk, and json->fragment).
> > So 6-7 files... smaller than the other subdirs, but not tiny.
> > 
> > There's also the fact that config.h/cpp is not like the others: all 
> > features (not just infra) depend on this, it's not in the `config::` 
> > namespace etc. So should it really be in the subdir?
> > 
> > Mind if we hold off on this until at least the scope of D82335 has landed? 
> > I think it would be awkward to do this move halfway through this patchset, 
> > but fairly easy to do at any point that things are stable.
> > it seems marginal to me - could go either way. We have a small handful (2 
> > headers and 3 impl files). There may be 1-2 more, but I'm not sure yet. 
> > (config-files-on-disk, and json->fragment).
> > So 6-7 files... smaller than the other subdirs, but not tiny.
> 
> if we end up with 6-7 files, I'd prefer to create a subdirectory, which make 
> our source structure a bit tidy, our clangd/ dir already has a lot of source 
> files.
> 
> 
> 
> > Mind if we hold off on this until at least the scope of D82335 has landed? 
> > I think it would be awkward to do this move halfway through this patchset, 
> > but fairly easy to do at any point that things are stable.
> 
> sure, I didn't mean we should do this in this patch.
> if we end up with 6-7 files, I'd prefer to create a subdirectory

SGTM. I'll carry on with this structure for now, and move files after - please 
remind me if I forget!



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:65
+if (F.HasUnrecognizedCondition)
+  Conditions.push_back([&](const Params &) { return false; });
+

hokein wrote:
> I think if this case happened, we might just bail out directly -- adding the 
> regex conditions seems unnecessary, we never execute them on Line 127 (but we 
> might still need computing the regex for diagnostics).
> 
> and I'm not sure the motivation of using `vector` for `Conditions` and 
> `Apply`, it looks like Apply is just 1 element (if we do the bailout for 
> conditions, `Conditions` will be at most 1 element).
> 
> I feel like using a single unique_function for `Conditions` and `Apply` might 
> be a better fit with `Fragment`. There is a comment in `ConfigFragment.h`:
> 
> >  Each separate condition must match (combined with AND).
> >  When one condition has multiple values, any may match (combined with OR).
>  
> so my reading is that:
> 
> - `Fragment` and `CompiledFragment` is a 1:1 mapping
> - A `Fragment::Condition` represents a single condition, so the AND match 
> applies for different `Fragment`s, which is in ConfigProvider?
> - A single condition can have multiple values (`PathMatch`), we use OR match
> 
> Is that correct?
> I think if this case happened, we might just bail out directly -- adding the 
> regex conditions seems unnecessary, we never execute them on Line 127 (but we 
> might still need computing the regex for diagnostics).
>
> and I'm not sure the motivation of using vector for Conditions and Apply, it 
> looks like Apply is just 1 element (if we do the bailout for conditions, 
> Conditions will be at most 1 element).

So the explanation for both of these things is that this file is going to grow 
a lot, and in particular these sections handling PathMatch etc are going to 
occur again and again, so we need a scalable pattern. I've deliberately limited 
the scope of the first patchset to only support one condition, one setting to 
apply etc, but you have to imagine there are likely to be 5 conditions and 
maybe 25 settings.

So with that in mind:
 - yes, actually saving the conditions isn't necessary, but we do need to 
evaluate them for side-effects (diagnostics) and it's simpler not to keep track 
of them. Optimizing performance of broken configs is not a big win :-)
 - it's marginally simpler to have only one Apply function, but much simpler if 
separate parts of compile() can push settings independently. Same goes for 
condition in fact...

> Fragment and CompiledFragment is a 1:1 mapping

Yes. I think this is somewhere where the naming is helpful at least :-)

> A Fragment::Condition represents a single condition, so the AND match applies 
> for different Fragments, which is in ConfigProvider?

The AND match applies to the multiple Conditions in a single CompiledFragment. 
Each fragment only considers its own conditions. 
So given:
```
If:
  Platform: win
  PathMatch: [foo1/.*, foo2/.*]

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-30 Thread Haojian Wu via Phabricator via cfe-commits
hokein added inline comments.



Comment at: clang-tools-extra/clangd/CMakeLists.txt:41
+  ConfigCompile.cpp
   ConfigYAML.cpp
   Diagnostics.cpp

sammccall wrote:
> hokein wrote:
> > we have a few config-related files now, I wonder would it make sense to 
> > create a config/ subdir?
> It seems marginal to me - could go either way. We have a small handful (2 
> headers and 3 impl files). There may be 1-2 more, but I'm not sure yet. 
> (config-files-on-disk, and json->fragment).
> So 6-7 files... smaller than the other subdirs, but not tiny.
> 
> There's also the fact that config.h/cpp is not like the others: all features 
> (not just infra) depend on this, it's not in the `config::` namespace etc. So 
> should it really be in the subdir?
> 
> Mind if we hold off on this until at least the scope of D82335 has landed? I 
> think it would be awkward to do this move halfway through this patchset, but 
> fairly easy to do at any point that things are stable.
> it seems marginal to me - could go either way. We have a small handful (2 
> headers and 3 impl files). There may be 1-2 more, but I'm not sure yet. 
> (config-files-on-disk, and json->fragment).
> So 6-7 files... smaller than the other subdirs, but not tiny.

if we end up with 6-7 files, I'd prefer to create a subdirectory, which make 
our source structure a bit tidy, our clangd/ dir already has a lot of source 
files.



> Mind if we hold off on this until at least the scope of D82335 has landed? I 
> think it would be awkward to do this move halfway through this patchset, but 
> fairly easy to do at any point that things are stable.

sure, I didn't mean we should do this in this patch.



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:65
+if (F.HasUnrecognizedCondition)
+  Conditions.push_back([&](const Params &) { return false; });
+

I think if this case happened, we might just bail out directly -- adding the 
regex conditions seems unnecessary, we never execute them on Line 127 (but we 
might still need computing the regex for diagnostics).

and I'm not sure the motivation of using `vector` for `Conditions` and `Apply`, 
it looks like Apply is just 1 element (if we do the bailout for conditions, 
`Conditions` will be at most 1 element).

I feel like using a single unique_function for `Conditions` and `Apply` might 
be a better fit with `Fragment`. There is a comment in `ConfigFragment.h`:

>  Each separate condition must match (combined with AND).
>  When one condition has multiple values, any may match (combined with OR).
 
so my reading is that:

- `Fragment` and `CompiledFragment` is a 1:1 mapping
- A `Fragment::Condition` represents a single condition, so the AND match 
applies for different `Fragment`s, which is in ConfigProvider?
- A single condition can have multiple values (`PathMatch`), we use OR match

Is that correct?



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:84
+  void compile(Fragment::CompileFlagsBlock &) {
+if (!F.Add.empty()) {
+  std::vector Add;

nit: use early return.



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:96
+
+  constexpr static auto Error = llvm::SourceMgr::DK_Error;
+  void diag(llvm::SourceMgr::DiagKind Kind, llvm::StringRef Message,

sammccall wrote:
> hokein wrote:
> > this member seems unused.
> it's used on line 52 `diag(Error, ...)`.
> 
> I wouldn't bother extracting it except there should be a bunch more places 
> where errors are emitted, and keeping them readable is worthwhile IME.
Ah, I see. I must have missed that.

btw, could you spell out the auto type for `Error`? I believe it is `DiagKind`, 
and it would be more clear that it is used in the first parameter of `diag` 
(given that they are close).



Comment at: clang-tools-extra/clangd/ConfigProvider.h:32
+/// Describes the context used to evaluate configuration fragments.
+struct Params {
+  /// Absolute path to file we're targeting. Unix slashes.

sammccall wrote:
> hokein wrote:
> > nit: I find the name `Params` is too general, it implies less information. 
> > When I read the code using `Params`, I have to go-to-definition to see what 
> > it means. 
> > 
> > if we only have a single member in this struct, maybe just use it directly.
> Agree with the vague name.
> In most contexts, the name is `config::Params` which is at least a bit more 
> specific.
> 
> > if we only have a single member in this struct, maybe just use it directly.
> 
> I don't like this, though - there are likely to be more things here in the 
> future (e.g. platform), and passing around a string is much less clear as to 
> intent. So any idea about a name for this struct?
> 
> What about `config::Environment` or `config::Env`? still too vague?
not better idea. `config::Env` doesn't seem to be much better... and the 
signature `apply(const Params , Config )` seems neat. 

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-29 Thread Sam McCall via Phabricator via cfe-commits
sammccall marked 10 inline comments as done.
sammccall added inline comments.



Comment at: clang-tools-extra/clangd/CMakeLists.txt:41
+  ConfigCompile.cpp
   ConfigYAML.cpp
   Diagnostics.cpp

hokein wrote:
> we have a few config-related files now, I wonder would it make sense to 
> create a config/ subdir?
It seems marginal to me - could go either way. We have a small handful (2 
headers and 3 impl files). There may be 1-2 more, but I'm not sure yet. 
(config-files-on-disk, and json->fragment).
So 6-7 files... smaller than the other subdirs, but not tiny.

There's also the fact that config.h/cpp is not like the others: all features 
(not just infra) depend on this, it's not in the `config::` namespace etc. So 
should it really be in the subdir?

Mind if we hold off on this until at least the scope of D82335 has landed? I 
think it would be awkward to do this move halfway through this patchset, but 
fairly easy to do at any point that things are stable.



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:96
+
+  constexpr static auto Error = llvm::SourceMgr::DK_Error;
+  void diag(llvm::SourceMgr::DiagKind Kind, llvm::StringRef Message,

hokein wrote:
> this member seems unused.
it's used on line 52 `diag(Error, ...)`.

I wouldn't bother extracting it except there should be a bunch more places 
where errors are emitted, and keeping them readable is worthwhile IME.



Comment at: clang-tools-extra/clangd/ConfigProvider.h:32
+/// Describes the context used to evaluate configuration fragments.
+struct Params {
+  /// Absolute path to file we're targeting. Unix slashes.

hokein wrote:
> nit: I find the name `Params` is too general, it implies less information. 
> When I read the code using `Params`, I have to go-to-definition to see what 
> it means. 
> 
> if we only have a single member in this struct, maybe just use it directly.
Agree with the vague name.
In most contexts, the name is `config::Params` which is at least a bit more 
specific.

> if we only have a single member in this struct, maybe just use it directly.

I don't like this, though - there are likely to be more things here in the 
future (e.g. platform), and passing around a string is much less clear as to 
intent. So any idea about a name for this struct?

What about `config::Environment` or `config::Env`? still too vague?



Comment at: clang-tools-extra/clangd/ConfigProvider.h:40
+///
+/// Fragments are compiled by Providers when first loaded, and cached for 
reuse.
+/// Like a compiled program, this is good for performance and also encourages

hokein wrote:
> I might miss the context, where is the provider, not added yet? 
Right, D82335 has a rought draft of everything together (it's called 
ConfigProvider in that patch).

I think it's less churn-y to add the documentation as the classes are added, 
even if it means temporarily referring to something that doesn't exist yet.



Comment at: clang-tools-extra/clangd/ConfigProvider.h:47
+  /// This always produces a usable compiled fragment (errors are recovered).
+  explicit CompiledFragment(Fragment, DiagnosticCallback);
+

hokein wrote:
> Would it be more nature to have a function `compile`(or so) to do the actual 
> compile (Fragment -> CompiledFragment) rather than doing it in a constructor?
I'm not sure. The idea that the input/result is Fragment/CompiledFragment, and 
that the compilation cannot fail, suggests to me that a constructor is OK here.
On the other hand, the DiangosticCallback param (which we don't keep a 
reference to) is a bit weird.

So I don't really feel strongly about it one way or the other, happy to change 
it if you do think it would be significantly better.



Comment at: clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp:40
+TEST_F(ConfigCompileTests, Condition) {
+  // No condition.
+  Frag.CompileFlags.Add.emplace_back("X");

hokein wrote:
> my first impression was that each `// XXX` is a separated test, but it turns 
> out not, the tests here seem to be stateful -- `Frag` keeps being modified. I 
> think it is intentional, but it is hard for readers to track given that the 
> code is long.
Agree. Made this test reset fragment state after each group of assertions.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612



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


[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-29 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 274237.
sammccall marked 3 inline comments as done.
sammccall added a comment.

Address some review comments


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigProvider.h
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigTesting.h
  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
@@ -8,14 +8,13 @@
 
 #include "Annotations.h"
 #include "ConfigFragment.h"
-#include "Matchers.h"
+#include "ConfigTesting.h"
 #include "Protocol.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "gtest/internal/gtest-internal.h"
 
 namespace clang {
 namespace clangd {
@@ -36,55 +35,6 @@
   return false;
 }
 
-Position toPosition(llvm::SMLoc L, const llvm::SourceMgr ) {
-  auto LineCol = SM.getLineAndColumn(L);
-  Position P;
-  P.line = LineCol.first - 1;
-  P.character = LineCol.second - 1;
-  return P;
-}
-
-Range toRange(llvm::SMRange R, const llvm::SourceMgr ) {
-  return Range{toPosition(R.Start, SM), toPosition(R.End, SM)};
-}
-
-struct CapturedDiags {
-  std::function callback() {
-return [this](const llvm::SMDiagnostic ) {
-  Diagnostics.emplace_back();
-  Diag  = Diagnostics.back();
-  Out.Message = D.getMessage().str();
-  Out.Kind = D.getKind();
-  Out.Pos.line = D.getLineNo() - 1;
-  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
-  if (!D.getRanges().empty()) {
-const auto  = D.getRanges().front();
-Out.Rng.emplace();
-Out.Rng->start.line = Out.Rng->end.line = Out.Pos.line;
-Out.Rng->start.character = R.first;
-Out.Rng->end.character = R.second;
-  }
-};
-  }
-  struct Diag {
-std::string Message;
-llvm::SourceMgr::DiagKind Kind;
-Position Pos;
-llvm::Optional Rng;
-
-friend void PrintTo(const Diag , std::ostream *OS) {
-  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
-  << D.Message << "@" << llvm::to_string(D.Pos);
-}
-  };
-  std::vector Diagnostics;
-};
-
-MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }
-MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }
-MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }
-MATCHER_P(DiagRange, R, "") { return arg.Rng == R; }
-
 TEST(ParseYAML, SyntacticForms) {
   CapturedDiags Diags;
   const char *YAML = R"yaml(
Index: clang-tools-extra/clangd/unittests/ConfigTesting.h
===
--- /dev/null
+++ clang-tools-extra/clangd/unittests/ConfigTesting.h
@@ -0,0 +1,77 @@
+//===-- ConfigTesting.h - Helpers for configuration tests ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+
+#include "Protocol.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gmock/gmock.h"
+#include 
+
+namespace clang {
+namespace clangd {
+namespace config {
+
+// Provides a DiagnosticsCallback that records diganostics.
+// Unlike just pushing them into a vector, underlying storage need not survive.
+struct CapturedDiags {
+  std::function callback() {
+return [this](const llvm::SMDiagnostic ) {
+  Diagnostics.emplace_back();
+  Diag  = Diagnostics.back();
+  Out.Message = D.getMessage().str();
+  Out.Kind = D.getKind();
+  Out.Pos.line = D.getLineNo() - 1;
+  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
+  if (!D.getRanges().empty()) {
+const auto  = D.getRanges().front();
+Out.Range.emplace();
+Out.Range->start.line = Out.Range->end.line = Out.Pos.line;
+Out.Range->start.character = R.first;
+Out.Range->end.character = R.second;
+  }
+};
+  }
+  struct Diag {
+std::string Message;
+llvm::SourceMgr::DiagKind Kind;
+Position Pos;
+llvm::Optional Range;
+
+friend void PrintTo(const Diag , std::ostream *OS) {
+  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
+

[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-29 Thread Haojian Wu via Phabricator via cfe-commits
hokein added inline comments.



Comment at: clang-tools-extra/clangd/CMakeLists.txt:41
+  ConfigCompile.cpp
   ConfigYAML.cpp
   Diagnostics.cpp

we have a few config-related files now, I wonder would it make sense to create 
a config/ subdir?



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:47
+
+  std::string RegexError = "";
+  llvm::Optional compileRegex(const Located ) {

this is only used in method `compileRegex`, looks like can be moved to a local 
variable? is there life-time issue?



Comment at: clang-tools-extra/clangd/ConfigCompile.cpp:96
+
+  constexpr static auto Error = llvm::SourceMgr::DK_Error;
+  void diag(llvm::SourceMgr::DiagKind Kind, llvm::StringRef Message,

this member seems unused.



Comment at: clang-tools-extra/clangd/ConfigProvider.h:32
+/// Describes the context used to evaluate configuration fragments.
+struct Params {
+  /// Absolute path to file we're targeting. Unix slashes.

nit: I find the name `Params` is too general, it implies less information. When 
I read the code using `Params`, I have to go-to-definition to see what it 
means. 

if we only have a single member in this struct, maybe just use it directly.



Comment at: clang-tools-extra/clangd/ConfigProvider.h:40
+///
+/// Fragments are compiled by Providers when first loaded, and cached for 
reuse.
+/// Like a compiled program, this is good for performance and also encourages

I might miss the context, where is the provider, not added yet? 



Comment at: clang-tools-extra/clangd/ConfigProvider.h:47
+  /// This always produces a usable compiled fragment (errors are recovered).
+  explicit CompiledFragment(Fragment, DiagnosticCallback);
+

Would it be more nature to have a function `compile`(or so) to do the actual 
compile (Fragment -> CompiledFragment) rather than doing it in a constructor?



Comment at: clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp:40
+TEST_F(ConfigCompileTests, Condition) {
+  // No condition.
+  Frag.CompileFlags.Add.emplace_back("X");

my first impression was that each `// XXX` is a separated test, but it turns 
out not, the tests here seem to be stateful -- `Frag` keeps being modified. I 
think it is intentional, but it is hard for readers to track given that the 
code is long.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D82612



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


[PATCH] D82612: [clangd] Config: compile Fragment -> CompiledFragment -> Config

2020-06-25 Thread Sam McCall via Phabricator via cfe-commits
sammccall created this revision.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous, 
MaskRay, ilya-biryukov, mgorny.
Herald added a project: clang.
sammccall added a parent revision: D82606: [clangd] Config: config struct 
propagated through Context.
sammccall edited the summary of this revision.
sammccall added a reviewer: hokein.

https://reviews.llvm.org/D82335 shows how this fits in with related config work.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D82612

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigProvider.h
  clang-tools-extra/clangd/unittests/CMakeLists.txt
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigTesting.h
  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
@@ -8,14 +8,13 @@
 
 #include "Annotations.h"
 #include "ConfigFragment.h"
-#include "Matchers.h"
+#include "ConfigTesting.h"
 #include "Protocol.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "gtest/internal/gtest-internal.h"
 
 namespace clang {
 namespace clangd {
@@ -36,55 +35,6 @@
   return false;
 }
 
-Position toPosition(llvm::SMLoc L, const llvm::SourceMgr ) {
-  auto LineCol = SM.getLineAndColumn(L);
-  Position P;
-  P.line = LineCol.first - 1;
-  P.character = LineCol.second - 1;
-  return P;
-}
-
-Range toRange(llvm::SMRange R, const llvm::SourceMgr ) {
-  return Range{toPosition(R.Start, SM), toPosition(R.End, SM)};
-}
-
-struct CapturedDiags {
-  std::function callback() {
-return [this](const llvm::SMDiagnostic ) {
-  Diagnostics.emplace_back();
-  Diag  = Diagnostics.back();
-  Out.Message = D.getMessage().str();
-  Out.Kind = D.getKind();
-  Out.Pos.line = D.getLineNo() - 1;
-  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
-  if (!D.getRanges().empty()) {
-const auto  = D.getRanges().front();
-Out.Range.emplace();
-Out.Range->start.line = Out.Range->end.line = Out.Pos.line;
-Out.Range->start.character = R.first;
-Out.Range->end.character = R.second;
-  }
-};
-  }
-  struct Diag {
-std::string Message;
-llvm::SourceMgr::DiagKind Kind;
-Position Pos;
-llvm::Optional Range;
-
-friend void PrintTo(const Diag , std::ostream *OS) {
-  *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")
-  << D.Message << "@" << llvm::to_string(D.Pos);
-}
-  };
-  std::vector Diagnostics;
-};
-
-MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }
-MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }
-MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }
-MATCHER_P(DiagRange, R, "") { return arg.Range == R; }
-
 TEST(ParseYAML, SyntacticForms) {
   CapturedDiags Diags;
   const char *YAML = R"yaml(
Index: clang-tools-extra/clangd/unittests/ConfigTesting.h
===
--- /dev/null
+++ clang-tools-extra/clangd/unittests/ConfigTesting.h
@@ -0,0 +1,77 @@
+//===-- ConfigTesting.h - Helpers for configuration tests ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H
+
+#include "Protocol.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gmock/gmock.h"
+#include 
+
+namespace clang {
+namespace clangd {
+namespace config {
+
+// Provides a DiagnosticsCallback that records diganostics.
+// Unlike just pushing them into a vector, underlying storage need not survive.
+struct CapturedDiags {
+  std::function callback() {
+return [this](const llvm::SMDiagnostic ) {
+  Diagnostics.emplace_back();
+  Diag  = Diagnostics.back();
+  Out.Message = D.getMessage().str();
+  Out.Kind = D.getKind();
+  Out.Pos.line = D.getLineNo() - 1;
+  Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr?
+  if (!D.getRanges().empty()) {
+const auto  = D.getRanges().front();
+Out.Range.emplace();
+Out.Range->start.line = Out.Range->end.line = Out.Pos.line;
+Out.Range->start.character = R.first;
+Out.Range->end.character = R.second;
+  }
+};
+  }
+  struct Diag {
+