diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp @@ -234,6 +234,510 @@ } } +TEST(FoldingRanges, ControlFlow) { + const char *Tests[] = { + // If. + R"cpp( + int main() {[[ + bool B = true; + int I = 0; + char C = 'z'; + + if ([[B && I > 42 || ~([[++I]])]]) {[[ + ++I; + ]]} else {[[ + B = false; + ]]} + + if ([[B && ([[!B]]) || !([[([[C == 'a']])]])]]) {[[ + ++I; + ]]} else if ([[!B]]) {[[ + --I; + ]]} else {[[ + C = 'a'; + ]]} + ]]} + )cpp", + // While. + R"cpp( + int main() {[[ + bool B; + while ([[B]]) {[[ + B = !B; + ]]} + + do {[[ + B = !B; + ]]} while ([[B]]); + ]]} + )cpp", + // For. + R"cpp( + int main() {[[ + for ([[[[int I = 0]];[[I < 42]];[[++I]]]]) {[[ + --I; + ]]} + ]]} + )cpp", + // Switch. + R"cpp( + void noop(); + + int main() {[[ + int i = 2; + switch ([[i]]) {[[ + case 1:[[ noop();]] + case 2:[[ noop(); //[[execution starts at this case label]]]] + case 3:[[ noop();]] + case 4: + case 5:[[ noop(); + break; //[[execution of subsequent statements is terminated]]]] + case 6:[[ noop();]] + ]]} + ]]} + )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; + } +} + +TEST(FoldingRanges, Misc) { + const char *Tests[] = { + // Statement groups. + R"cpp( + int main() {[[ + [[int X = 5; + int Y = 42; + bool B = 15;]] + if ([[B]]) {[[ ++X; ]]} + unsigned U = 9000; + ]]} + )cpp", + // Enum. + R"cpp( + enum Color {[[ + Green = 0, + YInMn, + Orange, + ]]}; + )cpp", + // Argument lists. + R"cpp( + int foo([[char C, bool B]]) { return static_cast(C); } + + foo([['z', true]]); + + struct Foo { + Foo([[int I, unsigned U, bool B=true]]) {} + }; + + Foo F = Foo([[/*[[I=]]*/1, /*[[U=]]*/2, /*[[B=]]false]]); + F = Foo([[1, 2]]); + )cpp", + // Namespace. + R"cpp( + namespace ns {[[ + int Variable = 42; + namespace nested {[[ + int NestedVariable = 50; + ]]} + ]]} + + namespace a {[[ + namespace b {[[ + namespace c {[[ + + ]]} //[[ namespacee c]] + ]]} //[[ namespacee b]] + ]]} //[[ namespacee a]] + + namespace modern::ns::syntax {[[ + ]]} //[[ namespace modern::ns::syntax]] + + namespace {[[ + ]]} //[[ namespace]] + )cpp", + // Strings. + R"cpp( + std::string String = "[[ShortString]]"; + String = "[[Suuuuuuuuuuuuuuuuuper Looooooooooooooooong String]]"; + String = u8"[[Suuuuuuuuuuuuuuuuuper Looooooooooooooooong String]]"; + + String = R"raw([[Suuuuuuuuuuuuuuuuuper Looooooooooooooong String]])raw"; + + const char *text = + "[[This" + "is a multiline" + "string]]"; + + const char *text2 = + "[[Here is another \ + string \ + also \ + multilineeeeee]]"; + )cpp", + // Arrays. + R"cpp( + char Array[] = {[[ 'F', 'o', 'o', 'b', 'a', 'r', '\0' ]}; + + Array = {[[ 'F', 'o', 'o', 'b', 'a', 'r', 'F', 'o', 'o', 'b', + 'a', 'r', '\0' ]]}; + + int Nested[3][4] = {[[{[[0,1,2,3]]}, {[[4,5,6,7]]}, {[[8,9,10,11]]}}; + )cpp", + // Templates. + R"cpp( + template <[[typename T, typename U, typename V, int X, char C, bool B]]> + class Foo {}; + + template <[[bool B]]> + class Foo<[[unsigned, unsigned, unsigned, -42, 'z', B]]> { + int Specialization; + }; + + template <> + class Foo<[[unsigned, unsigned, unsigned, -42, 'z', false]]> { + int Specialization; + }; + + Foo<[[char, int, bool, 42, 'x', false]]> F; + + template <[[typename T, unsigned U]]> + void foo([[T t, unsigned u]]) { return; } + + template <[[unsigned U]]> + void foo([[char t, U u]]) {} + + template <> + void foo([[char t, U u]]) {} + )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; + } +} + +TEST(FoldingRanges, Comments) { + const char *Tests[] = { + R"cpp( + //[[ This is a one-line comment.]] + )cpp", + R"cpp( + //[[ Continuous + // Comment + // ...]] + + //[[ And another one + // But doesn't belong to the one above]] + + //[[ This time + // + // It's connected!]] + + ///[[ Doxygen]] + + ///[[ Doxygen + /// With more multiple lines. + /// + /// + /// The End]] + )cpp", + R"cpp( + /*[[ C-Style comment. ]]*/ + + [[/* Spanning + * multiple + * lines */]] + )cpp", + R"cpp( + [[/* C-Style + * multiple + * lines */]] + //[[ Followed by C++ Style + // Are still different comments!]] + )cpp", + R"cpp( + /*[[! + * ... text ... + ]]*/ + + /*[[* + * ... text ... + ]]*/ + + /*[[! + ... text ... + ]]*/ + + ///[[ + /// ... text ... + ///]] + + //[[! + //!... text ... + //!]] + + /*[[*******************************************//** + * ... text + **********************************************]]*/ + + //[[/////////////////////////////////////////////// + /// ... text ... + /////////////////////////////////////////////////]] + + /*[[*********************************************** + * ... text + **********************************************]]*/ + )cpp", + R"cpp( + /*[[ + Tricky C-Style + + void Foo(int I); + void Foo(int I, char C); + /*]]*/ + )cpp", + R"cpp( + void Foo([[int I, bool Boolean=false]]) {[[ + return; //[[ Do nothing, just return!]] + ]]} + + const auto Variable = Foo([[42, /*[[Boolean=]]*/]]true]]); + )cpp", + R"cpp( + //[[ Some documentation. + // @param + void Foo([[int I, bool Boolean=false]]) /*[[This is a function]]*// {[[ //[[ With a comment]] + return; //[[ Do nothing, just return!]] + ]]} + + const auto Variable = Foo([[42, /*[[Boolean=]]*/]]true]]); + )cpp", + R"cpp( + //[[ Empty comments don't generate folding ranges!]] + + // + /**/ + /// + )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; + } +} + +TEST(FoldingRanges, Preprocessor) { + const char *Tests[] = { + R"cpp( + #include [[ + #include + #include ]] + + #include [[]] + + #include "external/Logger.h" + #include "external/Vector.h"]] + #include [["project/DataStructures/Trie.h" + + #include [["project/File.h" + #include ]] + #include + + #include [["math.h"]] + )cpp", + R"cpp( + #define true [[false]] + + #define getmax(a,b) [[((a)>(b)?(a):(b))]] + #define PI [[3.14159]] + )cpp", + R"cpp( + #ifndef [[FOO_H_INCLUDED /*[[ any name uniquely mapped to file name ]]*/ + #define FOO_H_INCLUDED + //[[ ^ Don't fold simple definition?]] + + //[[ contents of the file are here]] + #endif]] + + #ifndef LIBRARY_FILENAME_H [[ + #define LIBRARY_FILENAME_H + //[[ contents of the header]] + #endif]] /*[[ LIBRARY_FILENAME_H ]]*/ + + #if [[__has_include() + # include + # define have_optional 1 + #]]elif __has_include() [[]] + # include + # define have_optional 1 + # define experimental_optional 1 + #]]else[[ + # define have_optional 0 + #endif]] + + #if [[OK + # define NICE + #]]else[[ + # define true [[false]] + #endif]] + + #ifdef SOMETHING[[ + //[[ Oh no!]] + #undef SOMETHING + #]]else[[ + #define OKAY_GOOD + #endif]] + )cpp", + R"cpp( + #error [["Not a standard compliant compiler"]] + + # error [["Another error"]] + + #error [["Subsequent"]] + #error [["Errrors"]] + )cpp", + R"cpp( + #line [[777 FNAME]] + + #pragma STDC [[FENV_ACCESS on]] + #pragma pack(2) + )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; + } +} + +TEST(FoldingRanges, Classes) { + const char *Tests[] = { + // Basics. + R"cpp( + class Foo {[[ + int I; + ]]}; + + struct Bar {[[ + int I; + ]]}; + + class Baz {[[ + public:[[ + Foo(); + + int I;]] + ]]}; + + class Chewbacca {}; + class Vader; + + struct FooStruct {[[ + int Field; + ]]}; + struct BarStruct {[[]]}; + struct FwdDeclaration; + )cpp", + // Accessor secions. + R"cpp( + class Foo {[[ + public: + private: + protected: + public: + ]]}; + + class Bar {[[ + public:[[ + Bar(); + Bar([[int X]]); +]] + private:[[ + int Field;]] + protected:[[ + bool ProtectThis;]] + public:[[ + int getField() {[[ return Field; ]]}]] + ]]}; + )cpp", + // Nested classes with accessor sections. + R"cpp( + class Nested {[[ + public: + private:[[ + class Inner {[[ + public:[[ + Inner(); + Inner(int X); + ]] + private:[[ + int Field;]] + protected:[[ + bool ProtectThis;]] + public:[[ + int getField() {[[ return Field; ]]}]] + ]};]] + protected: + public: + ]]}; + + struct NestedStruct {[[ + public: + private:[[ + class Inner {[[ + public:[[ + Inner(); + Inner(int X); + ]] + private:[[ + int Field;]] + protected:[[ + bool ProtectThis;]] + public:[[ + int getField() {[[ return Field; ]]}]] + ]]};]] + protected: + public: + ]]}; + )cpp", + // Initializer lists. + R"cpp( + class Foo {[[ + public:[[ + Foo([[int Integer, char Char, bool B]]) :[[ I(Integer), C(Char), B(Boolean) ]] {[[ + int X; + ]]}]] + private:[[ + int I; + char C; + bool B;]] + ]]}; + )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