Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clangd/unittests/SelectionTests.cpp @ 165:597b3f1c2c93
fix call createTailCallEliminationPass
author | anatofuz |
---|---|
date | Tue, 24 Mar 2020 15:30:52 +0900 |
parents | 1d019706d866 |
children | 0572611fdcc8 |
line wrap: on
line source
//===-- SelectionTests.cpp - ----------------------------------------------===// // // 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 "Selection.h" #include "SourceCode.h" #include "TestTU.h" #include "clang/AST/Decl.h" #include "llvm/Support/Casting.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace clang { namespace clangd { namespace { using ::testing::UnorderedElementsAreArray; SelectionTree makeSelectionTree(const StringRef MarkedCode, ParsedAST &AST) { Annotations Test(MarkedCode); switch (Test.points().size()) { case 1: // Point selection. return SelectionTree(AST.getASTContext(), AST.getTokens(), cantFail(positionToOffset(Test.code(), Test.point()))); case 2: // Range selection. return SelectionTree( AST.getASTContext(), AST.getTokens(), cantFail(positionToOffset(Test.code(), Test.points()[0])), cantFail(positionToOffset(Test.code(), Test.points()[1]))); default: ADD_FAILURE() << "Expected 1-2 points for selection.\n" << MarkedCode; return SelectionTree(AST.getASTContext(), AST.getTokens(), 0u, 0u); } } Range nodeRange(const SelectionTree::Node *N, ParsedAST &AST) { if (!N) return Range{}; const SourceManager &SM = AST.getSourceManager(); const LangOptions &LangOpts = AST.getLangOpts(); StringRef Buffer = SM.getBufferData(SM.getMainFileID()); if (llvm::isa_and_nonnull<TranslationUnitDecl>(N->ASTNode.get<Decl>())) return Range{Position{}, offsetToPosition(Buffer, Buffer.size())}; auto FileRange = toHalfOpenFileRange(SM, LangOpts, N->ASTNode.getSourceRange()); assert(FileRange && "We should be able to get the File Range"); return Range{ offsetToPosition(Buffer, SM.getFileOffset(FileRange->getBegin())), offsetToPosition(Buffer, SM.getFileOffset(FileRange->getEnd()))}; } std::string nodeKind(const SelectionTree::Node *N) { return N ? N->kind() : "<null>"; } std::vector<const SelectionTree::Node *> allNodes(const SelectionTree &T) { std::vector<const SelectionTree::Node *> Result = {&T.root()}; for (unsigned I = 0; I < Result.size(); ++I) { const SelectionTree::Node *N = Result[I]; Result.insert(Result.end(), N->Children.begin(), N->Children.end()); } return Result; } // Returns true if Common is a descendent of Root. // Verifies nothing is selected above Common. bool verifyCommonAncestor(const SelectionTree::Node &Root, const SelectionTree::Node *Common, StringRef MarkedCode) { if (&Root == Common) return true; if (Root.Selected) ADD_FAILURE() << "Selected nodes outside common ancestor\n" << MarkedCode; bool Seen = false; for (const SelectionTree::Node *Child : Root.Children) if (verifyCommonAncestor(*Child, Common, MarkedCode)) { if (Seen) ADD_FAILURE() << "Saw common ancestor twice\n" << MarkedCode; Seen = true; } return Seen; } TEST(SelectionTest, CommonAncestor) { struct Case { // Selection is between ^marks^. // common ancestor marked with a [[range]]. const char *Code; const char *CommonAncestorKind; }; Case Cases[] = { { R"cpp( template <typename T> int x = [[T::^U::]]ccc(); )cpp", "NestedNameSpecifierLoc", }, { R"cpp( struct AAA { struct BBB { static int ccc(); };}; int x = AAA::[[B^B^B]]::ccc(); )cpp", "RecordTypeLoc", }, { R"cpp( struct AAA { struct BBB { static int ccc(); };}; int x = AAA::[[B^BB^]]::ccc(); )cpp", "RecordTypeLoc", }, { R"cpp( struct AAA { struct BBB { static int ccc(); };}; int x = [[AAA::BBB::c^c^c]](); )cpp", "DeclRefExpr", }, { R"cpp( struct AAA { struct BBB { static int ccc(); };}; int x = [[AAA::BBB::cc^c(^)]]; )cpp", "CallExpr", }, { R"cpp( void foo() { [[if (1^11) { return; } else {^ }]] } )cpp", "IfStmt", }, { R"cpp( int x(int); #define M(foo) x(foo) int a = 42; int b = M([[^a]]); )cpp", "DeclRefExpr", }, { R"cpp( void foo(); #define CALL_FUNCTION(X) X() void bar() { CALL_FUNCTION([[f^o^o]]); } )cpp", "DeclRefExpr", }, { R"cpp( void foo(); #define CALL_FUNCTION(X) X() void bar() { [[CALL_FUNC^TION(fo^o)]]; } )cpp", "CallExpr", }, { R"cpp( void foo(); #define CALL_FUNCTION(X) X() void bar() { [[C^ALL_FUNC^TION(foo)]]; } )cpp", "CallExpr", }, { R"cpp( void foo(); #define CALL_FUNCTION(X) X^()^ void bar() { CALL_FUNCTION(foo); } )cpp", nullptr, }, { R"cpp( struct S { S(const char*); }; S [[s ^= "foo"]]; )cpp", "CXXConstructExpr", }, { R"cpp( struct S { S(const char*); }; [[S ^s = "foo"]]; )cpp", "VarDecl", }, { R"cpp( [[^void]] (*S)(int) = nullptr; )cpp", "BuiltinTypeLoc", }, { R"cpp( [[void (*S)^(int)]] = nullptr; )cpp", "FunctionProtoTypeLoc", }, { R"cpp( [[void (^*S)(int)]] = nullptr; )cpp", "FunctionProtoTypeLoc", }, { R"cpp( [[void (*^S)(int) = nullptr]]; )cpp", "VarDecl", }, { R"cpp( [[void ^(*S)(int)]] = nullptr; )cpp", "FunctionProtoTypeLoc", }, { R"cpp( struct S { int foo() const; int bar() { return [[f^oo]](); } }; )cpp", "MemberExpr", // Not implicit CXXThisExpr, or its implicit cast! }, { R"cpp( auto lambda = [](const char*){ return 0; }; int x = lambda([["y^"]]); )cpp", "StringLiteral", // Not DeclRefExpr to operator()! }, // Point selections. {"void foo() { [[^foo]](); }", "DeclRefExpr"}, {"void foo() { [[f^oo]](); }", "DeclRefExpr"}, {"void foo() { [[fo^o]](); }", "DeclRefExpr"}, {"void foo() { [[foo^()]]; }", "CallExpr"}, {"void foo() { [[foo^]] (); }", "DeclRefExpr"}, {"int bar; void foo() [[{ foo (); }]]^", "CompoundStmt"}, {"int x = [[42]]^;", "IntegerLiteral"}, // Ignores whitespace, comments, and semicolons in the selection. {"void foo() { [[foo^()]]; /*comment*/^}", "CallExpr"}, // Tricky case: FunctionTypeLoc in FunctionDecl has a hole in it. {"[[^void]] foo();", "BuiltinTypeLoc"}, {"[[void foo^()]];", "FunctionProtoTypeLoc"}, {"[[^void foo^()]];", "FunctionDecl"}, {"[[void ^foo()]];", "FunctionDecl"}, // Tricky case: two VarDecls share a specifier. {"[[int ^a]], b;", "VarDecl"}, {"[[int a, ^b]];", "VarDecl"}, // Tricky case: CXXConstructExpr wants to claim the whole init range. { R"cpp( struct X { X(int); }; class Y { X x; Y() : [[^x(4)]] {} }; )cpp", "CXXCtorInitializer", // Not the CXXConstructExpr! }, // Tricky case: anonymous struct is a sibling of the VarDecl. {"[[st^ruct {int x;}]] y;", "CXXRecordDecl"}, {"[[struct {int x;} ^y]];", "VarDecl"}, {"struct {[[int ^x]];} y;", "FieldDecl"}, // FIXME: the AST has no location info for qualifiers. {"const [[a^uto]] x = 42;", "AutoTypeLoc"}, {"[[co^nst auto x = 42]];", "VarDecl"}, {"^", nullptr}, {"void foo() { [[foo^^]] (); }", "DeclRefExpr"}, // FIXME: Ideally we'd get a declstmt or the VarDecl itself here. // This doesn't happen now; the RAV doesn't traverse a node containing ;. {"int x = 42;^", nullptr}, // Common ancestor is logically TUDecl, but we never return that. {"^int x; int y;^", nullptr}, // Node types that have caused problems in the past. {"template <typename T> void foo() { [[^T]] t; }", "TemplateTypeParmTypeLoc"}, // No crash { R"cpp( template <class T> struct Foo {}; template <[[template<class> class /*cursor here*/^U]]> struct Foo<U<int>*> {}; )cpp", "TemplateTemplateParmDecl"}, // Foreach has a weird AST, ensure we can select parts of the range init. // This used to fail, because the DeclStmt for C claimed the whole range. { R"cpp( struct Str { const char *begin(); const char *end(); }; Str makeStr(const char*); void loop() { for (const char C : [[mak^eStr("foo"^)]]) ; } )cpp", "CallExpr"}, // User-defined literals are tricky: is 12_i one token or two? // For now we treat it as one, and the UserDefinedLiteral as a leaf. { R"cpp( struct Foo{}; Foo operator""_ud(unsigned long long); Foo x = [[^12_ud]]; )cpp", "UserDefinedLiteral"}, { R"cpp( int a; decltype([[^a]] + a) b; )cpp", "DeclRefExpr"}, // Objective-C OpaqueValueExpr/PseudoObjectExpr has weird ASTs. // Need to traverse the contents of the OpaqueValueExpr to the POE, // and ensure we traverse only the syntactic form of the PseudoObjectExpr. { R"cpp( @interface I{} @property(retain) I*x; @property(retain) I*y; @end void test(I *f) { [[^f]].x.y = 0; } )cpp", "DeclRefExpr"}, { R"cpp( @interface I{} @property(retain) I*x; @property(retain) I*y; @end void test(I *f) { [[f.^x]].y = 0; } )cpp", "ObjCPropertyRefExpr"}, // Examples with implicit properties. { R"cpp( @interface I{} -(int)foo; @end int test(I *f) { return 42 + [[^f]].foo; } )cpp", "DeclRefExpr"}, { R"cpp( @interface I{} -(int)foo; @end int test(I *f) { return 42 + [[f.^foo]]; } )cpp", "ObjCPropertyRefExpr"}, }; for (const Case &C : Cases) { Annotations Test(C.Code); TestTU TU; TU.Code = std::string(Test.code()); // FIXME: Auto-completion in a template requires disabling delayed template // parsing. TU.ExtraArgs.push_back("-fno-delayed-template-parsing"); TU.ExtraArgs.push_back("-xobjective-c++"); auto AST = TU.build(); auto T = makeSelectionTree(C.Code, AST); EXPECT_EQ("TranslationUnitDecl", nodeKind(&T.root())) << C.Code; if (Test.ranges().empty()) { // If no [[range]] is marked in the example, there should be no selection. EXPECT_FALSE(T.commonAncestor()) << C.Code << "\n" << T; } else { // If there is an expected selection, common ancestor should exist // with the appropriate node type. EXPECT_EQ(C.CommonAncestorKind, nodeKind(T.commonAncestor())) << C.Code << "\n" << T; // Convert the reported common ancestor to a range and verify it. EXPECT_EQ(nodeRange(T.commonAncestor(), AST), Test.range()) << C.Code << "\n" << T; // Check that common ancestor is reachable on exactly one path from root, // and no nodes outside it are selected. EXPECT_TRUE(verifyCommonAncestor(T.root(), T.commonAncestor(), C.Code)) << C.Code; } } } // Regression test: this used to match the injected X, not the outer X. TEST(SelectionTest, InjectedClassName) { const char* Code = "struct ^X { int x; };"; auto AST = TestTU::withCode(Annotations(Code).code()).build(); auto T = makeSelectionTree(Code, AST); ASSERT_EQ("CXXRecordDecl", nodeKind(T.commonAncestor())) << T; auto *D = dyn_cast<CXXRecordDecl>(T.commonAncestor()->ASTNode.get<Decl>()); EXPECT_FALSE(D->isInjectedClassName()); } // FIXME: Doesn't select the binary operator node in // #define FOO(X) X + 1 // int a, b = [[FOO(a)]]; TEST(SelectionTest, Selected) { // Selection with ^marks^. // Partially selected nodes marked with a [[range]]. // Completely selected nodes marked with a $C[[range]]. const char *Cases[] = { R"cpp( int abc, xyz = [[^ab^c]]; )cpp", R"cpp( int abc, xyz = [[a^bc^]]; )cpp", R"cpp( int abc, xyz = $C[[^abc^]]; )cpp", R"cpp( void foo() { [[if ([[1^11]]) $C[[{ $C[[return]]; }]] else [[{^ }]]]] char z; } )cpp", R"cpp( template <class T> struct unique_ptr {}; void foo(^$C[[unique_ptr<$C[[unique_ptr<$C[[int]]>]]>]]^ a) {} )cpp", R"cpp(int a = [[5 >^> 1]];)cpp", R"cpp( #define ECHO(X) X ECHO(EC^HO($C[[int]]) EC^HO(a)); )cpp", R"cpp( $C[[^$C[[int]] a^]]; )cpp", R"cpp( $C[[^$C[[int]] a = $C[[5]]^]]; )cpp", }; for (const char *C : Cases) { Annotations Test(C); auto AST = TestTU::withCode(Test.code()).build(); auto T = makeSelectionTree(C, AST); std::vector<Range> Complete, Partial; for (const SelectionTree::Node *N : allNodes(T)) if (N->Selected == SelectionTree::Complete) Complete.push_back(nodeRange(N, AST)); else if (N->Selected == SelectionTree::Partial) Partial.push_back(nodeRange(N, AST)); EXPECT_THAT(Complete, UnorderedElementsAreArray(Test.ranges("C"))) << C; EXPECT_THAT(Partial, UnorderedElementsAreArray(Test.ranges())) << C; } } TEST(SelectionTest, PathologicalPreprocessor) { const char *Case = R"cpp( #define MACRO while(1) void test() { #include "Expand.inc" br^eak; } )cpp"; Annotations Test(Case); auto TU = TestTU::withCode(Test.code()); TU.AdditionalFiles["Expand.inc"] = "MACRO\n"; auto AST = TU.build(); EXPECT_THAT(AST.getDiagnostics(), ::testing::IsEmpty()); auto T = makeSelectionTree(Case, AST); EXPECT_EQ("BreakStmt", T.commonAncestor()->kind()); EXPECT_EQ("WhileStmt", T.commonAncestor()->Parent->kind()); } TEST(SelectionTest, IncludedFile) { const char *Case = R"cpp( void test() { #include "Exp^and.inc" break; } )cpp"; Annotations Test(Case); auto TU = TestTU::withCode(Test.code()); TU.AdditionalFiles["Expand.inc"] = "while(1)\n"; auto AST = TU.build(); auto T = makeSelectionTree(Case, AST); EXPECT_EQ("WhileStmt", T.commonAncestor()->kind()); } TEST(SelectionTest, MacroArgExpansion) { // If a macro arg is expanded several times, we consider them all selected. const char *Case = R"cpp( int mul(int, int); #define SQUARE(X) mul(X, X); int nine = SQUARE(^3); )cpp"; Annotations Test(Case); auto AST = TestTU::withCode(Test.code()).build(); auto T = makeSelectionTree(Case, AST); // Unfortunately, this makes the common ancestor the CallExpr... // FIXME: hack around this by picking one? EXPECT_EQ("CallExpr", T.commonAncestor()->kind()); EXPECT_FALSE(T.commonAncestor()->Selected); EXPECT_EQ(2u, T.commonAncestor()->Children.size()); for (const auto* N : T.commonAncestor()->Children) { EXPECT_EQ("IntegerLiteral", N->kind()); EXPECT_TRUE(N->Selected); } // Verify that the common assert() macro doesn't suffer from this. // (This is because we don't associate the stringified token with the arg). Case = R"cpp( void die(const char*); #define assert(x) (x ? (void)0 : die(#x)) void foo() { assert(^42); } )cpp"; Test = Annotations(Case); AST = TestTU::withCode(Test.code()).build(); T = makeSelectionTree(Case, AST); EXPECT_EQ("IntegerLiteral", T.commonAncestor()->kind()); } TEST(SelectionTest, Implicit) { const char* Test = R"cpp( struct S { S(const char*); }; int f(S); int x = f("^"); )cpp"; auto AST = TestTU::withCode(Annotations(Test).code()).build(); auto T = makeSelectionTree(Test, AST); const SelectionTree::Node *Str = T.commonAncestor(); EXPECT_EQ("StringLiteral", nodeKind(Str)) << "Implicit selected?"; EXPECT_EQ("ImplicitCastExpr", nodeKind(Str->Parent)); EXPECT_EQ("CXXConstructExpr", nodeKind(Str->Parent->Parent)); EXPECT_EQ(Str, &Str->Parent->Parent->ignoreImplicit()) << "Didn't unwrap " << nodeKind(&Str->Parent->Parent->ignoreImplicit()); EXPECT_EQ("CXXConstructExpr", nodeKind(&Str->outerImplicit())); } } // namespace } // namespace clangd } // namespace clang