| // Use ERRP_GUARD() (see include/qapi/error.h) |
| // |
| // Copyright (c) 2020 Virtuozzo International GmbH. |
| // |
| // This program is free software; you can redistribute it and/or |
| // modify it under the terms of the GNU General Public License as |
| // published by the Free Software Foundation; either version 2 of the |
| // License, or (at your option) any later version. |
| // |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| // |
| // You should have received a copy of the GNU General Public License |
| // along with this program. If not, see |
| // <http://www.gnu.org/licenses/>. |
| // |
| // Usage example: |
| // spatch --sp-file scripts/coccinelle/errp-guard.cocci \ |
| // --macro-file scripts/cocci-macro-file.h --in-place \ |
| // --no-show-diff --max-width 80 FILES... |
| // |
| // Note: --max-width 80 is needed because coccinelle default is less |
| // than 80, and without this parameter coccinelle may reindent some |
| // lines which fit into 80 characters but not to coccinelle default, |
| // which in turn produces extra patch hunks for no reason. |
| |
| // Switch unusual Error ** parameter names to errp |
| // (this is necessary to use ERRP_GUARD). |
| // |
| // Disable optional_qualifier to skip functions with |
| // "Error *const *errp" parameter. |
| // |
| // Skip functions with "assert(_errp && *_errp)" statement, because |
| // that signals unusual semantics, and the parameter name may well |
| // serve a purpose. (like nbd_iter_channel_error()). |
| // |
| // Skip util/error.c to not touch, for example, error_propagate() and |
| // error_propagate_prepend(). |
| @ depends on !(file in "util/error.c") disable optional_qualifier@ |
| identifier fn; |
| identifier _errp != errp; |
| @@ |
| |
| fn(..., |
| - Error **_errp |
| + Error **errp |
| ,...) |
| { |
| ( |
| ... when != assert(_errp && *_errp) |
| & |
| <... |
| - _errp |
| + errp |
| ...> |
| ) |
| } |
| |
| // Add invocation of ERRP_GUARD() to errp-functions where // necessary |
| // |
| // Note, that without "when any" the final "..." does not mach |
| // something matched by previous pattern, i.e. the rule will not match |
| // double error_prepend in control flow like in |
| // vfio_set_irq_signaling(). |
| // |
| // Note, "exists" says that we want apply rule even if it does not |
| // match on all possible control flows (otherwise, it will not match |
| // standard pattern when error_propagate() call is in if branch). |
| @ disable optional_qualifier exists@ |
| identifier fn, local_err; |
| symbol errp; |
| @@ |
| |
| fn(..., Error **errp, ...) |
| { |
| + ERRP_GUARD(); |
| ... when != ERRP_GUARD(); |
| ( |
| ( |
| error_append_hint(errp, ...); |
| | |
| error_prepend(errp, ...); |
| | |
| error_vprepend(errp, ...); |
| ) |
| ... when any |
| | |
| Error *local_err = NULL; |
| ... |
| ( |
| error_propagate_prepend(errp, local_err, ...); |
| | |
| error_propagate(errp, local_err); |
| ) |
| ... |
| ) |
| } |
| |
| // Warn when several Error * definitions are in the control flow. |
| // This rule is not chained to rule1 and less restrictive, to cover more |
| // functions to warn (even those we are not going to convert). |
| // |
| // Note, that even with one (or zero) Error * definition in the each |
| // control flow we may have several (in total) Error * definitions in |
| // the function. This case deserves attention too, but I don't see |
| // simple way to match with help of coccinelle. |
| @check1 disable optional_qualifier exists@ |
| identifier fn, _errp, local_err, local_err2; |
| position p1, p2; |
| @@ |
| |
| fn(..., Error **_errp, ...) |
| { |
| ... |
| Error *local_err = NULL;@p1 |
| ... when any |
| Error *local_err2 = NULL;@p2 |
| ... when any |
| } |
| |
| @ script:python @ |
| fn << check1.fn; |
| p1 << check1.p1; |
| p2 << check1.p2; |
| @@ |
| |
| print('Warning: function {} has several definitions of ' |
| 'Error * local variable: at {}:{} and then at {}:{}'.format( |
| fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line)) |
| |
| // Warn when several propagations are in the control flow. |
| @check2 disable optional_qualifier exists@ |
| identifier fn, _errp; |
| position p1, p2; |
| @@ |
| |
| fn(..., Error **_errp, ...) |
| { |
| ... |
| ( |
| error_propagate_prepend(_errp, ...);@p1 |
| | |
| error_propagate(_errp, ...);@p1 |
| ) |
| ... |
| ( |
| error_propagate_prepend(_errp, ...);@p2 |
| | |
| error_propagate(_errp, ...);@p2 |
| ) |
| ... when any |
| } |
| |
| @ script:python @ |
| fn << check2.fn; |
| p1 << check2.p1; |
| p2 << check2.p2; |
| @@ |
| |
| print('Warning: function {} propagates to errp several times in ' |
| 'one control flow: at {}:{} and then at {}:{}'.format( |
| fn, p1[0].file, p1[0].line, p2[0].file, p2[0].line)) |
| |
| // Match functions with propagation of local error to errp. |
| // We want to refer these functions in several following rules, but I |
| // don't know a proper way to inherit a function, not just its name |
| // (to not match another functions with same name in following rules). |
| // Not-proper way is as follows: rename errp parameter in functions |
| // header and match it in following rules. Rename it back after all |
| // transformations. |
| // |
| // The common case is a single definition of local_err with at most one |
| // error_propagate_prepend() or error_propagate() on each control-flow |
| // path. Functions with multiple definitions or propagates we want to |
| // examine manually. Rules check1 and check2 emit warnings to guide us |
| // to them. |
| // |
| // Note that we match not only this "common case", but any function, |
| // which has the "common case" on at least one control-flow path. |
| @rule1 disable optional_qualifier exists@ |
| identifier fn, local_err; |
| symbol errp; |
| @@ |
| |
| fn(..., Error ** |
| - errp |
| + ____ |
| , ...) |
| { |
| ... |
| Error *local_err = NULL; |
| ... |
| ( |
| error_propagate_prepend(errp, local_err, ...); |
| | |
| error_propagate(errp, local_err); |
| ) |
| ... |
| } |
| |
| // Convert special case with goto separately. |
| // I tried merging this into the following rule the obvious way, but |
| // it made Coccinelle hang on block.c |
| // |
| // Note interesting thing: if we don't do it here, and try to fixup |
| // "out: }" things later after all transformations (the rule will be |
| // the same, just without error_propagate() call), coccinelle fails to |
| // match this "out: }". |
| @ disable optional_qualifier@ |
| identifier rule1.fn, rule1.local_err, out; |
| symbol errp; |
| @@ |
| |
| fn(..., Error ** ____, ...) |
| { |
| <... |
| - goto out; |
| + return; |
| ...> |
| - out: |
| - error_propagate(errp, local_err); |
| } |
| |
| // Convert most of local_err related stuff. |
| // |
| // Note, that we inherit rule1.fn and rule1.local_err names, not |
| // objects themselves. We may match something not related to the |
| // pattern matched by rule1. For example, local_err may be defined with |
| // the same name in different blocks inside one function, and in one |
| // block follow the propagation pattern and in other block doesn't. |
| // |
| // Note also that errp-cleaning functions |
| // error_free_errp |
| // error_report_errp |
| // error_reportf_errp |
| // warn_report_errp |
| // warn_reportf_errp |
| // are not yet implemented. They must call corresponding Error* - |
| // freeing function and then set *errp to NULL, to avoid further |
| // propagation to original errp (consider ERRP_GUARD in use). |
| // For example, error_free_errp may look like this: |
| // |
| // void error_free_errp(Error **errp) |
| // { |
| // error_free(*errp); |
| // *errp = NULL; |
| // } |
| @ disable optional_qualifier exists@ |
| identifier rule1.fn, rule1.local_err; |
| expression list args; |
| symbol errp; |
| @@ |
| |
| fn(..., Error ** ____, ...) |
| { |
| <... |
| ( |
| - Error *local_err = NULL; |
| | |
| |
| // Convert error clearing functions |
| ( |
| - error_free(local_err); |
| + error_free_errp(errp); |
| | |
| - error_report_err(local_err); |
| + error_report_errp(errp); |
| | |
| - error_reportf_err(local_err, args); |
| + error_reportf_errp(errp, args); |
| | |
| - warn_report_err(local_err); |
| + warn_report_errp(errp); |
| | |
| - warn_reportf_err(local_err, args); |
| + warn_reportf_errp(errp, args); |
| ) |
| ?- local_err = NULL; |
| |
| | |
| - error_propagate_prepend(errp, local_err, args); |
| + error_prepend(errp, args); |
| | |
| - error_propagate(errp, local_err); |
| | |
| - &local_err |
| + errp |
| ) |
| ...> |
| } |
| |
| // Convert remaining local_err usage. For example, different kinds of |
| // error checking in if conditionals. We can't merge this into |
| // previous hunk, as this conflicts with other substitutions in it (at |
| // least with "- local_err = NULL"). |
| @ disable optional_qualifier@ |
| identifier rule1.fn, rule1.local_err; |
| symbol errp; |
| @@ |
| |
| fn(..., Error ** ____, ...) |
| { |
| <... |
| - local_err |
| + *errp |
| ...> |
| } |
| |
| // Always use the same pattern for checking error |
| @ disable optional_qualifier@ |
| identifier rule1.fn; |
| symbol errp; |
| @@ |
| |
| fn(..., Error ** ____, ...) |
| { |
| <... |
| - *errp != NULL |
| + *errp |
| ...> |
| } |
| |
| // Revert temporary ___ identifier. |
| @ disable optional_qualifier@ |
| identifier rule1.fn; |
| @@ |
| |
| fn(..., Error ** |
| - ____ |
| + errp |
| , ...) |
| { |
| ... |
| } |