diff --git a/src/change.h b/src/change.h index a0d08f18..c29ee20f 100644 --- a/src/change.h +++ b/src/change.h @@ -31,9 +31,9 @@ void end_change_chain(View *view) NONNULL_ARGS; bool undo(View *view) NONNULL_ARGS WARN_UNUSED_RESULT; bool redo(View *view, unsigned long change_id) NONNULL_ARGS WARN_UNUSED_RESULT; void free_changes(Change *c) NONNULL_ARGS; -void buffer_insert_bytes(View *view, const char *buf, size_t len) NONNULL_ARG(1); +void buffer_insert_bytes(View *view, const char *buf, size_t len) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3); void buffer_delete_bytes(View *view, size_t len) NONNULL_ARGS; void buffer_erase_bytes(View *view, size_t len) NONNULL_ARGS; -void buffer_replace_bytes(View *view, size_t del_count, const char *ins, size_t ins_count) NONNULL_ARG(1); +void buffer_replace_bytes(View *view, size_t del_count, const char *ins, size_t ins_count) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(3, 4); #endif diff --git a/src/command/macro.h b/src/command/macro.h index 0f4b04d6..fbbcb2ec 100644 --- a/src/command/macro.h +++ b/src/command/macro.h @@ -26,7 +26,7 @@ bool macro_cancel(MacroRecorder *m) WARN_UNUSED_RESULT NONNULL_ARGS; void macro_command_hook(MacroRecorder *m, const char *cmd_name, char **args) NONNULL_ARGS; void macro_search_hook(MacroRecorder *m, const char *pattern, bool reverse, bool add_to_history) NONNULL_ARG(1); void macro_insert_char_hook(MacroRecorder *m, CodePoint c) NONNULL_ARGS; -void macro_insert_text_hook(MacroRecorder *m, const char *text, size_t size) NONNULL_ARG(1); +void macro_insert_text_hook(MacroRecorder *m, const char *text, size_t size) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3); String dump_macro(const MacroRecorder *m) WARN_UNUSED_RESULT NONNULL_ARGS; void free_macro(MacroRecorder *m) NONNULL_ARGS; diff --git a/src/command/parse.h b/src/command/parse.h index e18819c8..580e6d7a 100644 --- a/src/command/parse.h +++ b/src/command/parse.h @@ -13,7 +13,7 @@ typedef enum { CMDERR_UNEXPECTED_EOF, } CommandParseError; -char *parse_command_arg(const CommandRunner *runner, const char *cmd, size_t len) NONNULL_ARG(1) RETURNS_NONNULL; +char *parse_command_arg(const CommandRunner *runner, const char *cmd, size_t len) RETURNS_NONNULL NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3); size_t find_end(const char *cmd, size_t pos, CommandParseError *err) NONNULL_ARGS; CommandParseError parse_commands(const CommandRunner *runner, PointerArray *array, const char *cmd) NONNULL_ARGS WARN_UNUSED_RESULT; const char *command_parse_error_to_string(CommandParseError err) RETURNS_NONNULL; diff --git a/src/ctags.h b/src/ctags.h index 3b49fe29..1317b0cc 100644 --- a/src/ctags.h +++ b/src/ctags.h @@ -15,7 +15,7 @@ typedef struct { bool local; // Indicates if tag is local to file (e.g. "static" in C) } Tag; -NONNULL_ARGS WARN_UNUSED_RESULT +NONNULL_ARGS WARN_UNUSED_RESULT READWRITE(3) WRITEONLY(6) bool next_tag ( const char *buf, size_t buf_len, diff --git a/src/insert.h b/src/insert.h index c83f5a36..3cae0814 100644 --- a/src/insert.h +++ b/src/insert.h @@ -7,7 +7,7 @@ #include "util/unicode.h" #include "view.h" -void insert_text(View *view, const char *text, size_t size, bool move_after) NONNULL_ARG(1); +void insert_text(View *view, const char *text, size_t size, bool move_after) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3); void insert_ch(View *view, CodePoint ch) NONNULL_ARGS; void new_line(View *view, bool above) NONNULL_ARGS; diff --git a/src/join.h b/src/join.h index 002203bc..431d3479 100644 --- a/src/join.h +++ b/src/join.h @@ -5,6 +5,6 @@ #include "view.h" #include "util/macros.h" -void join_lines(View *view, const char *delim, size_t delim_len) NONNULL_ARG(1); +void join_lines(View *view, const char *delim, size_t delim_len) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3); #endif diff --git a/src/regexp.h b/src/regexp.h index 5f21f0ce..3e6ed575 100644 --- a/src/regexp.h +++ b/src/regexp.h @@ -39,7 +39,7 @@ void regexp_compile_or_fatal_error(regex_t *re, const char *pattern, int flags) bool regexp_init_word_boundary_tokens(RegexpWordBoundaryTokens *rwbt) NONNULL_ARGS; bool regexp_error_msg(ErrorBuffer *ebuf, const regex_t *re, const char *pattern, int err) NONNULL_ARG(2, 3); char *regexp_escape(const char *pattern, size_t len) NONNULL_ARGS WARN_UNUSED_RESULT; -size_t regexp_escapeb(char *buf, size_t buflen, const char *pat, size_t plen) NONNULL_ARG(1); +size_t regexp_escapeb(char *buf, size_t buflen, const char *pat, size_t plen) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(3, 4); const InternedRegexp *regexp_intern(ErrorBuffer *ebuf, const char *pattern) NONNULL_ARG(2) WARN_UNUSED_RESULT; bool regexp_is_interned(const char *pattern) NONNULL_ARGS; diff --git a/src/util/macros.h b/src/util/macros.h index 18c828f8..e6656e26 100644 --- a/src/util/macros.h +++ b/src/util/macros.h @@ -277,6 +277,20 @@ #define NONSTRING #endif +// https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-nonnull_005fif_005fnonzero-function-attribute +#if HAS_ATTRIBUTE(nonnull_if_nonzero) + #define NONNULL_ARG_IF_NONZERO_LENGTH(...) __attribute__((__nonnull_if_nonzero__, __VA_ARGS__)) +#else + #define NONNULL_ARG_IF_NONZERO_LENGTH(...) +#endif + +// https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-null_005fterminated_005fstring_005farg-function-attribute +#if HAS_ATTRIBUTE(null_terminated_string_arg) + #define CSTR_ARG(idx) __attribute__((__null_terminated_string_arg__(idx))) +#else + #define CSTR_ARG(idx) +#endif + #if HAS_ATTRIBUTE(counted_by) #define COUNTED_BY(member) __attribute__((counted_by(member))) #else @@ -295,6 +309,17 @@ #define DIAGNOSE_IF(x) #endif +// https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-access-function-attribute +#if HAS_ATTRIBUTE(access) + #define READONLY(...) __attribute__((__access__(read_only, __VA_ARGS__))) // in param + #define WRITEONLY(...) __attribute__((__access__(write_only, __VA_ARGS__))) // out param + #define READWRITE(...) __attribute__((__access__(read_write, __VA_ARGS__))) // in-out param +#else + #define READONLY(...) + #define WRITEONLY(...) + #define READWRITE(...) +#endif + #define UNUSED_ARG(x) unused__ ## x UNUSED #define XMALLOC MALLOC RETURNS_NONNULL #define XSTRDUP XMALLOC NONNULL_ARGS diff --git a/src/util/str-util.h b/src/util/str-util.h index 07168049..c7b88f70 100644 --- a/src/util/str-util.h +++ b/src/util/str-util.h @@ -91,10 +91,10 @@ static inline void strn_replace_byte(char *str, size_t n, char byte, char rep) // Extract a substring between `buf + pos` and either the next `delim` // byte (if found) or `buf + size` (the remainder of the string). The -// substring is returned as a StringView and the `posp` in-out-param +// substring is returned as a StringView and the `posp` in-out param // is set to the offset one byte after the found delimiter (or to the // end of the size-bounded string, if no delimiter was found). -NONNULL_ARGS +NONNULL_ARGS READONLY(1, 3) READWRITE(2) static inline StringView get_delim(const char *buf, size_t *posp, size_t size, int delim) { size_t pos = *posp; diff --git a/src/util/string-view.h b/src/util/string-view.h index 370300bb..c890b470 100644 --- a/src/util/string-view.h +++ b/src/util/string-view.h @@ -80,13 +80,13 @@ static inline bool strview_equal_cstring_icase(const StringView *sv, const char return strview_equal_strn_icase(sv, str, strlen(str)); } -NONNULL_ARG(1) +NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3) static inline bool strview_has_strn_prefix(const StringView *sv, const char *p, size_t n) { return sv->length >= n && mem_equal(sv->data, p, n); } -NONNULL_ARG(1) +NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3) static inline bool strview_has_strn_suffix(const StringView *sv, const char *suf, size_t suflen) { // See also: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3261.pdf diff --git a/src/util/string.h b/src/util/string.h index 85d604e6..a7561891 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -23,7 +23,7 @@ typedef struct { #define string_append_literal(s, x) string_append_buf(s, x, STRLEN(x)) -void string_append_buf(String *s, const char *ptr, size_t len) NONNULL_ARG(1); +void string_append_buf(String *s, const char *ptr, size_t len) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3); static inline String string_new(size_t size) { @@ -73,7 +73,7 @@ char *string_reserve_space(String *s, size_t more) NONNULL_ARGS_AND_RETURN; void string_append_byte(String *s, unsigned char byte) NONNULL_ARGS; size_t string_append_codepoint(String *s, CodePoint u) NONNULL_ARGS; size_t string_insert_codepoint(String *s, size_t pos, CodePoint u) NONNULL_ARGS; -void string_insert_buf(String *s, size_t pos, const char *buf, size_t len) NONNULL_ARG(1); +void string_insert_buf(String *s, size_t pos, const char *buf, size_t len) NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(3, 4); void string_append_memset(String *s, unsigned char byte, size_t len) NONNULL_ARGS; void string_sprintf(String *s, const char *fmt, ...) PRINTF(2) NONNULL_ARGS; char *string_steal_cstring(String *s) NONNULL_ARGS_AND_RETURN;