llvm/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp

// RUN: %clang_cc1 -std=c++20  -Wno-all -Wunsafe-buffer-usage -Wno-unsafe-buffer-usage-in-container\
// RUN:            -fsafe-buffer-usage-suggestions \
// RUN:            -fblocks -include %s -verify %s

// RUN: %clang -x c++ -frtti -fsyntax-only -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s
// RUN: %clang_cc1 -std=c++11 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s
// RUN: %clang_cc1 -std=c++20 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s
// CHECK-NOT: [-Wunsafe-buffer-usage]

#ifndef INCLUDED
#define INCLUDED
#pragma clang system_header

// no spanification warnings for system headers
#else

typedef __INTPTR_TYPE__ intptr_t;

namespace std {
  class type_info;
  class bad_cast;
  class bad_typeid;
}
using size_t = __typeof(sizeof(int));
void *malloc(size_t);

void foo(int v) {
}

void foo(int *p){}

namespace std{
  template <typename T> class span {

  T *elements;
 
  span(T *, unsigned){}

  public:

  constexpr span<T> subspan(size_t offset, size_t count) const {
    return span<T> (elements+offset, count); // expected-warning{{unsafe pointer arithmetic}}
  }

  constexpr T* data() const noexcept {
    return elements;
  }

 
  constexpr T* hello() const noexcept {
   return elements;
  }
};
 
 template <typename T> class span_duplicate {
  span_duplicate(T *, unsigned){}

  T array[10];

  public:

  T* data() {
    return array;
  }

};
}

using namespace std;

class A {
  int a, b, c;
};

class B {
  int a, b, c;
};

struct Base {
   virtual ~Base() = default;
};

struct Derived: Base {
  int d;
};

void cast_without_data(int *ptr) {
 A *a = (A*) ptr;
 float *p = (float*) ptr;
}

void warned_patterns(std::span<int> span_ptr, std::span<Base> base_span, span<int> span_without_qual) {
    A *a1 = (A*)span_ptr.data(); // expected-warning{{unsafe invocation of span::data}}
    a1 = (A*)span_ptr.data(); // expected-warning{{unsafe invocation of span::data}}

    a1 = (A*)(span_ptr.data()); // expected-warning{{unsafe invocation of span::data}}
    A *a2 = (A*) (span_without_qual.data()); // expected-warning{{unsafe invocation of span::data}}

    a2 = (A*) span_without_qual.data(); // expected-warning{{unsafe invocation of span::data}}

     // TODO:: Should we warn when we cast from base to derived type?
     Derived *b = dynamic_cast<Derived*> (base_span.data());// expected-warning{{unsafe invocation of span::data}}

    // TODO:: This pattern is safe. We can add special handling for it, if we decide this
    // is the recommended fixit for the unsafe invocations.
    A *a3 = (A*)span_ptr.subspan(0, sizeof(A)).data(); // expected-warning{{unsafe invocation of span::data}}
}

void not_warned_patterns(std::span<A> span_ptr, std::span<Base> base_span) {
    int *p = (int*) span_ptr.data(); // Cast to a smaller type
  
    B *b = (B*) span_ptr.data(); // Cast to a type of same size.

    p = (int*) span_ptr.data();
    A *a = (A*) span_ptr.hello(); // Invoking other methods.
   
     intptr_t k = (intptr_t) span_ptr.data();
    k = (intptr_t) (span_ptr.data());
}

// We do not want to warn about other types
void other_classes(std::span_duplicate<int> span_ptr) {
    int *p;
    A *a = (A*)span_ptr.data();
    a = (A*)span_ptr.data(); 
}

// Potential source for false negatives

A false_negatives(std::span<int> span_pt, span<A> span_A) {
  int *ptr = span_pt.data();

  A *a1 = (A*)ptr; //TODO: We want to warn here eventually.

  A *a2= span_A.data();
  return *a2; // TODO: Can cause OOB if span_pt is empty

}
#endif