997 lines · cpp
1//===-- DefineInlineTests.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 "TweakTesting.h"10#include "TestFS.h"11#include "gmock/gmock.h"12#include "gtest/gtest.h"13 14using ::testing::ElementsAre;15 16namespace clang {17namespace clangd {18namespace {19 20TWEAK_TEST(DefineInline);21 22TEST_F(DefineInlineTest, TriggersOnFunctionDecl) {23 // Basic check for function body and signature.24 EXPECT_AVAILABLE(R"cpp(25 class Bar {26 void baz();27 };28 29 [[void [[Bar::[[b^a^z]]]]() [[{30 return;31 }]]]]32 33 void foo();34 [[void [[f^o^o]]() [[{35 return;36 }]]]]37 )cpp");38 39 EXPECT_UNAVAILABLE(R"cpp(40 // Not a definition41 vo^i[[d^ ^f]]^oo();42 43 [[vo^id ]]foo[[()]] {[[44 [[(void)(5+3);45 return;]]46 }]]47 48 // Definition with no body.49 class Bar { Bar() = def^ault; };50 )cpp");51}52 53TEST_F(DefineInlineTest, NoForwardDecl) {54 Header = "void bar();";55 EXPECT_UNAVAILABLE(R"cpp(56 void bar() {57 return;58 }59 // FIXME: Generate a decl in the header.60 void fo^o() {61 return;62 })cpp");63}64 65TEST_F(DefineInlineTest, ReferencedDecls) {66 EXPECT_AVAILABLE(R"cpp(67 void bar();68 void foo(int test);69 70 void fo^o(int baz) {71 int x = 10;72 bar();73 })cpp");74 75 // Internal symbol usage.76 Header = "void foo(int test);";77 EXPECT_UNAVAILABLE(R"cpp(78 void bar();79 void fo^o(int baz) {80 int x = 10;81 bar();82 })cpp");83 84 // Becomes available after making symbol visible.85 Header = "void bar();" + Header;86 EXPECT_AVAILABLE(R"cpp(87 void fo^o(int baz) {88 int x = 10;89 bar();90 })cpp");91 92 // FIXME: Move declaration below bar to make it visible.93 Header.clear();94 EXPECT_UNAVAILABLE(R"cpp(95 void foo();96 void bar();97 98 void fo^o() {99 bar();100 })cpp");101 102 // Order doesn't matter within a class.103 EXPECT_AVAILABLE(R"cpp(104 class Bar {105 void foo();106 void bar();107 };108 109 void Bar::fo^o() {110 bar();111 })cpp");112 113 // FIXME: Perform include insertion to make symbol visible.114 ExtraFiles["a.h"] = "void bar();";115 Header = "void foo(int test);";116 EXPECT_UNAVAILABLE(R"cpp(117 #include "a.h"118 void fo^o(int baz) {119 int x = 10;120 bar();121 })cpp");122}123 124TEST_F(DefineInlineTest, TemplateSpec) {125 EXPECT_UNAVAILABLE(R"cpp(126 template <typename T> void foo();127 template<> void foo<char>();128 129 template<> void f^oo<int>() {130 })cpp");131 EXPECT_UNAVAILABLE(R"cpp(132 template <typename T> void foo();133 134 template<> void f^oo<int>() {135 })cpp");136 EXPECT_UNAVAILABLE(R"cpp(137 template <typename T> struct Foo { void foo(); };138 139 template <typename T> void Foo<T>::f^oo() {140 })cpp");141 EXPECT_AVAILABLE(R"cpp(142 template <typename T> void foo();143 void bar();144 template <> void foo<int>();145 146 template<> void f^oo<int>() {147 bar();148 })cpp");149 EXPECT_UNAVAILABLE(R"cpp(150 namespace bar {151 template <typename T> void f^oo() {}152 template void foo<int>();153 })cpp");154}155 156TEST_F(DefineInlineTest, CheckForCanonDecl) {157 EXPECT_UNAVAILABLE(R"cpp(158 void foo();159 160 void bar() {}161 void f^oo() {162 // This bar normally refers to the definition just above, but it is not163 // visible from the forward declaration of foo.164 bar();165 })cpp");166 // Make it available with a forward decl.167 EXPECT_AVAILABLE(R"cpp(168 void bar();169 void foo();170 171 void bar() {}172 void f^oo() {173 bar();174 })cpp");175}176 177TEST_F(DefineInlineTest, UsingShadowDecls) {178 EXPECT_UNAVAILABLE(R"cpp(179 namespace ns1 { void foo(int); }180 namespace ns2 { void foo(int*); }181 template <typename T>182 void bar();183 184 using ns1::foo;185 using ns2::foo;186 187 template <typename T>188 void b^ar() {189 foo(T());190 })cpp");191}192 193TEST_F(DefineInlineTest, TransformNestedNamespaces) {194 auto *Test = R"cpp(195 namespace a {196 void bar();197 namespace b {198 void baz();199 namespace c {200 void aux();201 }202 }203 }204 205 void foo();206 using namespace a;207 using namespace b;208 using namespace c;209 void f^oo() {210 bar();211 a::bar();212 213 baz();214 b::baz();215 a::b::baz();216 217 aux();218 c::aux();219 b::c::aux();220 a::b::c::aux();221 })cpp";222 auto *Expected = R"cpp(223 namespace a {224 void bar();225 namespace b {226 void baz();227 namespace c {228 void aux();229 }230 }231 }232 233 void foo(){234 a::bar();235 a::bar();236 237 a::b::baz();238 a::b::baz();239 a::b::baz();240 241 a::b::c::aux();242 a::b::c::aux();243 a::b::c::aux();244 a::b::c::aux();245 }246 using namespace a;247 using namespace b;248 using namespace c;249 )cpp";250 EXPECT_EQ(apply(Test), Expected);251}252 253TEST_F(DefineInlineTest, TransformUsings) {254 auto *Test = R"cpp(255 namespace a { namespace b { namespace c { void aux(); } } }256 257 void foo();258 void f^oo() {259 using namespace a;260 using namespace b;261 using namespace c;262 using c::aux;263 namespace d = c;264 })cpp";265 auto *Expected = R"cpp(266 namespace a { namespace b { namespace c { void aux(); } } }267 268 void foo(){269 using namespace a;270 using namespace a::b;271 using namespace a::b::c;272 using a::b::c::aux;273 namespace d = a::b::c;274 }275 )cpp";276 EXPECT_EQ(apply(Test), Expected);277}278 279TEST_F(DefineInlineTest, TransformDecls) {280 auto *Test = R"cpp(281 void foo();282 void f^oo() {283 class Foo {284 public:285 void foo();286 int x;287 };288 289 enum En { Zero, One };290 En x = Zero;291 292 enum class EnClass { Zero, One };293 EnClass y = EnClass::Zero;294 })cpp";295 auto *Expected = R"cpp(296 void foo(){297 class Foo {298 public:299 void foo();300 int x;301 };302 303 enum En { Zero, One };304 En x = Zero;305 306 enum class EnClass { Zero, One };307 EnClass y = EnClass::Zero;308 }309 )cpp";310 EXPECT_EQ(apply(Test), Expected);311}312 313TEST_F(DefineInlineTest, TransformTemplDecls) {314 auto *Test = R"cpp(315 namespace a {316 template <typename T> class Bar {317 public:318 void bar();319 };320 template <typename T> T bar;321 template <typename T> void aux() {}322 }323 324 void foo();325 326 using namespace a;327 void f^oo() {328 bar<Bar<int>>.bar();329 aux<Bar<int>>();330 })cpp";331 auto *Expected = R"cpp(332 namespace a {333 template <typename T> class Bar {334 public:335 void bar();336 };337 template <typename T> T bar;338 template <typename T> void aux() {}339 }340 341 void foo(){342 a::bar<a::Bar<int>>.bar();343 a::aux<a::Bar<int>>();344 }345 346 using namespace a;347 )cpp";348 EXPECT_EQ(apply(Test), Expected);349}350 351TEST_F(DefineInlineTest, TransformMembers) {352 auto *Test = R"cpp(353 class Foo {354 void foo();355 };356 357 void Foo::f^oo() {358 return;359 })cpp";360 auto *Expected = R"cpp(361 class Foo {362 void foo(){363 return;364 }365 };366 367 )cpp";368 EXPECT_EQ(apply(Test), Expected);369 370 ExtraFiles["a.h"] = R"cpp(371 class Foo {372 void foo();373 };)cpp";374 375 llvm::StringMap<std::string> EditedFiles;376 Test = R"cpp(377 #include "a.h"378 void Foo::f^oo() {379 return;380 })cpp";381 Expected = R"cpp(382 #include "a.h"383 )cpp";384 EXPECT_EQ(apply(Test, &EditedFiles), Expected);385 386 Expected = R"cpp(387 class Foo {388 void foo(){389 return;390 }391 };)cpp";392 EXPECT_THAT(EditedFiles,393 ElementsAre(FileWithContents(testPath("a.h"), Expected)));394}395 396TEST_F(DefineInlineTest, TransformDependentTypes) {397 auto *Test = R"cpp(398 namespace a {399 template <typename T> class Bar {};400 }401 402 template <typename T>403 void foo();404 405 using namespace a;406 template <typename T>407 void f^oo() {408 Bar<T> B;409 Bar<Bar<T>> q;410 })cpp";411 auto *Expected = R"cpp(412 namespace a {413 template <typename T> class Bar {};414 }415 416 template <typename T>417 void foo(){418 a::Bar<T> B;419 a::Bar<a::Bar<T>> q;420 }421 422 using namespace a;423 )cpp";424 425 EXPECT_EQ(apply(Test), Expected);426}427 428TEST_F(DefineInlineTest, TransformFunctionTempls) {429 // Check we select correct specialization decl.430 std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {431 {R"cpp(432 template <typename T>433 void foo(T p);434 435 template <>436 void foo<int>(int p);437 438 template <>439 void foo<char>(char p);440 441 template <>442 void fo^o<int>(int p) {443 return;444 })cpp",445 R"cpp(446 template <typename T>447 void foo(T p);448 449 template <>450 void foo<int>(int p){451 return;452 }453 454 template <>455 void foo<char>(char p);456 457 )cpp"},458 {// Make sure we are not selecting the first specialization all the time.459 R"cpp(460 template <typename T>461 void foo(T p);462 463 template <>464 void foo<int>(int p);465 466 template <>467 void foo<char>(char p);468 469 template <>470 void fo^o<char>(char p) {471 return;472 })cpp",473 R"cpp(474 template <typename T>475 void foo(T p);476 477 template <>478 void foo<int>(int p);479 480 template <>481 void foo<char>(char p){482 return;483 }484 485 )cpp"},486 {R"cpp(487 template <typename T>488 void foo(T p);489 490 template <>491 void foo<int>(int p);492 493 template <typename T>494 void fo^o(T p) {495 return;496 })cpp",497 R"cpp(498 template <typename T>499 void foo(T p){500 return;501 }502 503 template <>504 void foo<int>(int p);505 506 )cpp"},507 };508 for (const auto &Case : Cases)509 EXPECT_EQ(apply(Case.first), Case.second) << Case.first;510}511 512TEST_F(DefineInlineTest, TransformTypeLocs) {513 auto *Test = R"cpp(514 namespace a {515 template <typename T> class Bar {516 public:517 template <typename Q> class Baz {};518 };519 class Foo{};520 }521 522 void foo();523 524 using namespace a;525 void f^oo() {526 Bar<int> B;527 Foo foo;528 a::Bar<Bar<int>>::Baz<Bar<int>> q;529 })cpp";530 auto *Expected = R"cpp(531 namespace a {532 template <typename T> class Bar {533 public:534 template <typename Q> class Baz {};535 };536 class Foo{};537 }538 539 void foo(){540 a::Bar<int> B;541 a::Foo foo;542 a::Bar<a::Bar<int>>::Baz<a::Bar<int>> q;543 }544 545 using namespace a;546 )cpp";547 EXPECT_EQ(apply(Test), Expected);548}549 550TEST_F(DefineInlineTest, TransformDeclRefs) {551 auto *Test = R"cpp(552 namespace a {553 template <typename T> class Bar {554 public:555 void foo();556 static void bar();557 int x;558 static int y;559 };560 void bar();561 void test();562 }563 564 void foo();565 using namespace a;566 void f^oo() {567 a::Bar<int> B;568 B.foo();569 a::bar();570 Bar<Bar<int>>::bar();571 a::Bar<int>::bar();572 B.x = Bar<int>::y;573 Bar<int>::y = 3;574 bar();575 a::test();576 })cpp";577 auto *Expected = R"cpp(578 namespace a {579 template <typename T> class Bar {580 public:581 void foo();582 static void bar();583 int x;584 static int y;585 };586 void bar();587 void test();588 }589 590 void foo(){591 a::Bar<int> B;592 B.foo();593 a::bar();594 a::Bar<a::Bar<int>>::bar();595 a::Bar<int>::bar();596 B.x = a::Bar<int>::y;597 a::Bar<int>::y = 3;598 a::bar();599 a::test();600 }601 using namespace a;602 )cpp";603 EXPECT_EQ(apply(Test), Expected);604}605 606TEST_F(DefineInlineTest, StaticMembers) {607 auto *Test = R"cpp(608 namespace ns { class X { static void foo(); void bar(); }; }609 void ns::X::b^ar() {610 foo();611 })cpp";612 auto *Expected = R"cpp(613 namespace ns { class X { static void foo(); void bar(){614 foo();615 } }; }616 )cpp";617 EXPECT_EQ(apply(Test), Expected);618}619 620TEST_F(DefineInlineTest, TransformParamNames) {621 std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {622 {R"cpp(623 void foo(int, bool b, int T\624est);625 void ^foo(int f, bool x, int z) {})cpp",626 R"cpp(627 void foo(int f, bool x, int z){}628 )cpp"},629 {R"cpp(630 #define PARAM int Z631 void foo(PARAM);632 633 void ^foo(int X) {})cpp",634 "fail: Cant rename parameter inside macro body."},635 {R"cpp(636 #define TYPE int637 #define PARAM TYPE Z638 #define BODY(x) 5 * (x) + 2639 template <int P>640 void foo(PARAM, TYPE Q, TYPE, TYPE W = BODY(P));641 template <int x>642 void ^foo(int Z, int b, int c, int d) {})cpp",643 R"cpp(644 #define TYPE int645 #define PARAM TYPE Z646 #define BODY(x) 5 * (x) + 2647 template <int x>648 void foo(PARAM, TYPE b, TYPE c, TYPE d = BODY(x)){}649 )cpp"},650 };651 for (const auto &Case : Cases)652 EXPECT_EQ(apply(Case.first), Case.second) << Case.first;653}654 655TEST_F(DefineInlineTest, TransformTemplParamNames) {656 auto *Test = R"cpp(657 struct Foo {658 struct Bar {659 template <class, class X,660 template<typename> class, template<typename> class Y,661 int, int Z>662 void foo(X, Y<X>, int W = 5 * Z + 2);663 };664 };665 666 template <class T, class U,667 template<typename> class V, template<typename> class W,668 int X, int Y>669 void Foo::Bar::f^oo(U, W<U>, int Q) {})cpp";670 auto *Expected = R"cpp(671 struct Foo {672 struct Bar {673 template <class T, class U,674 template<typename> class V, template<typename> class W,675 int X, int Y>676 void foo(U, W<U>, int Q = 5 * Y + 2){}677 };678 };679 680 )cpp";681 EXPECT_EQ(apply(Test), Expected);682}683 684TEST_F(DefineInlineTest, TransformInlineNamespaces) {685 auto *Test = R"cpp(686 namespace a { inline namespace b { namespace { struct Foo{}; } } }687 void foo();688 689 using namespace a;690 void ^foo() {Foo foo;})cpp";691 auto *Expected = R"cpp(692 namespace a { inline namespace b { namespace { struct Foo{}; } } }693 void foo(){a::Foo foo;}694 695 using namespace a;696 )cpp";697 EXPECT_EQ(apply(Test), Expected);698}699 700TEST_F(DefineInlineTest, TokensBeforeSemicolon) {701 std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {702 {R"cpp(703 void foo() /*Comment -_-*/ /*Com 2*/ ;704 void fo^o() { return ; })cpp",705 R"cpp(706 void foo() /*Comment -_-*/ /*Com 2*/ { return ; }707 )cpp"},708 709 {R"cpp(710 void foo();711 void fo^o() { return ; })cpp",712 R"cpp(713 void foo(){ return ; }714 )cpp"},715 716 {R"cpp(717 #define SEMI ;718 void foo() SEMI719 void fo^o() { return ; })cpp",720 "fail: Couldn't find semicolon for target declaration."},721 };722 for (const auto &Case : Cases)723 EXPECT_EQ(apply(Case.first), Case.second) << Case.first;724}725 726TEST_F(DefineInlineTest, HandleMacros) {727 EXPECT_UNAVAILABLE(R"cpp(728 #define BODY { return; }729 void foo();730 void f^oo()BODY)cpp");731 732 EXPECT_UNAVAILABLE(R"cpp(733 #define BODY void foo(){ return; }734 void foo();735 [[BODY]])cpp");736 737 std::pair<llvm::StringRef, llvm::StringRef> Cases[] = {738 // We don't qualify declarations coming from macros.739 {R"cpp(740 #define BODY Foo741 namespace a { class Foo{}; }742 void foo();743 using namespace a;744 void f^oo(){BODY();})cpp",745 R"cpp(746 #define BODY Foo747 namespace a { class Foo{}; }748 void foo(){BODY();}749 using namespace a;750 )cpp"},751 752 // Macro is not visible at declaration location, but we proceed.753 {R"cpp(754 void foo();755 #define BODY return;756 void f^oo(){BODY})cpp",757 R"cpp(758 void foo(){BODY}759 #define BODY return;760 )cpp"},761 762 {R"cpp(763 #define TARGET void foo()764 TARGET;765 void f^oo(){ return; })cpp",766 R"cpp(767 #define TARGET void foo()768 TARGET{ return; }769 )cpp"},770 771 {R"cpp(772 #define TARGET foo773 void TARGET();774 void f^oo(){ return; })cpp",775 R"cpp(776 #define TARGET foo777 void TARGET(){ return; }778 )cpp"},779 };780 for (const auto &Case : Cases)781 EXPECT_EQ(apply(Case.first), Case.second) << Case.first;782}783 784TEST_F(DefineInlineTest, DropCommonNameSpecifiers) {785 struct {786 llvm::StringRef Test;787 llvm::StringRef Expected;788 } Cases[] = {789 {R"cpp(790 namespace a { namespace b { void aux(); } }791 namespace ns1 {792 void foo();793 namespace qq { void test(); }794 namespace ns2 {795 void bar();796 namespace ns3 { void baz(); }797 }798 }799 800 using namespace a;801 using namespace a::b;802 using namespace ns1::qq;803 void ns1::ns2::ns3::b^az() {804 foo();805 bar();806 baz();807 ns1::ns2::ns3::baz();808 aux();809 test();810 })cpp",811 R"cpp(812 namespace a { namespace b { void aux(); } }813 namespace ns1 {814 void foo();815 namespace qq { void test(); }816 namespace ns2 {817 void bar();818 namespace ns3 { void baz(){819 foo();820 bar();821 baz();822 ns1::ns2::ns3::baz();823 a::b::aux();824 qq::test();825 } }826 }827 }828 829 using namespace a;830 using namespace a::b;831 using namespace ns1::qq;832 )cpp"},833 {R"cpp(834 namespace ns1 {835 namespace qq { struct Foo { struct Bar {}; }; using B = Foo::Bar; }836 namespace ns2 { void baz(); }837 }838 839 using namespace ns1::qq;840 void ns1::ns2::b^az() { Foo f; B b; })cpp",841 R"cpp(842 namespace ns1 {843 namespace qq { struct Foo { struct Bar {}; }; using B = Foo::Bar; }844 namespace ns2 { void baz(){ qq::Foo f; qq::B b; } }845 }846 847 using namespace ns1::qq;848 )cpp"},849 {R"cpp(850 namespace ns1 {851 namespace qq {852 template<class T> struct Foo { template <class U> struct Bar {}; };853 template<class T, class U>854 using B = typename Foo<T>::template Bar<U>;855 }856 namespace ns2 { void baz(); }857 }858 859 using namespace ns1::qq;860 void ns1::ns2::b^az() { B<int, bool> b; })cpp",861 R"cpp(862 namespace ns1 {863 namespace qq {864 template<class T> struct Foo { template <class U> struct Bar {}; };865 template<class T, class U>866 using B = typename Foo<T>::template Bar<U>;867 }868 namespace ns2 { void baz(){ qq::B<int, bool> b; } }869 }870 871 using namespace ns1::qq;872 )cpp"},873 };874 for (const auto &Case : Cases)875 EXPECT_EQ(apply(Case.Test), Case.Expected) << Case.Test;876}877 878TEST_F(DefineInlineTest, QualifyWithUsingDirectives) {879 llvm::StringRef Test = R"cpp(880 namespace a {881 void bar();882 namespace b { struct Foo{}; void aux(); }883 namespace c { void cux(); }884 }885 using namespace a;886 using X = b::Foo;887 void foo();888 889 using namespace b;890 using namespace c;891 void ^foo() {892 cux();893 bar();894 X x;895 aux();896 using namespace c;897 // FIXME: The last reference to cux() in body of foo should not be898 // qualified, since there is a using directive inside the function body.899 cux();900 })cpp";901 llvm::StringRef Expected = R"cpp(902 namespace a {903 void bar();904 namespace b { struct Foo{}; void aux(); }905 namespace c { void cux(); }906 }907 using namespace a;908 using X = b::Foo;909 void foo(){910 c::cux();911 bar();912 X x;913 b::aux();914 using namespace c;915 // FIXME: The last reference to cux() in body of foo should not be916 // qualified, since there is a using directive inside the function body.917 c::cux();918 }919 920 using namespace b;921 using namespace c;922 )cpp";923 EXPECT_EQ(apply(Test), Expected) << Test;924}925 926TEST_F(DefineInlineTest, AddInline) {927 llvm::StringMap<std::string> EditedFiles;928 ExtraFiles["a.h"] = "void foo();";929 apply(R"cpp(#include "a.h"930 void fo^o() {})cpp",931 &EditedFiles);932 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(933 testPath("a.h"), "inline void foo(){}")));934 935 // Check we put inline before cv-qualifiers.936 ExtraFiles["a.h"] = "const int foo();";937 apply(R"cpp(#include "a.h"938 const int fo^o() { return 0; })cpp",939 &EditedFiles);940 EXPECT_THAT(EditedFiles,941 testing::ElementsAre(FileWithContents(942 testPath("a.h"), "inline const int foo(){ return 0; }")));943 944 // No double inline.945 ExtraFiles["a.h"] = "inline void foo();";946 apply(R"cpp(#include "a.h"947 inline void fo^o() {})cpp",948 &EditedFiles);949 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(950 testPath("a.h"), "inline void foo(){}")));951 952 // Constexprs don't need "inline".953 ExtraFiles["a.h"] = "constexpr void foo();";954 apply(R"cpp(#include "a.h"955 constexpr void fo^o() {})cpp",956 &EditedFiles);957 EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(958 testPath("a.h"), "constexpr void foo(){}")));959 960 // Class members don't need "inline".961 ExtraFiles["a.h"] = "struct Foo { void foo(); };";962 apply(R"cpp(#include "a.h"963 void Foo::fo^o() {})cpp",964 &EditedFiles);965 EXPECT_THAT(EditedFiles,966 testing::ElementsAre(FileWithContents(967 testPath("a.h"), "struct Foo { void foo(){} };")));968 969 // Function template doesn't need to be "inline"d.970 ExtraFiles["a.h"] = "template <typename T> void foo();";971 apply(R"cpp(#include "a.h"972 template <typename T>973 void fo^o() {})cpp",974 &EditedFiles);975 EXPECT_THAT(EditedFiles,976 testing::ElementsAre(FileWithContents(977 testPath("a.h"), "template <typename T> void foo(){}")));978 979 // Specializations needs to be marked "inline".980 ExtraFiles["a.h"] = R"cpp(981 template <typename T> void foo();982 template <> void foo<int>();)cpp";983 apply(R"cpp(#include "a.h"984 template <>985 void fo^o<int>() {})cpp",986 &EditedFiles);987 EXPECT_THAT(EditedFiles,988 testing::ElementsAre(FileWithContents(testPath("a.h"),989 R"cpp(990 template <typename T> void foo();991 template <> inline void foo<int>(){})cpp")));992}993 994} // namespace995} // namespace clangd996} // namespace clang997