chromium/tools/clang/rewrite_raw_ptr_fields/tests/affected-expr-expected.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stdint.h>  // for uintptr_t

#include <string>
#include <tuple>    // for std::tie
#include <utility>  // for std::swap

#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"

class SomeClass {};
class DerivedClass : public SomeClass {};

struct MyStruct {
  MyStruct(SomeClass& ref1, SomeClass& ref2, const SomeClass& ref3)
      : ref1_(ref1), ref2_(ref2), const_ref_(ref3) {}
  raw_ptr<SomeClass> ptr;
  raw_ptr<SomeClass> ptr2;
  raw_ptr<const SomeClass> const_ptr;
  int (*func_ptr_field)();
  const char* const_char_ptr;

  // Expected rewrite: const raw_ref<SomeClass> ref1_;
  const raw_ref<SomeClass> ref1_;
  // Expected rewrite: const raw_ref<SomeClass> ref2_;
  const raw_ref<SomeClass> ref2_;
  // Expected rewrite: const raw_ref<const SomeClass> const_ref_;
  const raw_ref<const SomeClass> const_ref_;
};

namespace auto_tests {

MyStruct* GetMyStruct() {
  return nullptr;
}

SomeClass* GetSomeClass() {
  return nullptr;
}

SomeClass* ConvertSomeClassToSomeClass(SomeClass* some_class) {
  return some_class;
}

void foo() {
  SomeClass s;
  MyStruct my_struct(s, s, s);

  // After the rewrite |my_struct.ptr_field| is no longer a pointer,
  // so |auto*| won't work.  We fix this up, by appending |.get()|.
  // Expected rewrite: auto* ptr_var = my_struct.ptr.get();
  auto* ptr_var = my_struct.ptr.get();

  // Tests for other kinds of initialization.
  // Expected rewrite: |.get()| should be appended in both cases below.
  auto* init_test1(my_struct.ptr.get());
  auto* init_test2{my_struct.ptr.get()};

  // Test for handling of the |const| qualifier.
  // Expected rewrite: const auto* ptr_var = my_struct.ptr.get();
  const auto* const_ptr_var = my_struct.ptr.get();

  // More complicated initialization expression, but the |ptr_field| struct
  // member dereference is still the top/last expression here.
  // Expected rewrite: ...->ptr.get()
  auto* complicated_var = GetMyStruct()->ptr.get();

  // The test below covers:
  // 1. Two variables with single |auto|,
  // 2. Tricky placement of |*| (next to the variable name).
  // Expected rewrite: ...ptr.get()... (twice in the 2nd example).
  auto *ptr_var1 = my_struct.ptr.get(), *ptr_var2 = GetSomeClass();
  auto *ptr_var3 = my_struct.ptr.get(), *ptr_var4 = my_struct.ptr.get();
  auto *ptr_var5 = GetSomeClass(), *ptr_var6 = my_struct.ptr.get();

  // Test for the case where
  // 1. The resulting type is the same as in the |ptr_var| and |complicated_var|
  //    examples
  // 2. Deep in the initialization expression there is a member dereference
  //    of |ptr_field|
  // but
  // 3. The final/top-level initialization expression doesn't dereference
  //    |ptr_field|.
  // No rewrite expected.
  auto* not_affected_field_var = ConvertSomeClassToSomeClass(my_struct.ptr);

  // Test for pointer |auto| assigned from non-raw_ptr-elligible field.
  // No rewrite expected.
  auto* func_ptr_var = my_struct.func_ptr_field;

  // Test for non-pointer |auto| assigned from raw_ptr-elligible field.
  // No rewrite expected.
  auto non_pointer_auto_var = my_struct.ptr;

  // Test for non-auto pointer.
  // No rewrite expected.
  SomeClass* non_auto_ptr_var = my_struct.ptr;

  // raw_ref tests
  {
    SomeClass some_class;
    MyStruct s(some_class, some_class, some_class);

    // After the rewrite |my_struct.ref_1| is no longer a native reference,
    // so |auto&| won't do what's expected.  We fix this up, by injecting *
    // operator. Expected rewrite: auto& ptr_var = *(my_struct.ref1_);
    auto& ref_var = *my_struct.ref1_;

    // Tests for other kinds of initialization.
    // Expected rewrite: operator* should be added in both cases below.
    auto& init_test1(*my_struct.ref1_);
    auto& init_test2{*my_struct.ref2_};

    // Test for handling of the |const| qualifier.
    // Expected rewrite: const auto& ptr_var = *my_struct.const_ref_;
    const auto& const_ref_var = *my_struct.const_ref_;

    // More complicated initialization expression, but the |ref1_| struct
    // member dereference is still the top/last expression here.
    // Expected rewrite: *GetMyStruct()->ref1_
    auto& complicated_var = *GetMyStruct()->ref1_;

    // The test below covers:
    // 1. Two variables with single |auto|,
    // 2. Tricky placement of |&| (next to the variable name).
    // Expected rewrite: *...ref_... (twice in the 2nd example).
    auto &ref_var1 = *my_struct.ref1_, &ref_var2 = *GetSomeClass();
    auto &ref_var3 = *my_struct.ref1_, &ref_var4 = *my_struct.ref1_;
    auto &ref_var5 = *GetSomeClass(), &ref_var6 = *my_struct.ref1_;

    // Expected rewrite: auto* not_affected_field_var =
    // ConvertSomeClassToSomeClass(&*my_struct.ref1_);
    auto* not_affected_field_var =
        ConvertSomeClassToSomeClass(&*my_struct.ref1_);

    // Test for non-pointer |auto| assigned from raw_ref-eligible field.
    // expected rewrite: auto non_pointer_auto_var = *my_struct.ref1_;
    auto non_pointer_auto_var = *my_struct.ref1_;

    // Test for non-auto pointer.
    // No rewrite expected.
    SomeClass& non_auto_ref_var = my_struct.ref1_;
  }
}

}  // namespace auto_tests

namespace printf_tests {

int ConvertSomeClassToInt(SomeClass* some_class) {
  return 123;
}

void MyPrintf(const char* fmt, ...) {}

void foo() {
  SomeClass some_class;
  MyStruct s(some_class, some_class, some_class);

  // Expected rewrite: MyPrintf("%p", s.ptr.get());
  MyPrintf("%p", s.ptr.get());

  // Test - all arguments are rewritten.
  // Expected rewrite: MyPrintf("%p, %p", s.ptr.get(), s.ptr2.get());
  MyPrintf("%p, %p", s.ptr.get(), s.ptr2.get());

  // Test - only |s.ptr|-style arguments are rewritten.
  // Expected rewrite: MyPrintf("%d, %p", 123, s.ptr.get());
  MyPrintf("%d, %p", 123, s.ptr.get());

  // Test - |s.ptr| is deeply nested.
  // No rewrite expected.
  MyPrintf("%d", ConvertSomeClassToInt(s.ptr));
}

}  // namespace printf_tests

namespace cast_tests {

void foo() {
  SomeClass s;
  MyStruct my_struct(s, s, s);

  // To get |const_cast<...>(...)| to compile after the rewrite we
  // need to rewrite the casted expression.
  // Expected rewrite: const_cast<SomeClass*>(my_struct.const_ptr.get());
  SomeClass* v = const_cast<SomeClass*>(my_struct.const_ptr.get());
  // Expected rewrite: const_cast<const SomeClass*>(my_struct.ptr.get());
  const SomeClass* v2 = const_cast<const SomeClass*>(my_struct.ptr.get());

  // To get |reinterpret_cast<uintptr_t>(...)| to compile after the rewrite we
  // need to rewrite the casted expression.
  // Expected rewrite: reinterpret_cast<uintptr_t>(my_struct.ptr.get());
  uintptr_t u = reinterpret_cast<uintptr_t>(my_struct.ptr.get());

  // There is no need to append |.get()| inside static_cast - unlike the
  // const_cast and reinterpret_cast examples above, static_cast will compile
  // just fine.
  DerivedClass* d = static_cast<DerivedClass*>(my_struct.ptr);
  void* void_var = static_cast<void*>(my_struct.ptr);
}

void foo2() {
  SomeClass s;
  MyStruct my_struct(s, s, s);

  // To get |const_cast<...>(...)| to compile after the rewrite we
  // need to rewrite the casted expression.
  // Expected rewrite: const_cast<SomeClass&>(*my_struct.const_ref_);
  SomeClass& v = const_cast<SomeClass&>(*my_struct.const_ref_);
  // Expected rewrite: const_cast<const SomeClass&>(*my_struct.ptr);
  const SomeClass& v2 = const_cast<const SomeClass&>(*my_struct.ref1_);

  // There is no need to append |.get()| inside static_cast - unlike the
  // const_cast and reinterpret_cast examples above, static_cast will compile
  // just fine.
  DerivedClass& d = static_cast<DerivedClass&>(*my_struct.ref1_);
}

}  // namespace cast_tests

namespace ternary_operator_tests {

void foo(int x) {
  SomeClass s;
  MyStruct my_struct(s, s, s);
  SomeClass* other_ptr = nullptr;

  // To avoid the following error type:
  //     conditional expression is ambiguous; 'const raw_ptr<SomeClass>'
  //     can be converted to 'SomeClass *' and vice versa
  // we need to append |.get()| to |my_struct.ptr| below.
  //
  // Expected rewrite: ... my_struct.ptr.get() ...
  SomeClass* v = (x > 123) ? my_struct.ptr.get() : other_ptr;

  // Rewrite in the other position.
  // Expected rewrite: ... my_struct.ptr.get() ...
  SomeClass* v2 = (x > 456) ? other_ptr : my_struct.ptr.get();

  // No rewrite is needed for the first, conditional argument.
  // No rewrite expected.
  int v3 = my_struct.ptr ? 123 : 456;

  // Test for 1st and 2nd arg.  Only 2nd arg should be rewritten.
  SomeClass* v4 = my_struct.ptr ? my_struct.ptr.get() : other_ptr;
}

void foo2(int x) {
  SomeClass s;
  MyStruct my_struct(s, s, s);
  SomeClass* other_ptr = nullptr;

  // Expected rewrite: SomeClass* v = (x > 123) ? &*my_struct.ref1_ :
  // other_ptr;
  SomeClass* v = (x > 123) ? &*my_struct.ref1_ : other_ptr;

  // Rewrite in the other position.
  // Expected rewrite: SomeClass* v2 = (x > 456) ? other_ptr :
  // &*my_struct.ref1_;
  SomeClass* v2 = (x > 456) ? other_ptr : &*my_struct.ref1_;
}

}  // namespace ternary_operator_tests

namespace string_comparison_operator_tests {

void foo(int x) {
  SomeClass s;
  MyStruct my_struct(s, s, s);
  std::string other_str = "other";

  // No rewrite expected. (for now)
  // TODO(crbug.com/40245402) |const char| pointer fields are not supported yet.
  bool v1 = my_struct.const_char_ptr == other_str;
  bool v2 = other_str == my_struct.const_char_ptr;
  bool v3 = my_struct.const_char_ptr > other_str;
  bool v4 = other_str > my_struct.const_char_ptr;
  bool v5 = my_struct.const_char_ptr >= other_str;
  bool v6 = other_str >= my_struct.const_char_ptr;
  bool v7 = my_struct.const_char_ptr < other_str;
  bool v8 = other_str < my_struct.const_char_ptr;
  bool v9 = my_struct.const_char_ptr <= other_str;
  bool v10 = other_str <= my_struct.const_char_ptr;
  std::string v11 = my_struct.const_char_ptr + other_str;
  std::string v12 = other_str + my_struct.const_char_ptr;
}

}  // namespace string_comparison_operator_tests

namespace templated_functions {

template <typename T>
void AffectedFunction(T* t) {}

template <typename T>
void TemplatedFunction_NonTemplatedParam(SomeClass* arg, T t) {}

template <typename T>
class MyTemplate {
 public:
  template <typename U>
  MyTemplate(U* u) {}

  void AffectedMethod(T* t) {}
};

// We also want to append |.get()| for |T| parameters (i.e. not just for |T*|
// parameters).
//
// One motivating example is the following pattern from
// //components/variations/service/ui_string_overrider.cc where the type of the
// 2 arguments needs to be kept consistent:
//     const uint32_t* end = ptr_field_ + num_resources_;
//     const uint32_t* element = std::lower_bound(ptr_field_, end, hash);
template <typename T>
void AffectedNonPointerFunction(T t) {}

// base::Unretained has a template specialization that accepts `const
// raw_ptr<T>&` as an argument (since https://crrev.com/c/3283196).  Therefore
// we expect that `.get()` is *not* used when calling base::Unretained.
//
// Originally, ActivityLogDatabasePolicy::ScheduleAndForget was used as a
// motivating example - passes a raw_ptr to base::Unretained.
template <typename T>
void Unretained(T* t) {}

// AffectedFunctionWithDeepT mimics ConvertPPResourceArrayToObjects from
// //ppapi/cpp/array_output.h
template <typename T>
void AffectedFunctionWithDeepT(MyTemplate<T>* blah) {}

// StructWithPointerToTemplate is used to test AffectedFunctionWithDeepT.
// StructWithPointerToTemplate mimics ResourceArrayOutputAdapter<T>
// (and its |output_| field that will be converted to a raw_ptr)
// from //ppapi/cpp/array_output.h
template <typename T>
struct StructWithPointerToTemplate {
  raw_ptr<MyTemplate<T>> ptr_to_template;
};

void foo() {
  SomeClass s;
  MyStruct my_struct(s, s, s);

  // Expected rewrite - appending: .get()
  AffectedFunction(my_struct.ptr.get());

  // Expected rewrite - appending: .get()
  MyTemplate<SomeClass> mt(my_struct.ptr.get());
  // Expected rewrite - appending: .get()
  mt.AffectedMethod(my_struct.ptr.get());

  // No rewrite expected.
  TemplatedFunction_NonTemplatedParam(my_struct.ptr, 123);

  // Expected rewrite - appending: .get()
  AffectedNonPointerFunction(my_struct.ptr.get());

  // Expected rewrite - appending: .get()
  StructWithPointerToTemplate<SomeClass> swptt;
  AffectedFunctionWithDeepT(swptt.ptr_to_template.get());

  // No rewrite expected - T& parameter.
  std::swap(my_struct.ptr, my_struct.ptr2);
  std::tie(my_struct.ptr, my_struct.ptr2) = std::make_pair(nullptr, nullptr);

  // No rewrite expected - functions named "Unretained" are excluded (they have
  // been manually modified to also provide a template specialization that
  // accepts `const raw_ptr<T>&` as an argument).
  Unretained(my_struct.ptr);
}

}  // namespace templated_functions

namespace templated_functions_raw_ref_tests {

template <typename T>
void AffectedFunction(T& t) {}

template <typename T>
void TemplatedFunction_NonTemplatedParam(SomeClass& arg, T t) {}

template <typename T>
class MyTemplate {
 public:
  template <typename U>
  MyTemplate(U& u) {}

  void AffectedMethod(T& t) {}
};

template <typename T>
void AffectedNonPointerFunction(T t) {}

// AffectedFunctionWithDeepT mimics ConvertPPResourceArrayToObjects from
// //ppapi/cpp/array_output.h
template <typename T>
void AffectedFunctionWithDeepT(MyTemplate<T>& blah) {}

// StructWithPointerToTemplate is used to test AffectedFunctionWithDeepT.
// StructWithPointerToTemplate mimics ResourceArrayOutputAdapter<T>
// (and its |output_| field that will be converted to a raw_ref)
// from //ppapi/cpp/array_output.h
template <typename T>
struct StructWithPointerToTemplate {
  StructWithPointerToTemplate(MyTemplate<T>& ref) : ref_to_template(ref) {}
  const raw_ref<MyTemplate<T>> ref_to_template;
};

void foo() {
  SomeClass s;
  MyStruct my_struct(s, s, s);

  // Expected rewrite: AffectedFunction(*my_struct.ref1_);
  AffectedFunction(*my_struct.ref1_);

  // Expected rewrite: MyTemplate<SomeClass> mt(*my_struct.ref1_);
  MyTemplate<SomeClass> mt(*my_struct.ref1_);
  // Expected rewrite: mt.AffectedMethod(*my_struct.ref1_);
  mt.AffectedMethod(*my_struct.ref1_);

  // Expected rewrite: TemplatedFunction_NonTemplatedParam(*my_struct.ref1_,
  // 123)
  TemplatedFunction_NonTemplatedParam(*my_struct.ref1_, 123);

  // Expected rewrite: AffectedNonPointerFunction(*my_struct.ref1_);
  AffectedNonPointerFunction(*my_struct.ref1_);

  MyTemplate<SomeClass> my_template(s);
  StructWithPointerToTemplate<SomeClass> swptt(my_template);
  // Expected rewrite: AffectedFunctionWithDeepT(*swptt.ref_to_template);
  AffectedFunctionWithDeepT(*swptt.ref_to_template);

  // Expected rewrite: std::swap(*my_struct.ref1_, *my_struct.ref2_)
  std::swap(*my_struct.ref1_, *my_struct.ref2_);
  std::tie(*my_struct.ref1_, *my_struct.ref2_) = std::make_pair(s, s);
}

}  // namespace templated_functions_raw_ref_tests

namespace implicit_constructors {

template <typename CharT>
class BasicStringPiece;
typedef BasicStringPiece<char> StringPiece;
template <typename CharT>
class BasicStringPiece {
 public:
  constexpr BasicStringPiece(const char* str) {}
};
// Test case:
void FunctionTakingBasicStringPiece(StringPiece arg) {}
void FunctionTakingBasicStringPieceRef(const StringPiece& arg) {}

class ClassWithImplicitConstructor {
 public:
  ClassWithImplicitConstructor(SomeClass* blah) {}
};
void FunctionTakingArgWithImplicitConstructor(
    ClassWithImplicitConstructor arg) {}

void foo() {
  SomeClass s;
  MyStruct my_struct(s, s, s);

  // No rewrite expected. (for now)
  // TODO(crbug.com/40245402) |const char| pointer fields are not supported yet.
  FunctionTakingBasicStringPiece(my_struct.const_char_ptr);
  FunctionTakingBasicStringPieceRef(my_struct.const_char_ptr);

  // No rewrite expected.
  FunctionTakingBasicStringPiece(StringPiece(my_struct.const_char_ptr));
  FunctionTakingBasicStringPieceRef(StringPiece(my_struct.const_char_ptr));

  // Expected rewrite - appending: .get().  This is the same scenario as with
  // StringPiece above (except that no templates are present here).
  FunctionTakingArgWithImplicitConstructor(my_struct.ptr.get());
}

}  // namespace implicit_constructors

namespace implicit_constructors_raw_ref_tests {

template <typename CharT>
class BasicStringPiece;
typedef BasicStringPiece<char> StringPiece;
template <typename CharT>
class BasicStringPiece {
 public:
  constexpr BasicStringPiece(const char* str) {}
};
// Test case:
void FunctionTakingBasicStringPiece(StringPiece arg) {}
void FunctionTakingBasicStringPieceRef(const StringPiece& arg) {}

class ClassWithImplicitConstructor {
 public:
  ClassWithImplicitConstructor(SomeClass& blah) {}
};
void FunctionTakingArgWithImplicitConstructor(
    ClassWithImplicitConstructor arg) {}

void foo() {
  SomeClass s;
  MyStruct my_struct(s, s, s);
  // Expected rewrite:
  // FunctionTakingArgWithImplicitConstructor(*my_struct.ref1_);
  FunctionTakingArgWithImplicitConstructor(*my_struct.ref1_);
}

}  // namespace implicit_constructors_raw_ref_tests

namespace affected_implicit_template_specialization {

template <typename T, typename T2>
struct MyTemplate {
  raw_ptr<T> t_ptr;
  raw_ptr<T2> t2_ptr;

  struct NestedStruct {
    raw_ptr<SomeClass> nested_ptr_field;
    raw_ptr<T> nested_t_ptr_field;
  };
  NestedStruct nested_struct_field;
};

template <typename T3>
struct MyTemplate<SomeClass, T3> {
  raw_ptr<SomeClass> some_ptr;
  raw_ptr<T3> t3_ptr;
};

// The example that forces explicit |isAnonymousStructOrUnion| checks in
// the implementation of GetExplicitDecl.  The example is based on
// buildtools/third_party/libc++/trunk/include/string.
template <typename T>
struct MyStringTemplate {
  struct NestedStruct {
    union {
      long l;
      short s;
      raw_ptr<T> t_ptr;
      raw_ptr<int> i_ptr;
    };  // Unnamed / anonymous union *field*.

    struct {
      long l2;
      short s2;
      raw_ptr<T> t_ptr2;
      raw_ptr<int> i_ptr2;
    };  // Unnamed / anonymous struct *field*.
  };
  NestedStruct s;
};

void MyPrintf(const char* fmt, ...) {}

void foo() {
  // |s.t_ptr| comes from implicit template specialization (which needs to be
  // skipped for rewriting, but should be included for appending |.get()|).
  //
  // Expected rewrite: MyPrintf("%p", s.t_ptr.get());
  MyTemplate<int, int> s;
  MyPrintf("%p", s.t_ptr.get());

  // |s.some_ptr| and |s.t2_ptr| come from implicit template specialization or a
  // partial template specialization.
  //
  // Expected rewrite: MyPrintf("%p", s.some_ptr.get(), s.t3_ptr.get());
  MyTemplate<SomeClass, int> s2;
  MyPrintf("%p %p", s2.some_ptr.get(), s2.t3_ptr.get());

  // Nested structs require extra care when trying to look up the non-implicit
  // field definition.  Expected rewrite: adding |.get()| suffix.
  MyPrintf("%p", s.nested_struct_field.nested_ptr_field.get());
  MyPrintf("%p", s.nested_struct_field.nested_t_ptr_field.get());

  // Lines below are added mainly to Force implicit specialization of
  // MyStringTemplate (to force explicit |isAnonymousStructOrUnion| checks in
  // the rewriter).  Still, the expected rewrite is: appending |.get()| to the
  // printf arg.
  MyStringTemplate<void> mst;
  MyPrintf("%p %p", mst.s.t_ptr.get(), mst.s.t_ptr2.get());
}

}  // namespace affected_implicit_template_specialization

namespace affected_implicit_template_specialization_raw_ref_tests {

template <typename T, typename T2>
struct MyTemplate {
  const raw_ref<T> t_ref;
  const raw_ref<T2> t2_ref;

  struct NestedStruct {
    const raw_ref<SomeClass> nested_ref_field;
    const raw_ref<T> nested_t_ref_field;
  };
  NestedStruct nested_struct_field;
};

template <typename T3>
struct MyTemplate<SomeClass, T3> {
  const raw_ref<SomeClass> some_ptr;
  const raw_ref<T3> t3_ptr;
};
}  // namespace affected_implicit_template_specialization_raw_ref_tests

// The test scenario below is based on an example encountered in
// //cc/layers/picture_layer_impl_unittest.cc:
//   auto* shared_quad_state = render_pass->quad_list.begin()->shared_quad_state
// In this example, the AST looks like this:
//  `-DeclStmt
//    `-VarDecl shared_quad_state 'const SharedQuadState *' cinit
//      `-ExprWithCleanups 'const SharedQuadState *'
//        `-ImplicitCastExpr 'const SharedQuadState *' <LValueToRValue>
//          `-MemberExpr 'const SharedQuadState *const' lvalue ->shared...state
//            `-.....
// The rewriter needs to ignore the implicit ExprWithCleanups and
// ImplicitCastExpr nodes in order to find the MemberExpr.  If this is
// implemented incorrectly, then the rewriter won't append |.get()| to fix the
// |auto*| initialization.
namespace more_implicit_ast_nodes_trouble {

template <class BaseElementType>
struct ListContainer {
  struct ConstIterator {
    const BaseElementType* operator->() const { return nullptr; }
  };

  ConstIterator begin() const { return ConstIterator(); }
};

class SharedQuadState;

struct DrawQuad {
  raw_ptr<const SharedQuadState> shared_quad_state;
};

struct RenderPass {
  using QuadList = ListContainer<DrawQuad>;
  QuadList quad_list;
};

void foo() {
  RenderPass* render_pass = nullptr;
  auto* shared_quad_state =
      render_pass->quad_list.begin()->shared_quad_state.get();
}

}  // namespace more_implicit_ast_nodes_trouble