VitaNuo updated this revision to Diff 507326.
VitaNuo marked 10 inline comments as done.
VitaNuo added a comment.

Address review comments.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D146244

Files:
  clang-tools-extra/clangd/Hover.cpp
  clang-tools-extra/clangd/Hover.h
  clang-tools-extra/clangd/IncludeCleaner.cpp
  clang-tools-extra/clangd/IncludeCleaner.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
@@ -10,6 +10,7 @@
 #include "Annotations.h"
 #include "Config.h"
 #include "Hover.h"
+#include "TestFS.h"
 #include "TestIndex.h"
 #include "TestTU.h"
 #include "index/MemIndex.h"
@@ -20,6 +21,7 @@
 
 #include "gtest/gtest.h"
 #include <functional>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -2978,6 +2980,67 @@
     EXPECT_EQ(Case.HI.present().asMarkdown(), Case.ExpectedMarkdown);
 }
 
+TEST(Hover, UsedSymbols) {
+  struct {
+    Annotations Code;
+    const std::function<void(HoverInfo &)> ExpectedBuilder;
+  } Cases[] = {{Annotations(R"cpp(
+                                        #inclu^de "foo.h"            
+                                        int var = foo();
+                                      )cpp"),
+                [](HoverInfo &HI) { HI.UsedSymbolNames = {"foo"}; }},
+               {Annotations("[[#incl^ude \"foo.h\"]]"),
+                [](HoverInfo &HI) { HI.UsedSymbolNames = {}; }},
+               {Annotations(R"cpp(
+                                        #include "foo.h"  
+                                        #include ^"bar.h"
+                                        int fooVar = foo();
+                                        int barVar = bar();
+                                        int foobarVar = foobar();
+                                        X x;
+                                      )cpp"),
+                [](HoverInfo &HI) {
+                  HI.UsedSymbolNames = {"X", "bar", "foobar"};
+                }},
+               {Annotations(R"cpp(
+                                        #in^clude <foo>
+                                        int fooVar = foo();
+                                      )cpp"),
+                [](HoverInfo &HI) { HI.UsedSymbolNames = {"foo"}; }},
+               {Annotations(R"cpp(
+                                        #in^clude "public.h"
+                                        #include "private.h"
+                                        int fooVar = foo();
+                                      )cpp"),
+                [](HoverInfo &HI) { HI.UsedSymbolNames = {"foo"}; }}
+
+  };
+  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("int foo();");
+    TU.AdditionalFiles["bar.h"] = guard("int bar(); int foobar(); class X {};");
+    TU.AdditionalFiles["private.h"] = guard(R"cpp(
+                                                    // IWYU pragma: private, include "public.h"
+                                                    int foo(); 
+                                                  )cpp");
+    TU.AdditionalFiles["public.h"] = guard("");
+    TU.AdditionalFiles["system/foo"] = guard("int foo();");
+    TU.ExtraArgs.push_back("-isystem" + testPath("system"));
+
+    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->UsedSymbolNames, Expected.UsedSymbolNames);
+  }
+}
+
 TEST(Hover, DocsFromIndex) {
   Annotations T(R"cpp(
   template <typename T> class X {};
Index: clang-tools-extra/clangd/IncludeCleaner.h
===================================================================
--- clang-tools-extra/clangd/IncludeCleaner.h
+++ clang-tools-extra/clangd/IncludeCleaner.h
@@ -78,6 +78,9 @@
 /// representation. The spelling contains the ""<> characters.
 std::string spellHeader(ParsedAST &AST, const FileEntry *MainFile,
                         include_cleaner::Header Provider);
+
+std::vector<include_cleaner::SymbolReference>
+collectMacroReferences(ParsedAST &AST);
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/IncludeCleaner.cpp
===================================================================
--- clang-tools-extra/clangd/IncludeCleaner.cpp
+++ clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -124,27 +124,6 @@
   return true;
 }
 
-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;
-}
-
 llvm::StringRef getResolvedPath(const include_cleaner::Header &SymProvider) {
   switch (SymProvider.kind()) {
   case include_cleaner::Header::Physical:
@@ -276,6 +255,28 @@
 }
 } // namespace
 
+
+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;
+}
+
 include_cleaner::Includes
 convertIncludes(const SourceManager &SM,
                 const llvm::ArrayRef<Inclusion> Includes) {
Index: clang-tools-extra/clangd/Hover.h
===================================================================
--- clang-tools-extra/clangd/Hover.h
+++ clang-tools-extra/clangd/Hover.h
@@ -15,6 +15,7 @@
 #include "clang/Index/IndexSymbol.h"
 #include <optional>
 #include <string>
+#include <vector>
 
 namespace clang {
 namespace clangd {
@@ -110,6 +111,9 @@
   };
   // Set only if CalleeArgInfo is set.
   std::optional<PassType> CallPassType;
+  // Filled when hovering over the #include line. Contains the names of symbols
+  // from a #include'd file that are used in the main file.
+  std::vector<std::string> UsedSymbolNames;
 
   /// Produce a user-readable information.
   markup::Document present() const;
Index: clang-tools-extra/clangd/Hover.cpp
===================================================================
--- clang-tools-extra/clangd/Hover.cpp
+++ clang-tools-extra/clangd/Hover.cpp
@@ -12,6 +12,7 @@
 #include "CodeCompletionStrings.h"
 #include "Config.h"
 #include "FindTarget.h"
+#include "Headers.h"
 #include "IncludeCleaner.h"
 #include "ParsedAST.h"
 #include "Selection.h"
@@ -38,7 +39,9 @@
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/CharInfo.h"
+#include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Index/IndexSymbol.h"
@@ -53,6 +56,7 @@
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/raw_ostream.h"
 #include <optional>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -1134,6 +1138,52 @@
   HI.Provider = spellHeader(AST, SM.getFileEntryForID(SM.getMainFileID()), H);
 }
 
+// FIXME: similar functions are present in FindHeaders.cpp (symbolName)
+// and IncludeCleaner.cpp (getSymbolName). Introduce a name() method into
+// include_cleaner::Symbol instead.
+std::string getRefName(include_cleaner::SymbolReference Ref) {
+  std::string Name;
+  switch (Ref.Target.kind()) {
+  case include_cleaner::Symbol::Declaration:
+    if (const auto *ND = llvm::dyn_cast<NamedDecl>(&Ref.Target.declaration()))
+      Name = ND->getDeclName().getAsString();
+    break;
+  case include_cleaner::Symbol::Macro:
+    Name = Ref.Target.macro().Name->getName();
+    break;
+  }
+  return Name;
+}
+
+void maybeAddUsedSymbols(ParsedAST &AST, HoverInfo &HI, const Inclusion &Inc) {
+  const SourceManager &SM = AST.getSourceManager();
+  // Use set to record each symbol name once
+  std::set<std::string> UsedSymbolNames;
+  include_cleaner::walkUsed(
+      AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
+      AST.getPragmaIncludes(), SM,
+      [&](const include_cleaner::SymbolReference &Ref,
+          llvm::ArrayRef<include_cleaner::Header> Providers) {
+        if (Ref.RT != include_cleaner::RefType::Explicit ||
+            !SM.isWrittenInMainFile(SM.getSpellingLoc(Ref.RefLocation)))
+          return;
+
+        include_cleaner::Includes ConvertedIncludes =
+            convertIncludes(SM, llvm::ArrayRef{Inc});
+        for (const include_cleaner::Header &H : Providers) {
+          llvm::SmallVector<const include_cleaner::Include *> Matches =
+              ConvertedIncludes.match(H);
+          if (!Matches.empty()) {
+            UsedSymbolNames.insert(getRefName(Ref));
+            return;
+          }
+        }
+      });
+
+  HI.UsedSymbolNames =
+      std::vector<std::string>{UsedSymbolNames.begin(), UsedSymbolNames.end()};
+}
+
 } // namespace
 
 std::optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
@@ -1163,6 +1213,7 @@
     HI.Definition =
         URIForFile::canonicalize(Inc.Resolved, AST.tuPath()).file().str();
     HI.DefinitionLanguage = "";
+    maybeAddUsedSymbols(AST, HI, Inc);
     return HI;
   }
 
@@ -1378,6 +1429,31 @@
     Output.addCodeBlock(Buffer, DefinitionLanguage);
   }
 
+  if (!UsedSymbolNames.empty()) {
+    Output.addRuler();
+    markup::Paragraph &P = Output.addParagraph();
+    P.appendText("provides ");
+
+    const unsigned SymbolNamesLimit = 5;
+    for (unsigned I = 0;
+         I < UsedSymbolNames.size() - 1 && I < SymbolNamesLimit - 1; I++) {
+      P.appendCode(UsedSymbolNames[I]);
+      P.appendText(", ");
+    }
+
+    if (UsedSymbolNames.size() >= SymbolNamesLimit) {
+      P.appendCode(UsedSymbolNames[SymbolNamesLimit - 1]);
+    } else {
+      P.appendCode(UsedSymbolNames[UsedSymbolNames.size() - 1]);
+    }
+
+    if (UsedSymbolNames.size() > SymbolNamesLimit) {
+      P.appendText(" and ");
+      P.appendText(std::to_string(UsedSymbolNames.size() - SymbolNamesLimit));
+      P.appendText(" more");
+    }
+  }
+
   return Output;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to