brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.8 KiB · 3d5365a Raw
210 lines · cpp
1//===-- InsertionPointTess.cpp  -------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Annotations.h"10#include "Protocol.h"11#include "SourceCode.h"12#include "TestTU.h"13#include "XRefs.h"14#include "refactor/InsertionPoint.h"15#include "clang/AST/DeclBase.h"16#include "llvm/Testing/Support/Error.h"17#include "gmock/gmock.h"18#include "gtest/gtest.h"19 20namespace clang {21namespace clangd {22namespace {23using llvm::HasValue;24 25TEST(InsertionPointTests, Generic) {26  Annotations Code(R"cpp(27  namespace ns {28    $a^int a1;29    $b^// leading comment30    int b;31    $c^int c1; // trailing comment32    int c2;33    $a2^int a2;34  $end^};35  )cpp");36 37  auto StartsWith =38      [&](llvm::StringLiteral S) -> std::function<bool(const Decl *)> {39    return [S](const Decl *D) {40      if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))41        return llvm::StringRef(ND->getNameAsString()).starts_with(S);42      return false;43    };44  };45 46  auto AST = TestTU::withCode(Code.code()).build();47  auto &NS = cast<NamespaceDecl>(findDecl(AST, "ns"));48 49  // Test single anchors.50  auto Point = [&](llvm::StringLiteral Prefix, Anchor::Dir Direction) {51    auto Loc = insertionPoint(NS, {Anchor{StartsWith(Prefix), Direction}});52    return sourceLocToPosition(AST.getSourceManager(), Loc);53  };54  EXPECT_EQ(Point("a", Anchor::Above), Code.point("a"));55  EXPECT_EQ(Point("a", Anchor::Below), Code.point("b"));56  EXPECT_EQ(Point("b", Anchor::Above), Code.point("b"));57  EXPECT_EQ(Point("b", Anchor::Below), Code.point("c"));58  EXPECT_EQ(Point("c", Anchor::Above), Code.point("c"));59  EXPECT_EQ(Point("c", Anchor::Below), Code.point("a2"));60  EXPECT_EQ(Point("", Anchor::Above), Code.point("a"));61  EXPECT_EQ(Point("", Anchor::Below), Code.point("end"));62  EXPECT_EQ(Point("no_match", Anchor::Below), Position{});63 64  // Test anchor chaining.65  auto Chain = [&](llvm::StringLiteral P1, llvm::StringLiteral P2) {66    auto Loc = insertionPoint(NS, {Anchor{StartsWith(P1), Anchor::Above},67                                   Anchor{StartsWith(P2), Anchor::Above}});68    return sourceLocToPosition(AST.getSourceManager(), Loc);69  };70  EXPECT_EQ(Chain("a", "b"), Code.point("a"));71  EXPECT_EQ(Chain("b", "a"), Code.point("b"));72  EXPECT_EQ(Chain("no_match", "a"), Code.point("a"));73 74  // Test edit generation.75  auto Edit = insertDecl("foo;", NS, {Anchor{StartsWith("a"), Anchor::Below}});76  ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());77  EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()), Code.point("b"));78  EXPECT_EQ(Edit->getReplacementText(), "foo;");79  // If no match, the edit is inserted at the end.80  Edit = insertDecl("x;", NS, {Anchor{StartsWith("no_match"), Anchor::Below}});81  ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());82  EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),83            Code.point("end"));84}85 86// For CXX, we should check:87// - special handling for access specifiers88// - unwrapping of template decls89TEST(InsertionPointTests, CXX) {90  Annotations Code(R"cpp(91    class C {92    public:93      $Method^void pubMethod();94      $Field^int PubField;95 96    $private^private:97      $field^int PrivField;98      $method^void privMethod();99      template <typename T> void privTemplateMethod();100    $end^};101  )cpp");102 103  auto AST = TestTU::withCode(Code.code()).build();104  const CXXRecordDecl &C = cast<CXXRecordDecl>(findDecl(AST, "C"));105 106  auto IsMethod = [](const Decl *D) { return llvm::isa<CXXMethodDecl>(D); };107  auto Any = [](const Decl *D) { return true; };108 109  // Test single anchors.110  auto Point = [&](Anchor A, AccessSpecifier Protection) {111    auto Loc = insertionPoint(C, {A}, Protection);112    return sourceLocToPosition(AST.getSourceManager(), Loc);113  };114  EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_public), Code.point("Method"));115  EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_public), Code.point("Field"));116  EXPECT_EQ(Point({Any, Anchor::Above}, AS_public), Code.point("Method"));117  EXPECT_EQ(Point({Any, Anchor::Below}, AS_public), Code.point("private"));118  EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_private), Code.point("method"));119  EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_private), Code.point("end"));120  EXPECT_EQ(Point({Any, Anchor::Above}, AS_private), Code.point("field"));121  EXPECT_EQ(Point({Any, Anchor::Below}, AS_private), Code.point("end"));122  EXPECT_EQ(Point({IsMethod, Anchor::Above}, AS_protected), Position{});123  EXPECT_EQ(Point({IsMethod, Anchor::Below}, AS_protected), Position{});124  EXPECT_EQ(Point({Any, Anchor::Above}, AS_protected), Position{});125  EXPECT_EQ(Point({Any, Anchor::Below}, AS_protected), Position{});126 127  // Edits when there's no match --> end of matching access control section.128  auto Edit = insertDecl("x", C, {}, AS_public);129  ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());130  EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),131            Code.point("private"));132 133  Edit = insertDecl("x", C, {}, AS_private);134  ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());135  EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),136            Code.point("end"));137 138  Edit = insertDecl("x", C, {}, AS_protected);139  ASSERT_THAT_EXPECTED(Edit, llvm::Succeeded());140  EXPECT_EQ(offsetToPosition(Code.code(), Edit->getOffset()),141            Code.point("end"));142  EXPECT_EQ(Edit->getReplacementText(), "protected:\nx");143}144 145MATCHER_P(replacementText, Text, "") {146  if (arg.getReplacementText() != Text) {147    *result_listener << "replacement is " << arg.getReplacementText().str();148    return false;149  }150  return true;151}152 153TEST(InsertionPointTests, CXXAccessProtection) {154  // Empty class uses default access.155  auto AST = TestTU::withCode("struct S{};").build();156  const CXXRecordDecl &S = cast<CXXRecordDecl>(findDecl(AST, "S"));157  ASSERT_THAT_EXPECTED(insertDecl("x", S, {}, AS_public),158                       HasValue(replacementText("x")));159  ASSERT_THAT_EXPECTED(insertDecl("x", S, {}, AS_private),160                       HasValue(replacementText("private:\nx")));161 162  // We won't insert above the first access specifier if there's nothing there.163  AST = TestTU::withCode("struct T{private:};").build();164  const CXXRecordDecl &T = cast<CXXRecordDecl>(findDecl(AST, "T"));165  ASSERT_THAT_EXPECTED(insertDecl("x", T, {}, AS_public),166                       HasValue(replacementText("public:\nx")));167  ASSERT_THAT_EXPECTED(insertDecl("x", T, {}, AS_private),168                       HasValue(replacementText("x")));169 170  // But we will if there are declarations.171  AST = TestTU::withCode("struct U{int i;private:};").build();172  const CXXRecordDecl &U = cast<CXXRecordDecl>(findDecl(AST, "U"));173  ASSERT_THAT_EXPECTED(insertDecl("x", U, {}, AS_public),174                       HasValue(replacementText("x")));175  ASSERT_THAT_EXPECTED(insertDecl("x", U, {}, AS_private),176                       HasValue(replacementText("x")));177}178 179// In ObjC we need to take care to get the @end fallback right.180TEST(InsertionPointTests, ObjC) {181  Annotations Code(R"objc(182    @interface Foo183     -(void) v;184    $endIface^@end185    @implementation Foo186     -(void) v {}187    $endImpl^@end188  )objc");189  auto TU = TestTU::withCode(Code.code());190  TU.Filename = "TestTU.m";191  auto AST = TU.build();192 193  auto &Impl =194      cast<ObjCImplementationDecl>(findDecl(AST, [&](const NamedDecl &D) {195        return llvm::isa<ObjCImplementationDecl>(D);196      }));197  auto &Iface = *Impl.getClassInterface();198  Anchor End{[](const Decl *) { return true; }, Anchor::Below};199 200  const auto &SM = AST.getSourceManager();201  EXPECT_EQ(sourceLocToPosition(SM, insertionPoint(Iface, {End})),202            Code.point("endIface"));203  EXPECT_EQ(sourceLocToPosition(SM, insertionPoint(Impl, {End})),204            Code.point("endImpl"));205}206 207} // namespace208} // namespace clangd209} // namespace clang210