VitaNuo created this revision.
VitaNuo added a reviewer: hokein.
Herald added subscribers: kadircet, arphaman.
Herald added a project: All.
VitaNuo requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D144976

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Hover.h
  clang-tools-extra/clangd/unittests/HoverTests.cpp

Index: clang-tools-extra/clangd/unittests/HoverTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -28,6 +28,10 @@
 
 using PassMode = HoverInfo::PassType::PassMode;
 
+std::string guard(llvm::StringRef Code) {
+  return "#pragma once\n" + Code.str();
+}
+
 TEST(Hover, Structured) {
   struct {
     const char *const Code;
@@ -2882,6 +2886,160 @@
   }
 }
 
+TEST(Hover, Providers) {
+  struct {
+    Annotations Code;
+    llvm::StringLiteral FooHeader;
+    llvm::StringLiteral BarHeader;
+    llvm::StringLiteral FooBarHeader;
+    const std::function<void(HoverInfo &)> ExpectedBuilder;
+  } Cases[] = {{Annotations(
+                    R"cpp(
+                      #include "foo.h"
+                      int F = [[fo^o]]();
+                    )cpp"),
+                "int foo();", "", "",
+                [](HoverInfo &HI) { HI.ProviderInfo = {"foo.h"}; }},
+               {Annotations(
+                    R"cpp(
+                      #include "bar.h"
+                      #include "foo.h"
+                      int F = [[f^oo]]();
+                    )cpp"),
+                "int foo();", "int foo();", "",
+                [](HoverInfo &HI) {
+                  HI.ProviderInfo = {"bar.h", "foo.h"};
+                }},
+               {Annotations(
+                    R"cpp(
+                  #include "foo.h"
+                  int F = [[^foo]]();
+                )cpp"),
+                "#include \"bar.h\"", "int foo();", "",
+                [](HoverInfo &HI) { HI.ProviderInfo = {}; }},
+               {Annotations(
+                    R"cpp(
+                  #include "foo.h"
+                  int F = [[^foo]]();
+                )cpp"),
+                R"cpp(
+                  #include "bar.h"
+                  int foo();
+                )cpp",
+                "int foo();", "",
+                [](HoverInfo &HI) { HI.ProviderInfo = {"foo.h"}; }},
+               {Annotations(
+                    R"cpp(
+                  #include "bar.h"
+                  #include "foo.h"
+                  #include "foobar.h"
+                  int F = [[^foo]]();
+                )cpp"),
+                "int foo();", "int foo();", "#include \"bar.h\"",
+                [](HoverInfo &HI) {
+                  HI.ProviderInfo = {"bar.h", "foo.h"};
+                }},
+               {Annotations(
+                    R"cpp(
+                  #include "bar.h"
+                  #include "foo.h"
+                  #include "foobar.h"
+                  int F = [[foo^]]();
+                )cpp"),
+                "int foo();", "#include \"foo.h\"", "#include \"foo.h\"",
+                [](HoverInfo &HI) { HI.ProviderInfo = {"foo.h"}; }},
+               {Annotations(
+                    R"cpp(
+                  #include "foo.h"
+                  int F = [[^MACRO]];
+                )cpp"),
+                R"cpp(
+                  #include "bar.h"
+                  #define MACRO 5
+                )cpp",
+                "#define MACRO 10", "",
+                [](HoverInfo &HI) { HI.ProviderInfo = {"foo.h"}; }},
+               {Annotations(
+                    R"cpp(
+                  #include "foo.h"
+                  int F = [[M^ACRO]](5);
+                )cpp"),
+                R"cpp(
+                  #include "bar.h"
+                  #define MACRO(X) (X+1)
+                )cpp",
+                "#define MACRO 10", "",
+                [](HoverInfo &HI) { HI.ProviderInfo = {"foo.h"}; }},
+               {Annotations(
+                    R"cpp(
+                  #include "foo.h"
+                  int F = [[MA^CRO]](5);
+                )cpp"),
+                R"cpp(
+                  #include "bar.h"
+                  #include "foobar.h"
+                  #define MACRO(X) MACRO_HELPER(X)
+                )cpp",
+                "#define MACRO_HELPER(X) (X+1)", "#define MACRO(X) (X+10)",
+                [](HoverInfo &HI) { HI.ProviderInfo = {"foo.h"}; }},
+               {Annotations(
+                    R"cpp(
+                  #include "foo.h"
+                  int F = [[MAC^RO]](5);
+                )cpp"),
+                R"cpp(
+                  #include "bar.h"
+                  #include "foobar.h"
+                )cpp",
+                "#define MACRO(X) (X+1)", "#define MACRO(X) (X+2)",
+                [](HoverInfo &HI) { HI.ProviderInfo = {}; }},
+               {Annotations(
+                    R"cpp(
+                  #include "foo.h"
+                  #include "bar.h"    
+                  int F = [[MACR^O]];
+                )cpp"),
+                "#define MACRO 5", "#define MACRO 10", "#define MACRO 15",
+                [](HoverInfo &HI) { HI.ProviderInfo = {"bar.h"}; }}};
+
+  for (const auto &Case : Cases) {
+    SCOPED_TRACE(Case.Code.code());
+
+    TestTU TU;
+    TU.Filename = "foo.cpp";
+    TU.Code = Case.Code.code();
+    TU.AdditionalFiles["foo.h"] = guard(Case.FooHeader);
+    TU.AdditionalFiles["bar.h"] = guard(Case.BarHeader);
+    TU.AdditionalFiles["foobar.h"] = guard(Case.FooBarHeader);
+
+    auto AST = TU.build();
+    auto H = getHover(AST, Case.Code.point(), format::getLLVMStyle(), nullptr);
+    ASSERT_TRUE(H);
+    HoverInfo Expected;
+    Case.ExpectedBuilder(Expected);
+    SCOPED_TRACE(H->present().asMarkdown());
+    EXPECT_EQ(H->ProviderInfo, Expected.ProviderInfo);
+  }
+}
+
+TEST(Hover, ParseProviderInfo) {
+  HoverInfo HIFoo;
+  HIFoo.Name = "foo";
+  HIFoo.ProviderInfo = {"foo.h"};
+
+  HoverInfo HIFooBar;
+  HIFooBar.Name = "foo";
+  HIFooBar.ProviderInfo = {"bar.h", "foo.h"};
+  struct Case {
+    HoverInfo HI;
+    llvm::StringRef ExpectedMarkdown;
+  } Cases[] = {{HIFoo, "### `foo`  \n\n---\nProvided by `foo.h`"},
+               {HIFooBar, "### `foo`  \n\n---\nProvided by `bar.h`, `foo.h`"}};
+
+  for (const auto &Case : Cases)
+    EXPECT_EQ(Case.HI.present().asMarkdown(), Case.ExpectedMarkdown);
+}
+
 TEST(Hover, DocsFromIndex) {
   Annotations T(R"cpp(
   template <typename T> class X {};
Index: clang-tools-extra/clangd/Hover.h
===================================================================
--- clang-tools-extra/clangd/Hover.h
+++ clang-tools-extra/clangd/Hover.h
@@ -67,6 +67,8 @@
   std::string LocalScope;
   /// Name of the symbol, does not contain any "::".
   std::string Name;
+  /// Headers providing the symbol (directly included in the main file).
+  std::optional<std::vector<std::string>> ProviderInfo;
   std::optional<Range> SymRange;
   index::SymbolKind Kind = index::SymbolKind::Unknown;
   std::string Documentation;
Index: clang-tools-extra/clangd/Hover.cpp
===================================================================
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -15,8 +15,12 @@
 #include "ParsedAST.h"
 #include "Selection.h"
 #include "SourceCode.h"
+#include "clang-include-cleaner/Analysis.h"
+#include "clang-include-cleaner/Types.h"
 #include "index/SymbolCollector.h"
+#include "support/Logger.h"
 #include "support/Markup.h"
+#include "support/Trace.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/ASTTypeTraits.h"
@@ -1084,6 +1088,100 @@
   return Candidates.front();
 }
 
+// FIXME(bakalova): Remove after merging https://reviews.llvm.org/D143496
+std::vector<include_cleaner::SymbolReference>
+collectMacroReferences(ParsedAST &AST) {
+  const auto &SM = AST.getSourceManager();
+  //  FIXME: !!this is a hacky way to collect macro references.
+  std::vector<include_cleaner::SymbolReference> Macros;
+  auto &PP = AST.getPreprocessor();
+  for (const syntax::Token &Tok :
+       AST.getTokens().spelledTokens(SM.getMainFileID())) {
+    auto Macro = locateMacroAt(Tok, PP);
+    if (!Macro)
+      continue;
+    if (auto DefLoc = Macro->Info->getDefinitionLoc(); DefLoc.isValid())
+      Macros.push_back(
+          {Tok.location(),
+           include_cleaner::Macro{/*Name=*/PP.getIdentifierInfo(Tok.text(SM)),
+                                  DefLoc},
+           include_cleaner::RefType::Explicit});
+  }
+  return Macros;
+}
+
+void maybeAddSymbolProviders(ParsedAST &AST, HoverInfo &HI,
+                             SourceLocation CurLoc) {
+  trace::Span Tracer("Hover::maybeAddSymbolProviders");
+  include_cleaner::walkUsed(
+      AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
+      AST.getPragmaIncludes(), AST.getSourceManager(),
+      [&](const include_cleaner::SymbolReference &Ref,
+          llvm::ArrayRef<include_cleaner::Header> Providers) {
+        const SourceManager &SM = AST.getSourceManager();
+        if (!Ref.RefLocation.isFileID() ||
+            !SM.isWrittenInMainFile(SM.getSpellingLoc(Ref.RefLocation)))
+          // Ignore locations within macro expansions or not in the main file
+          return;
+
+        if (Ref.RT != include_cleaner::RefType::Explicit)
+          // Ignore non-explicit references (e.g. variable names).
+          return;
+
+        const syntax::Token *Tok =
+            AST.getTokens().spelledTokenAt(Ref.RefLocation);
+        if (Tok == nullptr) {
+          elog("Can't find spelled token at the symbol reference location.");
+          return;
+        }
+        const SourceLocation &RefEndLocation = Tok->endLocation();
+
+        // Check that the user is hovering over the current symbol reference.
+        if (CurLoc < Ref.RefLocation || CurLoc > RefEndLocation) {
+          return;
+        }
+        if (Providers.empty())
+          return;
+
+        std::vector<std::string> DirectlyIncludedProviders;
+        std::vector<Inclusion> MainFileIncludes =
+            AST.getIncludeStructure().MainFileIncludes;
+        for (const Inclusion &Inc : MainFileIncludes) {
+          for (const include_cleaner::Header &Provider : Providers) {
+            if (Provider.kind() == include_cleaner::Header::Physical &&
+                SM.getFileEntryForID(SM.getMainFileID()) ==
+                    Provider.physical()) {
+              // Do not report main file as provider.
+              continue;
+            }
+
+            llvm::StringRef WrittenRef =
+                llvm::StringRef(Inc.Written).trim("\"<>");
+
+            switch (Provider.kind()) {
+            case include_cleaner::Header::Physical:
+              if (Provider.physical()->tryGetRealPathName() == Inc.Resolved)
+                DirectlyIncludedProviders.push_back(WrittenRef.str());
+              break;
+            case include_cleaner::Header::Standard:
+              if (Provider.standard().name() == Inc.Written)
+                DirectlyIncludedProviders.push_back(WrittenRef.str());
+              break;
+            case include_cleaner::Header::Verbatim:
+              if (Provider.verbatim() == Inc.Written)
+                DirectlyIncludedProviders.push_back(WrittenRef.str());
+            }
+          }
+        }
+
+        if (DirectlyIncludedProviders.empty())
+          return;
+
+        HI.ProviderInfo = std::optional<std::vector<std::string>>{
+            std::move(DirectlyIncludedProviders)};
+      });
+}
+
 } // namespace
 
 std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
@@ -1131,6 +1229,8 @@
       HighlightRange = Tok.range(SM).toCharRange(SM);
       if (auto M = locateMacroAt(Tok, AST.getPreprocessor())) {
         HI = getHoverContents(*M, Tok, AST);
+        if (HI) 
+          maybeAddSymbolProviders(AST, *HI, *CurLoc);
         break;
       }
     } else if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {
@@ -1165,9 +1265,10 @@
         if (DeclToUse == N->ASTNode.get<Decl>())
           addLayoutInfo(*DeclToUse, *HI);
         // Look for a close enclosing expression to show the value of.
-        if (!HI->Value)
+        if (HI && !HI->Value)
           HI->Value = printExprValue(N, AST.getASTContext());
         maybeAddCalleeArgInfo(N, *HI, PP);
+        maybeAddSymbolProviders(AST, *HI, *CurLoc);
       } else if (const Expr *E = N->ASTNode.get<Expr>()) {
         HI = getHoverContents(N, E, AST, PP, Index);
       } else if (const Attr *A = N->ASTNode.get<Attr>()) {
@@ -1231,6 +1332,20 @@
         llvm::to_string(*ReturnType));
   }
 
+  if (ProviderInfo) {
+    Output.addRuler();
+    markup::Paragraph &P = Output.addParagraph();
+    P.appendText("Provided by ");
+    P.appendSpace();
+    for (unsigned I = 0; I < (*ProviderInfo).size() - 1; I++) {
+      P.appendCode((*ProviderInfo)[I]);
+      P.appendText(",");
+      P.appendSpace();
+    }
+    P.appendCode((*ProviderInfo).back());
+    Output.addRuler();
+  }
+
   if (Parameters && !Parameters->empty()) {
     Output.addParagraph().appendText("Parameters: ");
     markup::BulletList &L = Output.addBulletList();
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to