forked from OSchip/llvm-project
275 lines
7.4 KiB
C++
275 lines
7.4 KiB
C++
//===-- 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 "ClangdServer.h"
|
|
#include "Matchers.h"
|
|
#include "Protocol.h"
|
|
#include "SemanticSelection.h"
|
|
#include "SourceCode.h"
|
|
#include "SyncAPI.h"
|
|
#include "TestFS.h"
|
|
#include "TestTU.h"
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <vector>
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
namespace {
|
|
|
|
using ::testing::ElementsAre;
|
|
using ::testing::ElementsAreArray;
|
|
using ::testing::UnorderedElementsAreArray;
|
|
|
|
// front() is SR.range, back() is outermost range.
|
|
std::vector<Range> gatherRanges(const SelectionRange &SR) {
|
|
std::vector<Range> Ranges;
|
|
for (const SelectionRange *S = &SR; S; S = S->parent.get())
|
|
Ranges.push_back(S->range);
|
|
return Ranges;
|
|
}
|
|
|
|
std::vector<Range>
|
|
gatherFoldingRanges(llvm::ArrayRef<FoldingRange> FoldingRanges) {
|
|
std::vector<Range> Ranges;
|
|
Range NextRange;
|
|
for (const auto &R : FoldingRanges) {
|
|
NextRange.start.line = R.startLine;
|
|
NextRange.start.character = R.startCharacter;
|
|
NextRange.end.line = R.endLine;
|
|
NextRange.end.character = R.endCharacter;
|
|
Ranges.push_back(NextRange);
|
|
}
|
|
return Ranges;
|
|
}
|
|
|
|
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",
|
|
R"cpp( // Cursor at end of VarDecl.
|
|
[[int v = [[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(gatherRanges(llvm::cantFail(getSemanticRanges(AST, T.point()))),
|
|
ElementsAreArray(T.ranges()))
|
|
<< Test;
|
|
}
|
|
}
|
|
|
|
TEST(SemanticSelection, RunViaClangdServer) {
|
|
MockFS FS;
|
|
MockCompilationDatabase CDB;
|
|
ClangdServer Server(CDB, FS, ClangdServer::optsForTest());
|
|
|
|
auto FooH = testPath("foo.h");
|
|
FS.Files[FooH] = R"cpp(
|
|
int foo(int x);
|
|
#define HASH(x) ((x) % 10)
|
|
)cpp";
|
|
|
|
auto FooCpp = testPath("Foo.cpp");
|
|
const char *SourceContents = R"cpp(
|
|
#include "foo.h"
|
|
[[void bar(int& inp) [[{
|
|
// inp = HASH(foo(inp));
|
|
[[inp = [[HASH([[foo([[in^p]])]])]]]];
|
|
}]]]]
|
|
$empty[[^]]
|
|
)cpp";
|
|
Annotations SourceAnnotations(SourceContents);
|
|
FS.Files[FooCpp] = std::string(SourceAnnotations.code());
|
|
Server.addDocument(FooCpp, SourceAnnotations.code());
|
|
|
|
auto Ranges = runSemanticRanges(Server, FooCpp, SourceAnnotations.points());
|
|
ASSERT_TRUE(bool(Ranges))
|
|
<< "getSemanticRange returned an error: " << Ranges.takeError();
|
|
ASSERT_EQ(Ranges->size(), SourceAnnotations.points().size());
|
|
EXPECT_THAT(gatherRanges(Ranges->front()),
|
|
ElementsAreArray(SourceAnnotations.ranges()));
|
|
EXPECT_THAT(gatherRanges(Ranges->back()),
|
|
ElementsAre(SourceAnnotations.range("empty")));
|
|
}
|
|
|
|
TEST(FoldingRanges, All) {
|
|
const char *Tests[] = {
|
|
R"cpp(
|
|
#define FOO int foo() {\
|
|
int Variable = 42; \
|
|
}
|
|
|
|
// Do not generate folding range for braces within macro expansion.
|
|
FOO
|
|
|
|
// Do not generate folding range within macro arguments.
|
|
#define FUNCTOR(functor) functor
|
|
void func() {[[
|
|
FUNCTOR([](){});
|
|
]]}
|
|
|
|
// Do not generate folding range with a brace coming from macro.
|
|
#define LBRACE {
|
|
void bar() LBRACE
|
|
int X = 42;
|
|
}
|
|
)cpp",
|
|
R"cpp(
|
|
void func() {[[
|
|
int Variable = 100;
|
|
|
|
if (Variable > 5) {[[
|
|
Variable += 42;
|
|
]]} else if (Variable++)
|
|
++Variable;
|
|
else {[[
|
|
Variable--;
|
|
]]}
|
|
|
|
// Do not generate FoldingRange for empty CompoundStmts.
|
|
for (;;) {}
|
|
|
|
// If there are newlines between {}, we should generate one.
|
|
for (;;) {[[
|
|
|
|
]]}
|
|
]]}
|
|
)cpp",
|
|
R"cpp(
|
|
class Foo {
|
|
public:
|
|
Foo() {[[
|
|
int X = 1;
|
|
]]}
|
|
|
|
private:
|
|
int getBar() {[[
|
|
return 42;
|
|
]]}
|
|
|
|
// Braces are located at the same line: no folding range here.
|
|
void getFooBar() { }
|
|
};
|
|
)cpp",
|
|
};
|
|
for (const char *Test : Tests) {
|
|
auto T = Annotations(Test);
|
|
auto AST = TestTU::withCode(T.code()).build();
|
|
EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(AST))),
|
|
UnorderedElementsAreArray(T.ranges()))
|
|
<< Test;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace clangd
|
|
} // namespace clang
|