kadircet updated this revision to Diff 301280.
kadircet marked 9 inline comments as done.
kadircet added a comment.

- Drop PathSpec, deduce it from emptyness of FragmentDirectory.
- Make tests more homogenous.
- Add path cannonicalization to compile stage.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D90270

Files:
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigFragment.h
  clang-tools-extra/clangd/ConfigProvider.cpp
  clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
  clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp

Index: clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp
@@ -10,6 +10,7 @@
 #include "ConfigProvider.h"
 #include "ConfigTesting.h"
 #include "TestFS.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/SourceMgr.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -187,6 +188,36 @@
   EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
 }
 
+TEST(ProviderTest, SourceInfo) {
+  MockFS FS;
+
+  FS.Files["baz/foo.yaml"] = R"yaml(
+If:
+  PathMatch: .*
+  PathExclude: bar.h
+CompileFlags:
+  Add: bar
+)yaml";
+  const auto BarPath = testPath("baz/bar.h", llvm::sys::path::Style::posix);
+  CapturedDiags Diags;
+  Params Bar;
+  Bar.Path = BarPath;
+
+  // This should be an absolute match/exclude hence baz/bar.h should not be
+  // excluded.
+  auto P = Provider::fromYAMLFile(testPath("baz/foo.yaml"), FS);
+  auto Cfg = P->getConfig(Bar, Diags.callback());
+  ASSERT_THAT(Diags.Diagnostics, IsEmpty());
+  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar"));
+  Diags.Diagnostics.clear();
+
+  // This should be a relative match/exclude hence baz/bar.h should be excluded.
+  P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
+  Cfg = P->getConfig(Bar, Diags.callback());
+  ASSERT_THAT(Diags.Diagnostics, IsEmpty());
+  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
+  Diags.Diagnostics.clear();
+}
 } // namespace
 } // namespace config
 } // namespace clangd
Index: clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
+++ clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
@@ -9,8 +9,12 @@
 #include "Config.h"
 #include "ConfigFragment.h"
 #include "ConfigTesting.h"
+#include "TestFS.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Path.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include <string>
 
 namespace clang {
 namespace clangd {
@@ -116,6 +120,62 @@
           "Invalid Background value 'Foo'. Valid values are Build, Skip.")));
 }
 
+TEST_F(ConfigCompileTests, PathSpecMatch) {
+  auto BarPath = llvm::sys::path::convert_to_slash(testPath("foo/bar.h"));
+  Parm.Path = BarPath;
+
+  struct {
+    std::string Directory;
+    std::string PathSpec;
+    bool ShouldMatch;
+  } Cases[] = {
+      {
+          // Absolute path matches.
+          "",
+          llvm::sys::path::convert_to_slash(testPath("foo/bar.h")),
+          true,
+      },
+      {
+          // Absolute path fails.
+          "",
+          llvm::sys::path::convert_to_slash(testPath("bar/bar.h")),
+          false,
+      },
+      {
+          // Relative should fail to match as /foo/bar.h doesn't reside under
+          // /baz/.
+          testPath("baz"),
+          "bar\\.h",
+          false,
+      },
+      {
+          // Relative should pass with /foo as directory.
+          testPath("foo"),
+          "bar\\.h",
+          true,
+      },
+  };
+
+  // PathMatch
+  for (const auto &Case : Cases) {
+    Frag = {};
+    Frag.If.PathMatch.emplace_back(Case.PathSpec);
+    Frag.Source.Directory = Case.Directory;
+    EXPECT_EQ(compileAndApply(), Case.ShouldMatch);
+    ASSERT_THAT(Diags.Diagnostics, IsEmpty());
+  }
+
+  // PathEclude
+  for (const auto &Case : Cases) {
+    SCOPED_TRACE(Case.Directory);
+    SCOPED_TRACE(Case.PathSpec);
+    Frag = {};
+    Frag.If.PathExclude.emplace_back(Case.PathSpec);
+    Frag.Source.Directory = Case.Directory;
+    EXPECT_NE(compileAndApply(), Case.ShouldMatch);
+    ASSERT_THAT(Diags.Diagnostics, IsEmpty());
+  }
+}
 } // namespace
 } // namespace config
 } // namespace clangd
Index: clang-tools-extra/clangd/ConfigProvider.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigProvider.cpp
+++ clang-tools-extra/clangd/ConfigProvider.cpp
@@ -13,9 +13,11 @@
 #include "support/Trace.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Path.h"
 #include <chrono>
 #include <mutex>
+#include <string>
 
 namespace clang {
 namespace clangd {
@@ -51,14 +53,18 @@
 
     // Finally parse and compile the actual fragments.
     for (auto &Fragment :
-         Fragment::parseYAML(Buf->get()->getBuffer(), Path, DC))
+         Fragment::parseYAML(Buf->get()->getBuffer(), Path, DC)) {
+      Fragment.Source.Directory = Dir;
       CachedValue.push_back(std::move(Fragment).compile(DC));
+    }
   }
 
 public:
   // Must be set before the cache is used. Not a constructor param to allow
   // computing ancestor-relative paths to be deferred.
   std::string Path;
+  // Directory associated with this fragment.
+  std::string Dir;
 
   // Retrieves up-to-date config fragments from disk.
   // A cached result may be reused if the mtime and size are unchanged.
@@ -170,6 +176,7 @@
             llvm::SmallString<256> ConfigPath = Ancestor;
             path::append(ConfigPath, RelPath);
             R.first->second.Path = ConfigPath.str().str();
+            R.first->second.Dir = Ancestor.str();
           }
           Caches.push_back(&R.first->second);
         }
@@ -177,8 +184,9 @@
       // Finally query each individual file.
       // This will take a (per-file) lock for each file that actually exists.
       std::vector<CompiledFragment> Result;
-      for (FileConfigCache *Cache : Caches)
+      for (FileConfigCache *Cache : Caches) {
         Cache->read(FS, DC, P.FreshTime, Result);
+      }
       return Result;
     };
 
Index: clang-tools-extra/clangd/ConfigFragment.h
===================================================================
--- clang-tools-extra/clangd/ConfigFragment.h
+++ clang-tools-extra/clangd/ConfigFragment.h
@@ -91,6 +91,9 @@
     /// The start of the original source for this fragment.
     /// Only valid if SourceManager is set.
     llvm::SMLoc Location;
+    /// Absolute path to directory the fragment is associated with. Relative
+    /// paths mentioned in the fragment are resolved against this.
+    std::string Directory;
   };
   SourceInfo Source;
 
Index: clang-tools-extra/clangd/ConfigCompile.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigCompile.cpp
+++ clang-tools-extra/clangd/ConfigCompile.cpp
@@ -31,9 +31,11 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/SourceMgr.h"
+#include <string>
 
 namespace clang {
 namespace clangd {
@@ -70,6 +72,9 @@
   CompiledFragmentImpl &Out;
   DiagnosticCallback Diagnostic;
   llvm::SourceMgr *SourceMgr;
+  // Directory containing the fragment. Absolute path with forward slashes, with
+  // a trailing slash or empty for user-config fragments.
+  std::string FragmentDirectory;
 
   llvm::Optional<llvm::Regex> compileRegex(const Located<std::string> &Text) {
     std::string Anchored = "^(" + *Text + ")$";
@@ -129,6 +134,11 @@
   }
 
   void compile(Fragment &&F) {
+    if (!F.Source.Directory.empty()) {
+      FragmentDirectory = llvm::sys::path::convert_to_slash(F.Source.Directory);
+      if (FragmentDirectory.back() != '/')
+        FragmentDirectory += '/';
+    }
     compile(std::move(F.If));
     compile(std::move(F.CompileFlags));
     compile(std::move(F.Index));
@@ -145,11 +155,19 @@
     }
     if (!PathMatch->empty()) {
       Out.Conditions.push_back(
-          [PathMatch(std::move(PathMatch))](const Params &P) {
+          [PathMatch(std::move(PathMatch)),
+           FragmentDir(FragmentDirectory)](const Params &P) {
             if (P.Path.empty())
               return false;
+            llvm::StringRef Path = P.Path;
+            // Ignore the file if it is not nested under Fragment and strip the
+            // prefix.
+            if (!Path.consume_front(FragmentDir))
+              return false;
+            if (!FragmentDir.empty())
+              Path.consume_front("/");
             return llvm::any_of(*PathMatch, [&](const llvm::Regex &RE) {
-              return RE.match(P.Path);
+              return RE.match(Path);
             });
           });
     }
@@ -161,11 +179,19 @@
     }
     if (!PathExclude->empty()) {
       Out.Conditions.push_back(
-          [PathExclude(std::move(PathExclude))](const Params &P) {
+          [PathExclude(std::move(PathExclude)),
+           FragmentDir(FragmentDirectory)](const Params &P) {
             if (P.Path.empty())
               return false;
+            llvm::StringRef Path = P.Path;
+            // Ignore the file if it is not nested under Fragment and strip the
+            // prefix.
+            if (!Path.consume_front(FragmentDir))
+              return true;
+            if (!FragmentDir.empty())
+              Path.consume_front("/");
             return llvm::none_of(*PathExclude, [&](const llvm::Regex &RE) {
-              return RE.match(P.Path);
+              return RE.match(Path);
             });
           });
     }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to