From 3995deaf76d0c988d315630ace46eb01dba37174 Mon Sep 17 00:00:00 2001
From: Dale Phurrough <dale@hidale.com>
Date: Sat, 28 Aug 2021 01:12:44 +0200
Subject: [PATCH] fix opencv/opencv#20544 nodiscard for msvc/gcc

- includes workaround for preprocessor non-compliance
- enable attribute syntax checking in msvc
---
 cmake/OpenCVCompilerOptions.cmake          |  3 ++
 modules/core/include/opencv2/core/cvdef.h  | 39 ++++++++++++--
 modules/core/include/opencv2/core/mat.hpp  | 62 +++++++++++-----------
 modules/core/include/opencv2/core/matx.hpp | 14 ++---
 4 files changed, 77 insertions(+), 41 deletions(-)

diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake
index 303d4f451e..d444085ea3 100644
--- a/cmake/OpenCVCompilerOptions.cmake
+++ b/cmake/OpenCVCompilerOptions.cmake
@@ -400,6 +400,9 @@ if(MSVC)
     endif()
   endif()
 
+  # Enable [[attribute]] syntax checking to prevent silent failure: "attribute is ignored in this syntactic position"
+  add_extra_compiler_option("/w15240")
+
   if(NOT ENABLE_NOISY_WARNINGS)
     ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127) # conditional expression is constant
     ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4251) # class 'std::XXX' needs to have dll-interface to be used by clients of YYY
diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h
index a4b560b4e1..6011b2a931 100644
--- a/modules/core/include/opencv2/core/cvdef.h
+++ b/modules/core/include/opencv2/core/cvdef.h
@@ -575,14 +575,47 @@ Cv64suf;
 #  endif
 #endif
 
+/****************************************************************************************\
+*                       CV_NODISCARD_STD attribute (C++17)                               *
+* encourages the compiler to issue a warning if the return value is discarded            *
+\****************************************************************************************/
+#ifndef CV_NODISCARD_STD
+#  ifndef __has_cpp_attribute
+//   workaround preprocessor non-compliance https://reviews.llvm.org/D57851
+#    define __has_cpp_attribute(__x) 0
+#  endif
+#  if __has_cpp_attribute(nodiscard)
+#    define CV_NODISCARD_STD [[nodiscard]]
+#  elif __cplusplus >= 201703L
+//   available when compiler is C++17 compliant
+#    define CV_NODISCARD_STD [[nodiscard]]
+#  elif defined(_MSC_VER) && _MSC_VER >= 1911 && _MSVC_LANG >= 201703L
+//   available with VS2017 v15.3+ with /std:c++17 or higher; works on functions and classes
+#    define CV_NODISCARD_STD [[nodiscard]]
+#  elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 700) && (__cplusplus >= 201103L)
+//   available with GCC 7.0+; works on functions, works or silently fails on classes
+#    define CV_NODISCARD_STD [[nodiscard]]
+#  elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 408) && (__cplusplus >= 201103L)
+//   available with GCC 4.8+ but it usually does nothing and can fail noisily -- therefore not used
+//   define CV_NODISCARD_STD [[gnu::warn_unused_result]]
+#  endif
+#endif
+#ifndef CV_NODISCARD_STD
+#  define CV_NODISCARD_STD /* nothing by default */
+#endif
+
 
 /****************************************************************************************\
-*                                  CV_NODISCARD attribute                                *
-* encourages the compiler to issue a warning if the return value is discarded (C++17)    *
+*                      CV_NODISCARD attribute (deprecated, GCC only)                     *
+* DONT USE: use instead the standard CV_NODISCARD_STD macro above                        *
+*           this legacy method silently fails to issue warning until some version        *
+*           after gcc 6.3.0. Yet with gcc 7+ you can use the above standard method       *
+*           which makes this method useless. Don't use it.                               *
+* @deprecated use instead CV_NODISCARD_STD                                               *
 \****************************************************************************************/
 #ifndef CV_NODISCARD
 #  if defined(__GNUC__)
-#    define CV_NODISCARD __attribute__((__warn_unused_result__)) // at least available with GCC 3.4
+#    define CV_NODISCARD __attribute__((__warn_unused_result__))
 #  elif defined(__clang__) && defined(__has_attribute)
 #    if __has_attribute(__warn_unused_result__)
 #      define CV_NODISCARD __attribute__((__warn_unused_result__))
diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp
index d0ce61e123..fdcb8fc817 100644
--- a/modules/core/include/opencv2/core/mat.hpp
+++ b/modules/core/include/opencv2/core/mat.hpp
@@ -1204,14 +1204,14 @@ public:
     The method creates a square diagonal matrix from specified main diagonal.
     @param d One-dimensional matrix that represents the main diagonal.
      */
-    static Mat diag(const Mat& d);
+    CV_NODISCARD_STD static Mat diag(const Mat& d);
 
     /** @brief Creates a full copy of the array and the underlying data.
 
     The method creates a full copy of the array. The original step[] is not taken into account. So, the
     array copy is a continuous array occupying total()*elemSize() bytes.
      */
-    Mat clone() const CV_NODISCARD;
+    CV_NODISCARD_STD Mat clone() const;
 
     /** @brief Copies the matrix to another one.
 
@@ -1375,20 +1375,20 @@ public:
     @param cols Number of columns.
     @param type Created matrix type.
      */
-    static MatExpr zeros(int rows, int cols, int type);
+    CV_NODISCARD_STD static MatExpr zeros(int rows, int cols, int type);
 
     /** @overload
     @param size Alternative to the matrix size specification Size(cols, rows) .
     @param type Created matrix type.
     */
-    static MatExpr zeros(Size size, int type);
+    CV_NODISCARD_STD static MatExpr zeros(Size size, int type);
 
     /** @overload
     @param ndims Array dimensionality.
     @param sz Array of integers specifying the array shape.
     @param type Created matrix type.
     */
-    static MatExpr zeros(int ndims, const int* sz, int type);
+    CV_NODISCARD_STD static MatExpr zeros(int ndims, const int* sz, int type);
 
     /** @brief Returns an array of all 1's of the specified size and type.
 
@@ -1406,20 +1406,20 @@ public:
     @param cols Number of columns.
     @param type Created matrix type.
      */
-    static MatExpr ones(int rows, int cols, int type);
+    CV_NODISCARD_STD static MatExpr ones(int rows, int cols, int type);
 
     /** @overload
     @param size Alternative to the matrix size specification Size(cols, rows) .
     @param type Created matrix type.
     */
-    static MatExpr ones(Size size, int type);
+    CV_NODISCARD_STD static MatExpr ones(Size size, int type);
 
     /** @overload
     @param ndims Array dimensionality.
     @param sz Array of integers specifying the array shape.
     @param type Created matrix type.
     */
-    static MatExpr ones(int ndims, const int* sz, int type);
+    CV_NODISCARD_STD static MatExpr ones(int ndims, const int* sz, int type);
 
     /** @brief Returns an identity matrix of the specified size and type.
 
@@ -1435,13 +1435,13 @@ public:
     @param cols Number of columns.
     @param type Created matrix type.
      */
-    static MatExpr eye(int rows, int cols, int type);
+    CV_NODISCARD_STD static MatExpr eye(int rows, int cols, int type);
 
     /** @overload
     @param size Alternative matrix size specification as Size(cols, rows) .
     @param type Created matrix type.
     */
-    static MatExpr eye(Size size, int type);
+    CV_NODISCARD_STD static MatExpr eye(Size size, int type);
 
     /** @brief Allocates new array data if needed.
 
@@ -2302,7 +2302,7 @@ public:
     Mat_ row(int y) const;
     Mat_ col(int x) const;
     Mat_ diag(int d=0) const;
-    Mat_ clone() const CV_NODISCARD;
+    CV_NODISCARD_STD Mat_ clone() const;
 
     //! overridden forms of Mat::elemSize() etc.
     size_t elemSize() const;
@@ -2315,14 +2315,14 @@ public:
     size_t stepT(int i=0) const;
 
     //! overridden forms of Mat::zeros() etc. Data type is omitted, of course
-    static MatExpr zeros(int rows, int cols);
-    static MatExpr zeros(Size size);
-    static MatExpr zeros(int _ndims, const int* _sizes);
-    static MatExpr ones(int rows, int cols);
-    static MatExpr ones(Size size);
-    static MatExpr ones(int _ndims, const int* _sizes);
-    static MatExpr eye(int rows, int cols);
-    static MatExpr eye(Size size);
+    CV_NODISCARD_STD static MatExpr zeros(int rows, int cols);
+    CV_NODISCARD_STD static MatExpr zeros(Size size);
+    CV_NODISCARD_STD static MatExpr zeros(int _ndims, const int* _sizes);
+    CV_NODISCARD_STD static MatExpr ones(int rows, int cols);
+    CV_NODISCARD_STD static MatExpr ones(Size size);
+    CV_NODISCARD_STD static MatExpr ones(int _ndims, const int* _sizes);
+    CV_NODISCARD_STD static MatExpr eye(int rows, int cols);
+    CV_NODISCARD_STD static MatExpr eye(Size size);
 
     //! some more overridden methods
     Mat_& adjustROI( int dtop, int dbottom, int dleft, int dright );
@@ -2469,10 +2469,10 @@ public:
     //!  <0 - a diagonal from the lower half)
     UMat diag(int d=0) const;
     //! constructs a square diagonal matrix which main diagonal is vector "d"
-    static UMat diag(const UMat& d);
+    CV_NODISCARD_STD static UMat diag(const UMat& d);
 
     //! returns deep copy of the matrix, i.e. the data is copied
-    UMat clone() const CV_NODISCARD;
+    CV_NODISCARD_STD UMat clone() const;
     //! copies the matrix content to "m".
     // It calls m.create(this->size(), this->type()).
     void copyTo( OutputArray m ) const;
@@ -2503,14 +2503,14 @@ public:
     double dot(InputArray m) const;
 
     //! Matlab-style matrix initialization
-    static UMat zeros(int rows, int cols, int type);
-    static UMat zeros(Size size, int type);
-    static UMat zeros(int ndims, const int* sz, int type);
-    static UMat ones(int rows, int cols, int type);
-    static UMat ones(Size size, int type);
-    static UMat ones(int ndims, const int* sz, int type);
-    static UMat eye(int rows, int cols, int type);
-    static UMat eye(Size size, int type);
+    CV_NODISCARD_STD static UMat zeros(int rows, int cols, int type);
+    CV_NODISCARD_STD static UMat zeros(Size size, int type);
+    CV_NODISCARD_STD static UMat zeros(int ndims, const int* sz, int type);
+    CV_NODISCARD_STD static UMat ones(int rows, int cols, int type);
+    CV_NODISCARD_STD static UMat ones(Size size, int type);
+    CV_NODISCARD_STD static UMat ones(int ndims, const int* sz, int type);
+    CV_NODISCARD_STD static UMat eye(int rows, int cols, int type);
+    CV_NODISCARD_STD static UMat eye(Size size, int type);
 
     //! allocates new matrix data unless the matrix already has specified size and type.
     // previous data is unreferenced if needed.
@@ -2767,7 +2767,7 @@ public:
     SparseMat& operator = (const Mat& m);
 
     //! creates full copy of the matrix
-    SparseMat clone() const CV_NODISCARD;
+    CV_NODISCARD_STD SparseMat clone() const;
 
     //! copies all the data to the destination matrix. All the previous content of m is erased
     void copyTo( SparseMat& m ) const;
@@ -3004,7 +3004,7 @@ public:
     SparseMat_& operator = (const Mat& m);
 
     //! makes full copy of the matrix. All the elements are duplicated
-    SparseMat_ clone() const CV_NODISCARD;
+    CV_NODISCARD_STD SparseMat_ clone() const;
     //! equivalent to cv::SparseMat::create(dims, _sizes, DataType<_Tp>::type)
     void create(int dims, const int* _sizes);
     //! converts sparse matrix to the old-style CvSparseMat. All the elements are copied
diff --git a/modules/core/include/opencv2/core/matx.hpp b/modules/core/include/opencv2/core/matx.hpp
index 733f675192..f25c8bce57 100644
--- a/modules/core/include/opencv2/core/matx.hpp
+++ b/modules/core/include/opencv2/core/matx.hpp
@@ -146,22 +146,22 @@ public:
     Matx(std::initializer_list<_Tp>); //!< initialize from an initializer list
 #endif
 
-    static Matx all(_Tp alpha);
-    static Matx zeros();
-    static Matx ones();
-    static Matx eye();
-    static Matx diag(const diag_type& d);
+    CV_NODISCARD_STD static Matx all(_Tp alpha);
+    CV_NODISCARD_STD static Matx zeros();
+    CV_NODISCARD_STD static Matx ones();
+    CV_NODISCARD_STD static Matx eye();
+    CV_NODISCARD_STD static Matx diag(const diag_type& d);
     /** @brief Generates uniformly distributed random numbers
     @param a Range boundary.
     @param b The other range boundary (boundaries don't have to be ordered, the lower boundary is inclusive,
     the upper one is exclusive).
      */
-    static Matx randu(_Tp a, _Tp b);
+    CV_NODISCARD_STD static Matx randu(_Tp a, _Tp b);
     /** @brief Generates normally distributed random numbers
     @param a Mean value.
     @param b Standard deviation.
      */
-    static Matx randn(_Tp a, _Tp b);
+    CV_NODISCARD_STD static Matx randn(_Tp a, _Tp b);
 
     //! dot product computed with the default precision
     _Tp dot(const Matx<_Tp, m, n>& v) const;