hokein created this revision.
hokein added a reviewer: sammccall.
Herald added subscribers: MaskRay, ioeric, jkorous-apple, ilya-biryukov, klimek.

Calculating the include path from absolute file path does not always
work for all build system, e.g. bazel uses symlink as the build working
directory. The absolute file path from editor and clang is diverged from
each other. We need to address it properly in build sysmtem integration.

This patch worksarounds the issue by providing a hook in URI which allows
clients to provide their customized include path.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45426

Files:
  clangd/ClangdServer.cpp
  clangd/URI.cpp
  clangd/URI.h
  unittests/clangd/ClangdTests.cpp
  unittests/clangd/TestScheme.h
  unittests/clangd/URITests.cpp


Index: unittests/clangd/URITests.cpp
===================================================================
--- unittests/clangd/URITests.cpp
+++ unittests/clangd/URITests.cpp
@@ -47,6 +47,10 @@
     return URI(Scheme, /*Authority=*/"",
                AbsolutePath.substr(Pos + llvm::StringRef(TestRoot).size()));
   }
+
+  llvm::Expected<std::string> getIncludePath(const URI &U) const override {
+    return ("\"" + U.body() + "\"").str();
+  }
 };
 
 const char *TestScheme::Scheme = "unittest";
Index: unittests/clangd/ClangdTests.cpp
===================================================================
--- unittests/clangd/ClangdTests.cpp
+++ unittests/clangd/ClangdTests.cpp
@@ -961,6 +961,10 @@
                        /*Preferred=*/"<Y.h>", "<Y.h>"));
   EXPECT_TRUE(Inserted(OriginalHeader, PreferredHeader, "\"Y.h\""));
   EXPECT_TRUE(Inserted("<y.h>", PreferredHeader, "\"Y.h\""));
+  auto TestURIHeader = URI::create("/abc/test-root/x/y/z.h", "unittest");
+  EXPECT_TRUE(static_cast<bool>(TestURIHeader));
+  EXPECT_TRUE(Inserted(TestURIHeader->toString(), "", "\"x/y/z.h\""));
+
 
   // Check that includes are sorted.
   const auto Expected = R"cpp(
Index: clangd/URI.h
===================================================================
--- clangd/URI.h
+++ clangd/URI.h
@@ -60,6 +60,14 @@
   static llvm::Expected<std::string> resolve(const URI &U,
                                              llvm::StringRef HintPath = "");
 
+  /// Tries to get the include path of the file corresponding to the URI.
+  /// This allows clients to provide their customized include paths.
+  ///
+  /// If the returned string is non-empty, clangd will use it directly when
+  /// doing include insertion; otherwise we will fall back to the clang to
+  /// calculate the include path from the resolved absolute path.
+  static llvm::Expected<std::string> includePath(const URI &U);
+
   friend bool operator==(const URI &LHS, const URI &RHS) {
     return std::tie(LHS.Scheme, LHS.Authority, LHS.Body) ==
            std::tie(RHS.Scheme, RHS.Authority, RHS.Body);
@@ -94,6 +102,13 @@
 
   virtual llvm::Expected<URI>
   uriFromAbsolutePath(llvm::StringRef AbsolutePath) const = 0;
+
+  /// Returns the include path of the file corresponding to the URI, which can
+  /// be #include directly. See URI::resolveIncludePath for details.
+  virtual llvm::Expected<std::string>
+  getIncludePath(const URI& U) const {
+    return "";  // no customized include path for this scheme.
+  }
 };
 
 /// By default, a "file" scheme is supported where URI paths are always 
absolute
Index: clangd/URI.cpp
===================================================================
--- clangd/URI.cpp
+++ clangd/URI.cpp
@@ -196,5 +196,12 @@
   return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath);
 }
 
+llvm::Expected<std::string> URI::includePath(const URI &Uri) {
+  auto S = findSchemeByName(Uri.Scheme);
+  if (!S)
+    return S.takeError();
+  return S->get()->getIncludePath(Uri);
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -286,6 +286,13 @@
   auto U = URI::parse(Header);
   if (!U)
     return U.takeError();
+
+  auto IncludePath = URI::includePath(*U);
+  if (!IncludePath)
+    return IncludePath.takeError();
+  if (!IncludePath->empty())
+    return HeaderFile{std::move(*IncludePath), /*Verbatim=*/true};
+
   auto Resolved = URI::resolve(*U, HintPath);
   if (!Resolved)
     return Resolved.takeError();


Index: unittests/clangd/URITests.cpp
===================================================================
--- unittests/clangd/URITests.cpp
+++ unittests/clangd/URITests.cpp
@@ -47,6 +47,10 @@
     return URI(Scheme, /*Authority=*/"",
                AbsolutePath.substr(Pos + llvm::StringRef(TestRoot).size()));
   }
+
+  llvm::Expected<std::string> getIncludePath(const URI &U) const override {
+    return ("\"" + U.body() + "\"").str();
+  }
 };
 
 const char *TestScheme::Scheme = "unittest";
Index: unittests/clangd/ClangdTests.cpp
===================================================================
--- unittests/clangd/ClangdTests.cpp
+++ unittests/clangd/ClangdTests.cpp
@@ -961,6 +961,10 @@
                        /*Preferred=*/"<Y.h>", "<Y.h>"));
   EXPECT_TRUE(Inserted(OriginalHeader, PreferredHeader, "\"Y.h\""));
   EXPECT_TRUE(Inserted("<y.h>", PreferredHeader, "\"Y.h\""));
+  auto TestURIHeader = URI::create("/abc/test-root/x/y/z.h", "unittest");
+  EXPECT_TRUE(static_cast<bool>(TestURIHeader));
+  EXPECT_TRUE(Inserted(TestURIHeader->toString(), "", "\"x/y/z.h\""));
+
 
   // Check that includes are sorted.
   const auto Expected = R"cpp(
Index: clangd/URI.h
===================================================================
--- clangd/URI.h
+++ clangd/URI.h
@@ -60,6 +60,14 @@
   static llvm::Expected<std::string> resolve(const URI &U,
                                              llvm::StringRef HintPath = "");
 
+  /// Tries to get the include path of the file corresponding to the URI.
+  /// This allows clients to provide their customized include paths.
+  ///
+  /// If the returned string is non-empty, clangd will use it directly when
+  /// doing include insertion; otherwise we will fall back to the clang to
+  /// calculate the include path from the resolved absolute path.
+  static llvm::Expected<std::string> includePath(const URI &U);
+
   friend bool operator==(const URI &LHS, const URI &RHS) {
     return std::tie(LHS.Scheme, LHS.Authority, LHS.Body) ==
            std::tie(RHS.Scheme, RHS.Authority, RHS.Body);
@@ -94,6 +102,13 @@
 
   virtual llvm::Expected<URI>
   uriFromAbsolutePath(llvm::StringRef AbsolutePath) const = 0;
+
+  /// Returns the include path of the file corresponding to the URI, which can
+  /// be #include directly. See URI::resolveIncludePath for details.
+  virtual llvm::Expected<std::string>
+  getIncludePath(const URI& U) const {
+    return "";  // no customized include path for this scheme.
+  }
 };
 
 /// By default, a "file" scheme is supported where URI paths are always absolute
Index: clangd/URI.cpp
===================================================================
--- clangd/URI.cpp
+++ clangd/URI.cpp
@@ -196,5 +196,12 @@
   return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath);
 }
 
+llvm::Expected<std::string> URI::includePath(const URI &Uri) {
+  auto S = findSchemeByName(Uri.Scheme);
+  if (!S)
+    return S.takeError();
+  return S->get()->getIncludePath(Uri);
+}
+
 } // namespace clangd
 } // namespace clang
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -286,6 +286,13 @@
   auto U = URI::parse(Header);
   if (!U)
     return U.takeError();
+
+  auto IncludePath = URI::includePath(*U);
+  if (!IncludePath)
+    return IncludePath.takeError();
+  if (!IncludePath->empty())
+    return HeaderFile{std::move(*IncludePath), /*Verbatim=*/true};
+
   auto Resolved = URI::resolve(*U, HintPath);
   if (!Resolved)
     return Resolved.takeError();
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to