//===-- 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 "support/TestTracer.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;

// Create a selection tree corresponding to a point or pair of points.
// This uses the precisely-defined createRight semantics. The fuzzier
// createEach is tested separately.
SelectionTree makeSelectionTree(const StringRef MarkedCode, ParsedAST &AST) {
  Annotations Test(MarkedCode);
  switch (Test.points().size()) {
  case 1: { // Point selection.
    unsigned Offset = cantFail(positionToOffset(Test.code(), Test.point()));
    return SelectionTree::createRight(AST.getASTContext(), AST.getTokens(),
                                      Offset, Offset);
  }
  case 2: // Range selection.
    return SelectionTree::createRight(
        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::createRight(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"},
      {"struct foo { [[int has^h<:32:>]]; };", "FieldDecl"},
      {"struct foo { [[op^erator int()]]; };", "CXXConversionDecl"},
      {"struct foo { [[^~foo()]]; };", "CXXDestructorDecl"},
      // FIXME: The following to should be class itself instead.
      {"struct foo { [[fo^o(){}]] };", "CXXConstructorDecl"},

      {R"cpp(
        struct S1 { void f(); };
        struct S2 { S1 * operator->(); };
        void test(S2 s2) {
          s2[[-^>]]f();
        }
      )cpp", "DeclRefExpr"} // DeclRefExpr to the "operator->" method.
  };
  for (const Case &C : Cases) {
    trace::TestTracer Tracer;
    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;
      EXPECT_THAT(Tracer.takeMetric("selection_recovery"), testing::IsEmpty());
    } 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;
      EXPECT_THAT(Tracer.takeMetric("selection_recovery"),
                  testing::ElementsAreArray({0}));
    }
  }
}

// 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());
}

TEST(SelectionTree, Metrics) {
  const char *Code = R"cpp(
    // error-ok: testing behavior on recovery expression
    int foo();
    int foo(int, int);
    int x = fo^o(42);
  )cpp";
  auto AST = TestTU::withCode(Annotations(Code).code()).build();
  trace::TestTracer Tracer;
  auto T = makeSelectionTree(Code, AST);
  EXPECT_THAT(Tracer.takeMetric("selection_recovery"),
              testing::ElementsAreArray({1}));
  EXPECT_THAT(Tracer.takeMetric("selection_recovery_type"),
              testing::ElementsAreArray({1}));
}

// 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 only consider the first one
  // 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);
  EXPECT_EQ("IntegerLiteral", T.commonAncestor()->kind());
  EXPECT_TRUE(T.commonAncestor()->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()));
}

TEST(SelectionTest, CreateAll) {
  llvm::Annotations Test("int$unique^ a=1$ambiguous^+1; $empty^");
  auto AST = TestTU::withCode(Test.code()).build();
  unsigned Seen = 0;
  SelectionTree::createEach(
      AST.getASTContext(), AST.getTokens(), Test.point("ambiguous"),
      Test.point("ambiguous"), [&](SelectionTree T) {
        // Expect to see the right-biased tree first.
        if (Seen == 0)
          EXPECT_EQ("BinaryOperator", nodeKind(T.commonAncestor()));
        else if (Seen == 1)
          EXPECT_EQ("IntegerLiteral", nodeKind(T.commonAncestor()));
        ++Seen;
        return false;
      });
  EXPECT_EQ(2u, Seen);

  Seen = 0;
  SelectionTree::createEach(AST.getASTContext(), AST.getTokens(),
                            Test.point("ambiguous"), Test.point("ambiguous"),
                            [&](SelectionTree T) {
                              ++Seen;
                              return true;
                            });
  EXPECT_EQ(1u, Seen) << "Return true --> stop iterating";

  Seen = 0;
  SelectionTree::createEach(AST.getASTContext(), AST.getTokens(),
                            Test.point("unique"), Test.point("unique"),
                            [&](SelectionTree T) {
                              ++Seen;
                              return false;
                            });
  EXPECT_EQ(1u, Seen) << "no ambiguity --> only one tree";

  Seen = 0;
  SelectionTree::createEach(AST.getASTContext(), AST.getTokens(),
                            Test.point("empty"), Test.point("empty"),
                            [&](SelectionTree T) {
                              EXPECT_FALSE(T.commonAncestor());
                              ++Seen;
                              return false;
                            });
  EXPECT_EQ(1u, Seen) << "empty tree still created";

  Seen = 0;
  SelectionTree::createEach(AST.getASTContext(), AST.getTokens(),
                            Test.point("unique"), Test.point("ambiguous"),
                            [&](SelectionTree T) {
                              ++Seen;
                              return false;
                            });
  EXPECT_EQ(1u, Seen) << "one tree for nontrivial selection";
}

} // namespace
} // namespace clangd
} // namespace clang
