brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.2 KiB · c2dd8e1 Raw
584 lines · cpp
1//===-- AddUsingTests.cpp ---------------------------------------*- C++ -*-===//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 "Config.h"10#include "TweakTesting.h"11#include "support/Context.h"12#include "llvm/ADT/StringMap.h"13#include "llvm/ADT/StringRef.h"14#include "gtest/gtest.h"15#include <string>16#include <utility>17 18namespace clang {19namespace clangd {20namespace {21 22TWEAK_TEST(AddUsing);23 24TEST_F(AddUsingTest, Prepare) {25  Config Cfg;26  Cfg.Style.FullyQualifiedNamespaces.push_back("ban");27  WithContextValue WithConfig(Config::Key, std::move(Cfg));28 29  const std::string Header = R"cpp(30#define NS(name) one::two::name31namespace ban { void foo() {} }32namespace banana { void foo() {} }33namespace one {34void oo() {}35template<typename TT> class tt {};36namespace two {37enum ee { ee_enum_value };38void ff() {}39class cc {40public:41  struct st {};42  static void mm() {}43  cc operator|(const cc& x) const { return x; }44};45}46})cpp";47 48  EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^f^f(); }");49  EXPECT_AVAILABLE(Header + "void fun() { o^n^e^::^o^o(); }");50  EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o^:^:^e^e E; }");51  EXPECT_AVAILABLE(Header + "void fun() { o^n^e^:^:^t^w^o:^:^c^c C; }");52  EXPECT_UNAVAILABLE(Header +53                     "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^m^m(); }");54  EXPECT_UNAVAILABLE(Header +55                     "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");56  EXPECT_UNAVAILABLE(Header +57                     "void fun() { o^n^e^:^:^t^w^o^:^:^c^c^:^:^s^t inst; }");58  EXPECT_UNAVAILABLE(Header + "void fun() { N^S(c^c) inst; }");59  // This used to crash. Ideally we would support this case, but for now we just60  // test that we don't crash.61  EXPECT_UNAVAILABLE(Header +62                     "template<typename TT> using foo = one::tt<T^T>;");63  // Test that we don't crash or misbehave on unnamed DeclRefExpr.64  EXPECT_UNAVAILABLE(Header +65                     "void fun() { one::two::cc() ^| one::two::cc(); }");66  // Do not offer code action when operating on a banned namespace.67  EXPECT_UNAVAILABLE(Header + "void fun() { ban::fo^o(); }");68  EXPECT_UNAVAILABLE(Header + "void fun() { ::ban::fo^o(); }");69  EXPECT_AVAILABLE(Header + "void fun() { banana::fo^o(); }");70 71  // NestedNameSpecifier, but no namespace.72  EXPECT_UNAVAILABLE(Header + "class Foo {}; class F^oo foo;");73 74  // Nested macro case.75  EXPECT_AVAILABLE(R"cpp(76  #define ID2(X) X77  #define ID(Y, X) Y;ID2(X)78  namespace ns { struct Foo{}; }79  ID(int xyz, ns::F^oo) f;)cpp");80 81  // Check that we do not trigger in header files.82  FileName = "test.h";83  ExtraArgs.push_back("-xc++-header"); // .h file is treated a C by default.84  EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");85  FileName = "test.hpp";86  EXPECT_UNAVAILABLE(Header + "void fun() { one::two::f^f(); }");87}88 89TEST_F(AddUsingTest, Crash1072) {90  // Used to crash when traversing catch(...)91  // https://github.com/clangd/clangd/issues/107292  const char *Code = R"cpp(93  namespace ns { class A; }94  ns::^A *err;95  void catchall() {96    try {} catch(...) {}97  }98  )cpp";99  EXPECT_AVAILABLE(Code);100}101 102TEST_F(AddUsingTest, Apply) {103  FileName = "test.cpp";104  struct {105    llvm::StringRef TestSource;106    llvm::StringRef ExpectedSource;107  } Cases[]{108      {109          // Function, no other using, namespace.110          R"cpp(111#include "test.hpp"112namespace {113void fun() {114  ^one::two::ff();115}116})cpp",117          R"cpp(118#include "test.hpp"119namespace {using one::two::ff;120 121void fun() {122  ff();123}124})cpp",125      },126      // Type, no other using, namespace.127      {128          R"cpp(129#include "test.hpp"130namespace {131void fun() {132  ::one::t^wo::cc inst;133}134})cpp",135          R"cpp(136#include "test.hpp"137namespace {using ::one::two::cc;138 139void fun() {140  cc inst;141}142})cpp",143      },144      // Type, no other using, no namespace.145      {146          R"cpp(147#include "test.hpp"148 149void fun() {150  one::two::e^e inst;151})cpp",152          R"cpp(153#include "test.hpp"154 155using one::two::ee;156 157void fun() {158  ee inst;159})cpp"},160      // Function, other usings.161      {162          R"cpp(163#include "test.hpp"164 165using one::two::cc;166using one::two::ee;167 168namespace {169void fun() {170  one::two::f^f();171}172})cpp",173          R"cpp(174#include "test.hpp"175 176using one::two::cc;177using one::two::ff;using one::two::ee;178 179namespace {180void fun() {181  ff();182}183})cpp",184      },185      // Function, other usings inside namespace.186      {187          R"cpp(188#include "test.hpp"189 190using one::two::cc;191 192namespace {193 194using one::two::ff;195 196void fun() {197  o^ne::oo();198}199})cpp",200          R"cpp(201#include "test.hpp"202 203using one::two::cc;204 205namespace {206 207using one::oo;using one::two::ff;208 209void fun() {210  oo();211}212})cpp"},213      // Using comes after cursor.214      {215          R"cpp(216#include "test.hpp"217 218namespace {219 220void fun() {221  one::t^wo::ff();222}223 224using one::two::cc;225 226})cpp",227          R"cpp(228#include "test.hpp"229 230namespace {using one::two::ff;231 232 233void fun() {234  ff();235}236 237using one::two::cc;238 239})cpp"},240      // Pointer type.241      {R"cpp(242#include "test.hpp"243 244void fun() {245  one::two::c^c *p;246})cpp",247       R"cpp(248#include "test.hpp"249 250using one::two::cc;251 252void fun() {253  cc *p;254})cpp"},255      // Namespace declared via macro.256      {R"cpp(257#include "test.hpp"258#define NS_BEGIN(name) namespace name {259 260NS_BEGIN(foo)261 262void fun() {263  one::two::f^f();264}265})cpp",266       R"cpp(267#include "test.hpp"268#define NS_BEGIN(name) namespace name {269 270using one::two::ff;271 272NS_BEGIN(foo)273 274void fun() {275  ff();276}277})cpp"},278      // Inside macro argument.279      {R"cpp(280#include "test.hpp"281#define CALL(name) name()282 283void fun() {284  CALL(one::t^wo::ff);285})cpp",286       R"cpp(287#include "test.hpp"288#define CALL(name) name()289 290using one::two::ff;291 292void fun() {293  CALL(ff);294})cpp"},295      // Parent namespace != lexical parent namespace296      {R"cpp(297#include "test.hpp"298namespace foo { void fun(); }299 300void foo::fun() {301  one::two::f^f();302})cpp",303       R"cpp(304#include "test.hpp"305using one::two::ff;306 307namespace foo { void fun(); }308 309void foo::fun() {310  ff();311})cpp"},312      // Inside a lambda.313      {314          R"cpp(315namespace NS {316void unrelated();317void foo();318}319 320auto L = [] {321  using NS::unrelated;322  NS::f^oo();323};)cpp",324          R"cpp(325namespace NS {326void unrelated();327void foo();328}329 330auto L = [] {331  using NS::foo;using NS::unrelated;332  foo();333};)cpp",334      },335      // If all other using are fully qualified, add ::336      {R"cpp(337#include "test.hpp"338 339using ::one::two::cc;340using ::one::two::ee;341 342void fun() {343  one::two::f^f();344})cpp",345       R"cpp(346#include "test.hpp"347 348using ::one::two::cc;349using ::one::two::ff;using ::one::two::ee;350 351void fun() {352  ff();353})cpp"},354      // Make sure we don't add :: if it's already there355      {R"cpp(356#include "test.hpp"357 358using ::one::two::cc;359using ::one::two::ee;360 361void fun() {362  ::one::two::f^f();363})cpp",364       R"cpp(365#include "test.hpp"366 367using ::one::two::cc;368using ::one::two::ff;using ::one::two::ee;369 370void fun() {371  ff();372})cpp"},373      // If even one using doesn't start with ::, do not add it374      {R"cpp(375#include "test.hpp"376 377using ::one::two::cc;378using one::two::ee;379 380void fun() {381  one::two::f^f();382})cpp",383       R"cpp(384#include "test.hpp"385 386using ::one::two::cc;387using one::two::ff;using one::two::ee;388 389void fun() {390  ff();391})cpp"},392      // using alias; insert using for the spelled name.393      {R"cpp(394#include "test.hpp"395 396void fun() {397  one::u^u u;398})cpp",399       R"cpp(400#include "test.hpp"401 402using one::uu;403 404void fun() {405  uu u;406})cpp"},407      // using namespace.408      {R"cpp(409#include "test.hpp"410using namespace one;411namespace {412two::c^c C;413})cpp",414       R"cpp(415#include "test.hpp"416using namespace one;417namespace {using two::cc;418 419cc C;420})cpp"},421      // Type defined in main file, make sure using is after that.422      {R"cpp(423namespace xx {424  struct yy {};425}426 427x^x::yy X;428)cpp",429       R"cpp(430namespace xx {431  struct yy {};432}433 434using xx::yy;435 436yy X;437)cpp"},438      // Type defined in main file via "using", insert after that.439      {R"cpp(440#include "test.hpp"441 442namespace xx {443  using yy = one::two::cc;444}445 446x^x::yy X;447)cpp",448       R"cpp(449#include "test.hpp"450 451namespace xx {452  using yy = one::two::cc;453}454 455using xx::yy;456 457yy X;458)cpp"},459      // Using must come after function definition.460      {R"cpp(461namespace xx {462  void yy();463}464 465void fun() {466  x^x::yy();467}468)cpp",469       R"cpp(470namespace xx {471  void yy();472}473 474using xx::yy;475 476void fun() {477  yy();478}479)cpp"},480      // Existing using with non-namespace part.481      {R"cpp(482#include "test.hpp"483using one::two::ee::ee_one;484one::t^wo::cc c;485)cpp",486       R"cpp(487#include "test.hpp"488using one::two::cc;using one::two::ee::ee_one;489cc c;490)cpp"},491      // Template (like std::vector).492      {R"cpp(493#include "test.hpp"494one::v^ec<int> foo;495)cpp",496       R"cpp(497#include "test.hpp"498using one::vec;499 500vec<int> foo;501)cpp"},502      // Typo correction.503      {R"cpp(504// error-ok505#include "test.hpp"506c^c C;507)cpp",508       R"cpp(509// error-ok510#include "test.hpp"511using one::two::cc;512 513cc C;514)cpp"},515      {R"cpp(516// error-ok517#include "test.hpp"518void foo() {519  switch(one::two::ee{}) { case two::ee_^one:break; }520}521)cpp",522       R"cpp(523// error-ok524#include "test.hpp"525using one::two::ee_one;526 527void foo() {528  switch(one::two::ee{}) { case ee_one:break; }529}530)cpp"},531      {R"cpp(532#include "test.hpp"533void foo() {534  one::f^unc_temp<int>();535})cpp",536       R"cpp(537#include "test.hpp"538using one::func_temp;539 540void foo() {541  func_temp<int>();542})cpp"},543      {R"cpp(544#include "test.hpp"545void foo() {546  one::va^r_temp<int>;547})cpp",548       R"cpp(549#include "test.hpp"550using one::var_temp;551 552void foo() {553  var_temp<int>;554})cpp"},555  };556  llvm::StringMap<std::string> EditedFiles;557  for (const auto &Case : Cases) {558    ExtraFiles["test.hpp"] = R"cpp(559namespace one {560void oo() {}561namespace two {562enum ee {ee_one};563void ff() {}564class cc {565public:566  struct st { struct nested {}; };567  static void mm() {}568};569}570using uu = two::cc;571template<typename T> struct vec {};572template <typename T> void func_temp();573template <typename T> T var_temp();574})cpp";575    // Typo correction is disabled in msvc-compatibility mode.576    ExtraArgs.push_back("-fno-ms-compatibility");577    EXPECT_EQ(apply(Case.TestSource, &EditedFiles), Case.ExpectedSource);578  }579}580 581} // namespace582} // namespace clangd583} // namespace clang584