From bbb0442bbfcfbef7d224551006e22d9c916e6543 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Thu, 26 Sep 2019 07:27:43 +0000 Subject: [PATCH] [clangd] Add a helper for extracting nonlocal decls in a FunctionDecl Summary: To be used by define-inline code action to determine whether the function/method body will still be valid in another context. Traverses clang-ast to find all decl nodes under the function decl and stores the non-local ones. Reviewers: hokein Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D67748 llvm-svn: 372948 --- clang-tools-extra/clangd/XRefs.cpp | 14 ++++ clang-tools-extra/clangd/XRefs.h | 5 +- clang-tools-extra/clangd/unittests/XRefsTests.cpp | 85 +++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 2c8d161..f51891b 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -9,6 +9,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "FindSymbols.h" +#include "FindTarget.h" #include "FormattedString.h" #include "Logger.h" #include "ParsedAST.h" @@ -1299,5 +1300,18 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, return OS; } +llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, + const FunctionDecl *FD) { + if (!FD->hasBody()) + return {}; + llvm::DenseSet DeclRefs; + findExplicitReferences(FD, [&](ReferenceLoc Ref) { + for (const Decl *D : Ref.Targets) { + if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter()) + DeclRefs.insert(D); + } + }); + return DeclRefs; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index d6b3930..dc2237cb0 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -17,8 +17,8 @@ #include "Path.h" #include "Protocol.h" #include "index/Index.h" -#include "clang/AST/Type.h" #include "index/SymbolLocation.h" +#include "clang/AST/Type.h" #include "clang/Format/Format.h" #include "clang/Index/IndexSymbol.h" #include "llvm/ADT/Optional.h" @@ -158,6 +158,9 @@ llvm::Optional getDeducedType(ParsedAST &AST, /// SourceLocationBeg must point to the first character of the token bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg); +/// Returns all decls that are referenced in the \p FD except local symbols. +llvm::DenseSet getNonLocalDeclRefs(ParsedAST &AST, + const FunctionDecl *FD); } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index e4a4a6f..d42bed4 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -10,6 +10,7 @@ #include "Matchers.h" #include "ParsedAST.h" #include "Protocol.h" +#include "SourceCode.h" #include "SyncAPI.h" #include "TestFS.h" #include "TestIndex.h" @@ -18,13 +19,19 @@ #include "index/FileIndex.h" #include "index/MemIndex.h" #include "index/SymbolCollector.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Index/IndexingAction.h" #include "llvm/ADT/None.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include +#include namespace clang { namespace clangd { @@ -2187,6 +2194,84 @@ TEST(GetDeducedType, KwAutoExpansion) { } } +TEST(GetNonLocalDeclRefs, All) { + struct Case { + llvm::StringRef AnnotatedCode; + std::vector ExpectedDecls; + } Cases[] = { + { + // VarDecl and ParamVarDecl + R"cpp( + void bar(); + void ^foo(int baz) { + int x = 10; + bar(); + })cpp", + {"bar"}, + }, + { + // Method from class + R"cpp( + class Foo { public: void foo(); }; + class Bar { + void foo(); + void bar(); + }; + void Bar::^foo() { + Foo f; + bar(); + f.foo(); + })cpp", + {"Bar", "Bar::bar", "Foo", "Foo::foo"}, + }, + { + // Local types + R"cpp( + void ^foo() { + class Foo { public: void foo() {} }; + class Bar { public: void bar() {} }; + Foo f; + Bar b; + b.bar(); + f.foo(); + })cpp", + {}, + }, + { + // Template params + R"cpp( + template class Q> + void ^foo() { + T x; + Q y; + })cpp", + {}, + }, + }; + for (const Case &C : Cases) { + Annotations File(C.AnnotatedCode); + auto AST = TestTU::withCode(File.code()).build(); + ASSERT_TRUE(AST.getDiagnostics().empty()) + << AST.getDiagnostics().begin()->Message; + SourceLocation SL = llvm::cantFail( + sourceLocationInMainFile(AST.getSourceManager(), File.point())); + + const FunctionDecl *FD = + llvm::dyn_cast(&findDecl(AST, [SL](const NamedDecl &ND) { + return ND.getLocation() == SL && llvm::isa(ND); + })); + ASSERT_NE(FD, nullptr); + + auto NonLocalDeclRefs = getNonLocalDeclRefs(AST, FD); + std::vector Names; + for (const Decl *D : NonLocalDeclRefs) { + if (const auto *ND = llvm::dyn_cast(D)) + Names.push_back(ND->getQualifiedNameAsString()); + } + EXPECT_THAT(Names, UnorderedElementsAreArray(C.ExpectedDecls)); + } +} + } // namespace } // namespace clangd } // namespace clang -- 2.7.4