Try to annotate a function as raising on returning null

It would be the same semantics of the PyObject* returning functions but
this returns a char* for success.

Currently the annotated function is validated alright but can't validate
the caller just yet, hence the need of a FAKE_RAISE().
This commit is contained in:
Daniele Varrazzo 2019-01-02 11:04:22 +01:00
parent 07b8cfe4a5
commit aabb868ca6
4 changed files with 33 additions and 3 deletions

View File

@ -195,6 +195,13 @@ static double round(double num)
#define RAISES_NEG #define RAISES_NEG
#endif #endif
#if defined(WITH_CPYCHECKER_NULL_RESULT_SETS_EXCEPTION_ATTRIBUTE)
#define RAISES_NULL \
__attribute__((cpychecker_null_result_sets_exception))
#else
#define RAISES_NULL
#endif
#if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE) #if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE)
#define RAISES \ #define RAISES \
__attribute__((cpychecker_sets_exception)) __attribute__((cpychecker_sets_exception))

View File

@ -88,6 +88,7 @@ psyco_curs_close(cursorObject *self, PyObject *dummy)
if (!self->query && self->conn->server_version >= 80200) { if (!self->query && self->conn->server_version >= 80200) {
if (!(lname = psycopg_escape_string( if (!(lname = psycopg_escape_string(
self->conn, self->name, -1, NULL, NULL))) { self->conn, self->name, -1, NULL, NULL))) {
FAKE_RAISE();
goto exit; goto exit;
} }
PyOS_snprintf(buffer, sizeof(buffer), PyOS_snprintf(buffer, sizeof(buffer),
@ -1423,11 +1424,13 @@ psyco_curs_copy_from(cursorObject *self, PyObject *args, PyObject *kwargs)
if (!(quoted_delimiter = psycopg_escape_string( if (!(quoted_delimiter = psycopg_escape_string(
self->conn, sep, -1, NULL, NULL))) { self->conn, sep, -1, NULL, NULL))) {
FAKE_RAISE();
goto exit; goto exit;
} }
if (!(quoted_null = psycopg_escape_string( if (!(quoted_null = psycopg_escape_string(
self->conn, null, -1, NULL, NULL))) { self->conn, null, -1, NULL, NULL))) {
FAKE_RAISE();
goto exit; goto exit;
} }
@ -1515,11 +1518,13 @@ psyco_curs_copy_to(cursorObject *self, PyObject *args, PyObject *kwargs)
if (!(quoted_delimiter = psycopg_escape_string( if (!(quoted_delimiter = psycopg_escape_string(
self->conn, sep, -1, NULL, NULL))) { self->conn, sep, -1, NULL, NULL))) {
FAKE_RAISE();
goto exit; goto exit;
} }
if (!(quoted_null = psycopg_escape_string( if (!(quoted_null = psycopg_escape_string(
self->conn, null, -1, NULL, NULL))) { self->conn, null, -1, NULL, NULL))) {
FAKE_RAISE();
goto exit; goto exit;
} }

View File

@ -47,7 +47,7 @@
* If tolen is set, it will contain the length of the escaped string, * If tolen is set, it will contain the length of the escaped string,
* including quotes. * including quotes.
*/ */
char * RAISES_NULL char *
psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len, psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
char *to, Py_ssize_t *tolen) char *to, Py_ssize_t *tolen)
{ {
@ -467,6 +467,8 @@ psyco_GetDecimalType(void)
} }
#ifdef WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE
/* Transfer ownership of an object to another object's state. /* Transfer ownership of an object to another object's state.
* *
* Work around what seems a bug to the cpychecker which doesn't recognise * Work around what seems a bug to the cpychecker which doesn't recognise
@ -474,10 +476,22 @@ psyco_GetDecimalType(void)
* *
* See davidmalcolm/gcc-python-plugin#109 * See davidmalcolm/gcc-python-plugin#109
*/ */
#ifdef WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE
STEALS(1) IGNORE_REFCOUNT BORROWED PyObject * STEALS(1) IGNORE_REFCOUNT BORROWED PyObject *
TO_STATE(PyObject* obj) TO_STATE(PyObject* obj)
{ {
return obj; return obj;
} }
/* Fake a function raising an exception.
*
* Useful to play together with functions with peculiar semantics such as
* psycopg_escape_string() which doesn't return a PyObject*, but raises an
* exception on NULL. I'm trying to implement a function attribute to raise
* on NULL, similar to raise on negative, but haven't finished it yet.
*/
RAISES IGNORE_REFCOUNT void
FAKE_RAISE()
{
}
#endif #endif

View File

@ -31,7 +31,7 @@ typedef struct cursorObject cursorObject;
typedef struct connectionObject connectionObject; typedef struct connectionObject connectionObject;
typedef struct replicationMessageObject replicationMessageObject; typedef struct replicationMessageObject replicationMessageObject;
HIDDEN char *psycopg_escape_string( HIDDEN RAISES_NULL char *psycopg_escape_string(
connectionObject *conn, connectionObject *conn,
const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen); const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen);
@ -58,10 +58,14 @@ HIDDEN RAISES BORROWED PyObject *psyco_set_error(
HIDDEN PyObject *psyco_GetDecimalType(void); HIDDEN PyObject *psyco_GetDecimalType(void);
/* Using this macro arbitrarily to check if we are under cpychecker.
* Otherwise don't compile this function. */
#ifdef WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE #ifdef WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE
HIDDEN STEALS(1) IGNORE_REFCOUNT BORROWED PyObject *TO_STATE(PyObject* obj); HIDDEN STEALS(1) IGNORE_REFCOUNT BORROWED PyObject *TO_STATE(PyObject* obj);
HIDDEN RAISES HIDDEN void FAKE_RAISE(void);
#else #else
#define TO_STATE(x) x #define TO_STATE(x) x
#define FAKE_RAISE() do {} while (0)
#endif #endif
#endif /* !defined(UTILS_H) */ #endif /* !defined(UTILS_H) */