diff options
| -rw-r--r-- | clang-tools-extra/clangd/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/SemanticSelection.cpp | 64 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/SemanticSelection.h | 32 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/unittests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp | 143 | 
5 files changed, 241 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt index 8d622c2c572..50de3a31649 100644 --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -66,6 +66,7 @@ add_clang_library(clangDaemon    RIFF.cpp    Selection.cpp    SemanticHighlighting.cpp +  SemanticSelection.cpp    SourceCode.cpp    QueryDriverDatabase.cpp    Threading.cpp diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp b/clang-tools-extra/clangd/SemanticSelection.cpp new file mode 100644 index 00000000000..91a5582ac29 --- /dev/null +++ b/clang-tools-extra/clangd/SemanticSelection.cpp @@ -0,0 +1,64 @@ +//===--- SemanticSelection.cpp -----------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "SemanticSelection.h" +#include "ParsedAST.h" +#include "Protocol.h" +#include "Selection.h" +#include "SourceCode.h" +#include "clang/AST/DeclBase.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { +namespace { +// Adds Range \p R to the Result if it is distinct from the last added Range. +// Assumes that only consecutive ranges can coincide. +void addIfDistinct(const Range &R, std::vector<Range> &Result) { +  if (Result.empty() || Result.back() != R) { +    Result.push_back(R); +  } +} +} // namespace + +llvm::Expected<std::vector<Range>> getSemanticRanges(ParsedAST &AST, +                                                     Position Pos) { +  std::vector<Range> Result; +  const auto &SM = AST.getSourceManager(); +  const auto &LangOpts = AST.getASTContext().getLangOpts(); + +  auto FID = SM.getMainFileID(); +  auto Offset = positionToOffset(SM.getBufferData(FID), Pos); +  if (!Offset) { +    return Offset.takeError(); +  } + +  // Get node under the cursor. +  SelectionTree ST(AST.getASTContext(), AST.getTokens(), *Offset); +  for (const auto *Node = ST.commonAncestor(); Node != nullptr; +       Node = Node->Parent) { +    if (const Decl *D = Node->ASTNode.get<Decl>()) { +      if (llvm::isa<TranslationUnitDecl>(D)) { +        break; +      } +    } + +    auto SR = toHalfOpenFileRange(SM, LangOpts, Node->ASTNode.getSourceRange()); +    if (!SR.hasValue() || SM.getFileID(SR->getBegin()) != SM.getMainFileID()) { +      continue; +    } +    Range R; +    R.start = sourceLocToPosition(SM, SR->getBegin()); +    R.end = sourceLocToPosition(SM, SR->getEnd()); +    addIfDistinct(R, Result); +  } +  return Result; +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/SemanticSelection.h b/clang-tools-extra/clangd/SemanticSelection.h new file mode 100644 index 00000000000..b0d301e2efb --- /dev/null +++ b/clang-tools-extra/clangd/SemanticSelection.h @@ -0,0 +1,32 @@ +//===--- SemanticSelection.h -------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Features for giving interesting semantic ranges around the cursor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSELECTION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSELECTION_H +#include "ParsedAST.h" +#include "Protocol.h" +#include "llvm/Support/Error.h" +#include <vector> +namespace clang { +namespace clangd { + +/// Returns the list of all interesting ranges around the Position \p Pos. +/// The interesting ranges corresponds to the AST nodes in the SelectionTree +/// containing \p Pos. +/// Any range in the result strictly contains all the previous ranges in the +/// result. front() is the inner most range. back() is the outermost range. +llvm::Expected<std::vector<Range>> getSemanticRanges(ParsedAST &AST, +                                                     Position Pos); +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSELECTION_H diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index 913401c72fc..ec835bc1336 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -56,6 +56,7 @@ add_unittest(ClangdUnitTests ClangdTests    RIFFTests.cpp    SelectionTests.cpp    SemanticHighlightingTests.cpp +  SemanticSelectionTests.cpp    SerializationTests.cpp    SourceCodeTests.cpp    SymbolCollectorTests.cpp diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp new file mode 100644 index 00000000000..cd29da2a43d --- /dev/null +++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp @@ -0,0 +1,143 @@ +//===-- SemanticSelectionTests.cpp  ----------------*- C++ -*--------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Annotations.h" +#include "Matchers.h" +#include "Protocol.h" +#include "SemanticSelection.h" +#include "SourceCode.h" +#include "TestTU.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/Error.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include <vector> +namespace clang { +namespace clangd { +namespace { +using ::testing::ElementsAreArray; + +TEST(SemanticSelection, All) { +  const char *Tests[] = { +      R"cpp( // Single statement in a function body. +        [[void func() [[{ +          [[[[int v = [[1^00]]]];]] +        }]]]] +      )cpp", +      R"cpp( // Expression +        [[void func() [[{ +          int a = 1; +          // int v = (10 + 2) * (a + a); +          [[[[int v = [[[[([[[[10^]] + 2]])]] * (a + a)]]]];]] +        }]]]] +      )cpp", +      R"cpp( // Function call. +        int add(int x, int y) { return x + y; } +        [[void callee() [[{ +          // int res = add(11, 22); +          [[[[int res = [[add([[1^1]], 22)]]]];]] +        }]]]] +      )cpp", +      R"cpp( // Tricky macros. +        #define MUL ) * ( +        [[void func() [[{ +          // int var = (4 + 15 MUL 6 + 10); +          [[[[int var = [[[[([[4 + [[1^5]]]] MUL]] 6 + 10)]]]];]] +        }]]]] +       )cpp", +      R"cpp( // Cursor inside a macro. +        #define HASH(x) ((x) % 10) +        [[void func() [[{ +          [[[[int a = [[HASH([[[[2^3]] + 34]])]]]];]] +        }]]]] +       )cpp", +      R"cpp( // Cursor on a macro. +        #define HASH(x) ((x) % 10) +        [[void func() [[{ +          [[[[int a = [[HA^SH(23)]]]];]] +        }]]]] +       )cpp", +      R"cpp( // Multiple declaration. +        [[void func() [[{ +          [[[[int var1, var^2]], var3;]] +        }]]]] +       )cpp", +      R"cpp( // Before comment. +        [[void func() [[{ +          int var1 = 1; +          [[[[int var2 = [[[[var1]]^ /*some comment*/ + 41]]]];]] +        }]]]] +       )cpp", +      // Empty file. +      "^", +      // FIXME: We should get the whole DeclStmt as a range. +      R"cpp( // Single statement in TU. +        [[int v = [[1^00]]]]; +      )cpp", +      // FIXME: No node found associated to the position. +      R"cpp( // Cursor at end of VarDecl. +        void func() { +          int v = 100 + 100^; +        } +      )cpp", +      // FIXME: No node found associated to the position. +      R"cpp( // Cursor in between spaces. +        void func() { +          int v = 100 + ^  100; +        } +      )cpp", +      // Structs. +      R"cpp( +        struct AAA { struct BBB { static int ccc(); };}; +        [[void func() [[{ +          // int x = AAA::BBB::ccc(); +          [[[[int x = [[[[AAA::BBB::c^cc]]()]]]];]] +        }]]]] +      )cpp", +      R"cpp( +        struct AAA { struct BBB { static int ccc(); };}; +        [[void func() [[{ +          // int x = AAA::BBB::ccc(); +          [[[[int x = [[[[[[[[[[AA^A]]::]]BBB::]]ccc]]()]]]];]] +        }]]]] +      )cpp", +      R"cpp( // Inside struct. +        struct A { static int a(); }; +        [[struct B {  +          [[static int b() [[{ +            [[return [[[[1^1]] + 2]]]]; +          }]]]] +        }]]; +      )cpp", +      // Namespaces. +      R"cpp(  +        [[namespace nsa {  +          [[namespace nsb {  +            static int ccc(); +            [[void func() [[{ +              // int x = nsa::nsb::ccc(); +              [[[[int x = [[[[nsa::nsb::cc^c]]()]]]];]] +            }]]]] +          }]] +        }]] +      )cpp", + +  }; + +  for (const char *Test : Tests) { +    auto T = Annotations(Test); +    auto AST = TestTU::withCode(T.code()).build(); +    EXPECT_THAT(llvm::cantFail(getSemanticRanges(AST, T.point())), +                ElementsAreArray(T.ranges())) +        << Test; +  } +} +} // namespace +} // namespace clangd +} // namespace clang  | 

