301 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- ExtractVariableTests.cpp --------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
// 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 "TestTU.h"
 | 
						|
#include "TweakTesting.h"
 | 
						|
#include "gmock/gmock-matchers.h"
 | 
						|
#include "gmock/gmock.h"
 | 
						|
#include "gtest/gtest.h"
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace clangd {
 | 
						|
namespace {
 | 
						|
 | 
						|
TWEAK_TEST(ExtractVariable);
 | 
						|
 | 
						|
TEST_F(ExtractVariableTest, Test) {
 | 
						|
  const char *AvailableCases = R"cpp(
 | 
						|
    int xyz(int a = 1) {
 | 
						|
      struct T {
 | 
						|
        int bar(int a = 1);
 | 
						|
        int z;
 | 
						|
      } t;
 | 
						|
      // return statement
 | 
						|
      return [[[[t.b[[a]]r]](t.z)]];
 | 
						|
    }
 | 
						|
    void f() {
 | 
						|
      int a = [[5 +]] [[4 * [[[[xyz]]()]]]];
 | 
						|
      // multivariable initialization
 | 
						|
      if(1)
 | 
						|
        int x = [[1]], y = [[a + 1]], a = [[1]], z = a + 1;
 | 
						|
      // if without else
 | 
						|
      if([[1]])
 | 
						|
        a = [[1]] + 1;
 | 
						|
      // if with else
 | 
						|
      if(a < [[3]])
 | 
						|
        if(a == [[4]])
 | 
						|
          a = [[5]] + 1;
 | 
						|
        else
 | 
						|
          a = [[5]] + 1;
 | 
						|
      else if (a < [[4]])
 | 
						|
        a = [[4]] + 1;
 | 
						|
      else
 | 
						|
        a = [[5]] + 1;
 | 
						|
      // for loop
 | 
						|
      for(a = [[1]] + 1; a > [[[[3]] + [[4]]]]; a++)
 | 
						|
        a = [[2]] + 1;
 | 
						|
      // while
 | 
						|
      while(a < [[1]])
 | 
						|
        a = [[1]] + 1;
 | 
						|
      // do while
 | 
						|
      do
 | 
						|
        a = [[1]] + 1;
 | 
						|
      while(a < [[3]]);
 | 
						|
    }
 | 
						|
  )cpp";
 | 
						|
  EXPECT_AVAILABLE(AvailableCases);
 | 
						|
 | 
						|
  ExtraArgs = {"-xc"};
 | 
						|
  const char *AvailableButC = R"cpp(
 | 
						|
    void foo() {
 | 
						|
      int x = [[1]];
 | 
						|
    })cpp";
 | 
						|
  EXPECT_UNAVAILABLE(AvailableButC);
 | 
						|
  ExtraArgs = {};
 | 
						|
 | 
						|
  const char *NoCrashCases = R"cpp(
 | 
						|
    // error-ok: broken code, but shouldn't crash
 | 
						|
    template<typename T, typename ...Args>
 | 
						|
    struct Test<T, Args...> {
 | 
						|
    Test(const T &v) :val[[(^]]) {}
 | 
						|
      T val;
 | 
						|
    };
 | 
						|
  )cpp";
 | 
						|
  EXPECT_UNAVAILABLE(NoCrashCases);
 | 
						|
 | 
						|
  const char *UnavailableCases = R"cpp(
 | 
						|
    int xyz(int a = [[1]]) {
 | 
						|
      struct T {
 | 
						|
        int bar(int a = [[1]]);
 | 
						|
        int z = [[1]];
 | 
						|
      } t;
 | 
						|
      return [[t]].bar([[[[t]].z]]);
 | 
						|
    }
 | 
						|
    void v() { return; }
 | 
						|
    // function default argument
 | 
						|
    void f(int b = [[1]]) {
 | 
						|
      // empty selection
 | 
						|
      int a = ^1 ^+ ^2;
 | 
						|
      // void expressions
 | 
						|
      auto i = new int, j = new int;
 | 
						|
      [[[[delete i]], delete j]];
 | 
						|
      [[v]]();
 | 
						|
      // if
 | 
						|
      if(1)
 | 
						|
        int x = 1, y = a + 1, a = 1, z = [[a + 1]];
 | 
						|
      if(int a = 1)
 | 
						|
        if([[a + 1]] == 4)
 | 
						|
          a = [[[[a]] +]] 1;
 | 
						|
      // for loop
 | 
						|
      for(int a = 1, b = 2, c = 3; a > [[b + c]]; [[a++]])
 | 
						|
        a = [[a + 1]];
 | 
						|
      // lambda
 | 
						|
      auto lamb = [&[[a]], &[[b]]](int r = [[1]]) {return 1;};
 | 
						|
      // assignment
 | 
						|
      xyz([[a = 5]]);
 | 
						|
      xyz([[a *= 5]]);
 | 
						|
      // Variable DeclRefExpr
 | 
						|
      a = [[b]];
 | 
						|
      a = [[xyz()]];
 | 
						|
      // statement expression
 | 
						|
      [[xyz()]];
 | 
						|
      while (a)
 | 
						|
        [[++a]];
 | 
						|
      // label statement
 | 
						|
      goto label;
 | 
						|
      label:
 | 
						|
        a = [[1]];
 | 
						|
    }
 | 
						|
  )cpp";
 | 
						|
  EXPECT_UNAVAILABLE(UnavailableCases);
 | 
						|
 | 
						|
  // vector of pairs of input and output strings
 | 
						|
  const std::vector<std::pair<std::string, std::string>> InputOutputs = {
 | 
						|
      // extraction from variable declaration/assignment
 | 
						|
      {R"cpp(void varDecl() {
 | 
						|
                   int a = 5 * (4 + (3 [[- 1)]]);
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(void varDecl() {
 | 
						|
                   auto placeholder = (3 - 1); int a = 5 * (4 + placeholder);
 | 
						|
                 })cpp"},
 | 
						|
      // FIXME: extraction from switch case
 | 
						|
      /*{R"cpp(void f(int a) {
 | 
						|
               if(1)
 | 
						|
                 while(a < 1)
 | 
						|
                   switch (1) {
 | 
						|
                       case 1:
 | 
						|
                         a = [[1 + 2]];
 | 
						|
                         break;
 | 
						|
                       default:
 | 
						|
                         break;
 | 
						|
                   }
 | 
						|
             })cpp",
 | 
						|
       R"cpp(void f(int a) {
 | 
						|
               auto placeholder = 1 + 2; if(1)
 | 
						|
                 while(a < 1)
 | 
						|
                   switch (1) {
 | 
						|
                       case 1:
 | 
						|
                         a = placeholder;
 | 
						|
                         break;
 | 
						|
                       default:
 | 
						|
                         break;
 | 
						|
                   }
 | 
						|
             })cpp"},*/
 | 
						|
      // Macros
 | 
						|
      {R"cpp(#define PLUS(x) x++
 | 
						|
                 void f(int a) {
 | 
						|
                   int y = PLUS([[1+a]]);
 | 
						|
                 })cpp",
 | 
						|
       /*FIXME: It should be extracted like this.
 | 
						|
        R"cpp(#define PLUS(x) x++
 | 
						|
              void f(int a) {
 | 
						|
                auto placeholder = 1+a; int y = PLUS(placeholder);
 | 
						|
              })cpp"},*/
 | 
						|
       R"cpp(#define PLUS(x) x++
 | 
						|
                 void f(int a) {
 | 
						|
                   auto placeholder = PLUS(1+a); int y = placeholder;
 | 
						|
                 })cpp"},
 | 
						|
      // ensure InsertionPoint isn't inside a macro
 | 
						|
      {R"cpp(#define LOOP(x) while (1) {a = x;}
 | 
						|
                 void f(int a) {
 | 
						|
                   if(1)
 | 
						|
                    LOOP(5 + [[3]])
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(#define LOOP(x) while (1) {a = x;}
 | 
						|
                 void f(int a) {
 | 
						|
                   auto placeholder = 3; if(1)
 | 
						|
                    LOOP(5 + placeholder)
 | 
						|
                 })cpp"},
 | 
						|
      {R"cpp(#define LOOP(x) do {x;} while(1);
 | 
						|
                 void f(int a) {
 | 
						|
                   if(1)
 | 
						|
                    LOOP(5 + [[3]])
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(#define LOOP(x) do {x;} while(1);
 | 
						|
                 void f(int a) {
 | 
						|
                   auto placeholder = 3; if(1)
 | 
						|
                    LOOP(5 + placeholder)
 | 
						|
                 })cpp"},
 | 
						|
      // attribute testing
 | 
						|
      {R"cpp(void f(int a) {
 | 
						|
                    [ [gsl::suppress("type")] ] for (;;) a = [[1]] + 1;
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(void f(int a) {
 | 
						|
                    auto placeholder = 1; [ [gsl::suppress("type")] ] for (;;) a = placeholder + 1;
 | 
						|
                 })cpp"},
 | 
						|
      // MemberExpr
 | 
						|
      {R"cpp(class T {
 | 
						|
                   T f() {
 | 
						|
                     return [[T().f()]].f();
 | 
						|
                   }
 | 
						|
                 };)cpp",
 | 
						|
       R"cpp(class T {
 | 
						|
                   T f() {
 | 
						|
                     auto placeholder = T().f(); return placeholder.f();
 | 
						|
                   }
 | 
						|
                 };)cpp"},
 | 
						|
      // Function DeclRefExpr
 | 
						|
      {R"cpp(int f() {
 | 
						|
                   return [[f]]();
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(int f() {
 | 
						|
                   auto placeholder = f(); return placeholder;
 | 
						|
                 })cpp"},
 | 
						|
      // FIXME: Wrong result for \[\[clang::uninitialized\]\] int b = [[1]];
 | 
						|
      // since the attr is inside the DeclStmt and the bounds of
 | 
						|
      // DeclStmt don't cover the attribute.
 | 
						|
 | 
						|
      // Binary subexpressions
 | 
						|
      {R"cpp(void f() {
 | 
						|
                   int x = 1 + [[2 + 3 + 4]] + 5;
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(void f() {
 | 
						|
                   auto placeholder = 2 + 3 + 4; int x = 1 + placeholder + 5;
 | 
						|
                 })cpp"},
 | 
						|
      {R"cpp(void f() {
 | 
						|
                   int x = [[1 + 2 + 3]] + 4 + 5;
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(void f() {
 | 
						|
                   auto placeholder = 1 + 2 + 3; int x = placeholder + 4 + 5;
 | 
						|
                 })cpp"},
 | 
						|
      {R"cpp(void f() {
 | 
						|
                   int x = 1 + 2 + [[3 + 4 + 5]];
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(void f() {
 | 
						|
                   auto placeholder = 3 + 4 + 5; int x = 1 + 2 + placeholder;
 | 
						|
                 })cpp"},
 | 
						|
      // Non-associative operations have no special support
 | 
						|
      {R"cpp(void f() {
 | 
						|
                   int x = 1 - [[2 - 3 - 4]] - 5;
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(void f() {
 | 
						|
                   auto placeholder = 1 - 2 - 3 - 4; int x = placeholder - 5;
 | 
						|
                 })cpp"},
 | 
						|
      // A mix of associative operators isn't associative.
 | 
						|
      {R"cpp(void f() {
 | 
						|
                   int x = 0 + 1 * [[2 + 3]] * 4 + 5;
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(void f() {
 | 
						|
                   auto placeholder = 1 * 2 + 3 * 4; int x = 0 + placeholder + 5;
 | 
						|
                 })cpp"},
 | 
						|
      // Overloaded operators are supported, we assume associativity
 | 
						|
      // as if they were built-in.
 | 
						|
      {R"cpp(struct S {
 | 
						|
                   S(int);
 | 
						|
                 };
 | 
						|
                 S operator+(S, S);
 | 
						|
 | 
						|
                 void f() {
 | 
						|
                   S x = S(1) + [[S(2) + S(3) + S(4)]] + S(5);
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(struct S {
 | 
						|
                   S(int);
 | 
						|
                 };
 | 
						|
                 S operator+(S, S);
 | 
						|
 | 
						|
                 void f() {
 | 
						|
                   auto placeholder = S(2) + S(3) + S(4); S x = S(1) + placeholder + S(5);
 | 
						|
                 })cpp"},
 | 
						|
      // Don't try to analyze across macro boundaries
 | 
						|
      // FIXME: it'd be nice to do this someday (in a safe way)
 | 
						|
      {R"cpp(#define ECHO(X) X
 | 
						|
                 void f() {
 | 
						|
                   int x = 1 + [[ECHO(2 + 3) + 4]] + 5;
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(#define ECHO(X) X
 | 
						|
                 void f() {
 | 
						|
                   auto placeholder = 1 + ECHO(2 + 3) + 4; int x = placeholder + 5;
 | 
						|
                 })cpp"},
 | 
						|
      {R"cpp(#define ECHO(X) X
 | 
						|
                 void f() {
 | 
						|
                   int x = 1 + [[ECHO(2) + ECHO(3) + 4]] + 5;
 | 
						|
                 })cpp",
 | 
						|
       R"cpp(#define ECHO(X) X
 | 
						|
                 void f() {
 | 
						|
                   auto placeholder = 1 + ECHO(2) + ECHO(3) + 4; int x = placeholder + 5;
 | 
						|
                 })cpp"},
 | 
						|
  };
 | 
						|
  for (const auto &IO : InputOutputs) {
 | 
						|
    EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
} // namespace clangd
 | 
						|
} // namespace clang
 |