This section describes how some properties of built-in functions can be transferred to user-defined functions by means of the option -function. See also -printf and -scanf . See also Section 9.2 Semantic Specification to see how to create custom function semantics.
PC-lint Plus is aware of the properties (which we will call semantics) of many standard functions, which we refer
to as special functions. A complete list of such functions is shown in Section 9.1.2 Function Listing
.
For example, function fopen() is recognized as a special function. Its two arguments are checked for the possibility of being the NULL pointer and its return value is considered possibly NULL. Similarly, fclose is regarded as a special function whose one argument is also checked for NULL. Thus, the code:
if( name ) printf ( "ok\n" ); f = fopen( name, "r" ); // Warning! name may be NULL fclose ( f ); // Warning! f may be NULL
will be greeted with the diagnostics indicated. You may transfer all three semantics of fopen to a function of your own, say myopen, by using the option
-function( fopen, myopen )
Then, PC-lint Plus would also check the 1st and 2nd arguments of myopen for NULL and assume that the returned pointer could possibly be NULL. In general, the syntax of -function is described as follows:
-function( Function0, Function1 [, Funcion2] ... )
specifies that Function1, Function2, etc. are like Function0 in that they exhibit special properties normally
associated with Function0.
The arguments to -function may be subscripted. For example, if myopen were to check its 2nd and 3rd arguments for NULL rather than its 1st and 2nd we could do the following:
-function( fopen(1), myopen(2) ) -function( fopen(2), myopen(3) )
This would transfer the semantics of NULL checking to the 2nd and 3rd arguments of myopen. This could be simplified to
-function( fopen(1), myopen(2), myopen(3) )
since the property of fopen(1) is identical to that of fopen(2). Any previous semantics associated with the 2nd and 3rd arguments to myopen would be lost. To transfer the return semantics you may use the option
-function( fopen(r), myopen(r) )
Some functions have a semantic that is not decomposable to a single argument or return value but is rather a combined property of the entire function. For example
char * fread( char *, size_t, size_t, FILE * );
has, in addition to the check-for-NULL semantics on its 1st and 4th arguments, and the check-for-negative semantics on the 2nd and 3rd arguments, an additional check to see if the size of argument 2 multiplied by argument 3 exceeds the buffer size given as the 1st argument. This condition is identified as semantic fread in Section 9.1.2 Function Listing . Thus
char buf[100]; fread( buf, 100, 2, f ); // Warning
To transfer this function-wide property to some other function we need to use the 0 (zero) index. Thus
-function( fread(0), myread(0) )
will transfer just the overflow checking (fread as described above) and not the argument checking. That is, of the
semantics appearing in Section 9.1.2 Function Listing for row labeled fread, the semantics transferred are only
those marked with an asterisk.
As a convenience, the subscript need not be repeated if it is the same as the 1st argument. Thus
-function( fread(0), myread )
is equivalent to the earlier option.
Just as in the case of fopen you may transfer all the properties of fread to your own function by not using a subscript as in:
function( fread, myread )
You may remove any or all of these semantics from a special function by not using a 2nd argument to the -function option. Thus
-function( fread )
will remove all of the semantics of the fread function and
-function( fread(0) )
removes only the special semantics described above.
In summary, an option of the form
-function( function(index), ...)
copies a single semantic into a destination or destinations. An option of the form
-function( function, ... )
copies all of a function’s semantics.
You may transfer semantics to member functions as well as non-member functions. Thus
-function( exit, X::quit )
transfers the properties of exit() to X::quit(). The semantics in this case is simply that the function is not
expected to return.
As another example involving member functions consider the following:
//lint -function( strlen(1), X::X(1), X::g ) // both X::X() and X::g() should have their 1st // argument checked for NULL. //lint +fpn pointer parameters may be NULL class X { public: char *buf; X(char *); void g(char *); }; void f(char *p) { // p may be NULL because of +fpn X x(p); // Warning 668 x.g(p); }
In this example, the semantics associated with the 1st argument of strlen are transferred to the 1st argument of
X::X and to the 1st argument of X::g. As the example illustrates, when we speak of the nth argument passed to a
member function we are ignoring in our count the implicit argument that is a pointer to the class (this is always
checked for NULL).
No distinction is made among overloaded functions. Thus, if X::X( int * ) is checked for NULL then so is X::X( char * ). If there is an X::X( int ) then its argument is not checked because its argument is not a pointer. If there is an X::X( int *, char * ) then the 1st argument is checked, but not the 2nd. User-defined semantics can be applied to individual function overloads or function template instantiations, see Section 9.2 Semantic Specifications for details.
| DeleteCriticalSection |
| 1p mutex(1) mutex_destroy |
| EnterCriticalSection |
| 1p mutex(1) mutex_lock |
| InitializeCriticalSection |
| 1p mutex(1) mutex_is_recursive(1) mutex_initialize |
| InitializeCriticalSectionAndSpinCount |
| 1p mutex(1) mutex_is_recursive(1) mutex_initialize |
| IsBadCodePtr |
| dangerous |
| IsBadHugeReadPtr |
| dangerous |
| IsBadHugeWritePtr |
| dangerous |
| IsBadReadPtr |
| dangerous |
| IsBadStringPtrA |
| dangerous |
| IsBadStringPtrW |
| dangerous |
| IsBadWritePtr |
| dangerous |
| LeaveCriticalSection |
| 1p mutex(1) mutex_unlock |
| QExplicitlySharedDataPointer::QExplicitlySharedDataPointer |
| shared_ptr_constructor |
| QMutex::QMutex |
| mutex(t) mutex_is_recursive(1) mutex_initialize |
| QMutex::lock |
| mutex(t) mutex_lock |
| QMutex::tryLock |
| mutex(t) try_lock_true mutex_lock |
| QMutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| QMutex::try_lock_for |
| mutex(t) try_lock_true mutex_lock |
| QMutex::try_lock_until |
| mutex(t) try_lock_true mutex_lock |
| QMutex::unlock |
| mutex(t) mutex_unlock |
| QMutexLocker::QMutexLocker |
| mutex_locker(t) mutex(1) locker_create |
| QMutexLocker::isLocked |
| mutex_locker(t) try_lock_true locker_owns |
| QMutexLocker::mutex |
| mutex_locker(t) locker_fetch |
| QMutexLocker::relock |
| mutex_locker(t) locker_lock |
| QMutexLocker::swap |
| mutex_locker(t) mutex_locker(1) locker_swap |
| QMutexLocker::unlock |
| mutex_locker(t) locker_unlock |
| QPointer::QPointer |
| weak_ptr_constructor |
| QReadLocker::QReadLocker |
| mutex_is_shared(t) mutex_locker(t) mutex(1) locker_create |
| QReadLocker::readWriteLock |
| mutex_locker(t) locker_fetch |
| QReadLocker::relock |
| mutex_locker(t) locker_lock_shared |
| QReadLocker::unlock |
| mutex_locker(t) locker_unlock |
| QReadWriteLock::QReadWriteLock |
| mutex(t) mutex_is_shared(t) mutex_is_recursive(1) mutex_initialize |
| QReadWriteLock::lockForRead |
| mutex(t) mutex_lock_shared |
| QReadWriteLock::lockForWrite |
| mutex(t) mutex_lock |
| QReadWriteLock::tryLockForRead |
| mutex(t) try_lock_true mutex_lock_shared |
| QReadWriteLock::tryLockForWrite |
| mutex(t) try_lock_true mutex_lock |
| QReadWriteLock::unlock |
| mutex(t) mutex_unlock |
| QRecursiveMutex::QRecursiveMutex |
| mutex(t) mutex_is_recursive(t) mutex_initialize |
| QRecursiveMutex::lock |
| mutex(t) mutex_lock |
| QRecursiveMutex::tryLock |
| mutex(t) try_lock_true mutex_lock |
| QRecursiveMutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| QRecursiveMutex::try_lock_for |
| mutex(t) try_lock_true mutex_lock |
| QRecursiveMutex::try_lock_until |
| mutex(t) try_lock_true mutex_lock |
| QRecursiveMutex::unlock |
| mutex(t) mutex_unlock |
| QScopedPointer::QScopedPointer |
| unique_ptr_constructor |
| QSharedDataPointer::QSharedDataPointer |
| shared_ptr_constructor |
| QSharedPointer::QSharedPointer |
| shared_ptr_constructor |
| QWeakPointer::QWeakPointer |
| weak_ptr_constructor |
| QWriteLocker::QWriteLocker |
| mutex_locker(t) mutex(1) locker_create |
| QWriteLocker::readWriteLock |
| mutex_locker(t) locker_fetch |
| QWriteLocker::relock |
| mutex_locker(t) locker_lock |
| QWriteLocker::unlock |
| mutex_locker(t) locker_unlock |
| TryEnterCriticalSection |
| 1p mutex(1) try_lock_one mutex_lock |
| _Exit |
| async_signal_safe r_no |
| __assert |
| *assert |
| __lint_assert |
| *assert |
| _putenv |
| 1p |
| _putenv_s |
| 1p 2p |
| _wsetlocale |
| r_null |
| abort |
| async_signal_safe r_no |
| acos |
| *dom_1 |
| acosf |
| *dom_1 |
| acosh |
| *dom_lt1 |
| acoshf |
| *dom_lt1 |
| acoshl |
| *dom_lt1 |
| acosl |
| *dom_1 |
| asctime |
| 1p |
| asctime_s |
| 1p chneg(2) 3p |
| asin |
| *dom_1 |
| asinf |
| *dom_1 |
| asinl |
| *dom_1 |
| at_quick_exit |
| 1p |
| atanh |
| *dom_1 |
| atanhf |
| *dom_1 |
| atanhl |
| *dom_1 |
| atexit |
| 1p |
| atof |
| 1p |
| atoi |
| 1p |
| atol |
| 1p |
| atoll |
| 1p |
| atomic_compare_exchange_strong |
| 1p thread_atomic(1) 2p |
| atomic_compare_exchange_strong_explicit |
| 1p thread_atomic(1) 2p |
| atomic_compare_exchange_weak |
| 1p thread_atomic(1) 2p |
| atomic_compare_exchange_weak_explicit |
| 1p thread_atomic(1) 2p |
| atomic_exchange |
| 1p thread_atomic(1) |
| atomic_exchange_explicit |
| 1p thread_atomic(1) |
| atomic_fetch_add |
| 1p thread_atomic(1) |
| atomic_fetch_add_explicit |
| 1p thread_atomic(1) |
| atomic_fetch_and |
| 1p thread_atomic(1) |
| atomic_fetch_and_explicit |
| 1p thread_atomic(1) |
| atomic_fetch_or |
| 1p thread_atomic(1) |
| atomic_fetch_or_explicit |
| 1p thread_atomic(1) |
| atomic_fetch_sub |
| 1p thread_atomic(1) |
| atomic_fetch_sub_explicit |
| 1p thread_atomic(1) |
| atomic_fetch_xor |
| 1p thread_atomic(1) |
| atomic_fetch_xor_explicit |
| 1p thread_atomic(1) |
| atomic_flag_clear |
| 1p thread_atomic(1) |
| atomic_flag_clear_explicit |
| 1p thread_atomic(1) |
| atomic_flag_test_and_set |
| 1p thread_atomic(1) |
| atomic_flag_test_and_set_explicit |
| 1p thread_atomic(1) |
| atomic_init |
| 1p thread_non_atomic(1) |
| atomic_load |
| 1p thread_atomic(1) |
| atomic_load_explicit |
| 1p thread_atomic(1) |
| atomic_store |
| 1p thread_atomic(1) |
| atomic_store_explicit |
| 1p thread_atomic(1) |
| bsearch |
| 1p 2p chneg(3) chneg(4) 5p r_null |
| bsearch_s |
| chneg(3) chneg(4) |
| call_once |
| 1p 2p |
| calloc |
| chneg(1) chneg(2) r_null *calloc |
| catgets |
| 4p |
| clearerr |
| 1p stream_placid(1) |
| clearerr_unlocked |
| 1p stream_placid(1) |
| cnd_broadcast |
| 1p |
| cnd_destroy |
| 1p |
| cnd_init |
| 1p |
| cnd_signal |
| 1p |
| cnd_timedwait |
| 1p 2p mutex(2) mutex_must_be_locked(2) 3p mutex_validate |
| cnd_wait |
| 1p 2p mutex(2) mutex_must_be_locked(2) mutex_validate |
| crypt |
| 1p 2p |
| ctime |
| 1p |
| ctime_s |
| 1p chneg(2) 3p |
| dbm_clearerr |
| 1p |
| dbm_close |
| 1p |
| dbm_delete |
| 1p |
| dbm_error |
| 1p |
| dbm_fetch |
| 1p |
| dbm_firstkey |
| 1p |
| dbm_nextkey |
| 1p |
| dbm_open |
| 1p |
| dbm_store |
| 1p |
| encrypt |
| 1p |
| exit |
| r_no |
| fclose |
| 1p custodial(1) stream_close(1) |
| feof |
| 1p stream_placid(1) |
| feof_unlocked |
| 1p stream_placid(1) |
| ferror |
| 1p stream_placid(1) |
| ferror_unlocked |
| 1p stream_placid(1) |
| fflush |
| stream_flush(1) |
| fflush_unlocked |
| 1p stream_flush(1) |
| fgetc |
| 1p stream_read(1) stream_byte(1) |
| fgetc_unlocked |
| 1p stream_read(1) stream_byte(1) |
| fgetpos |
| 1p 2p |
| fgets |
| 1p chneg(2) 3p stream_read(3) stream_byte(3) r_null *fgets |
| fgets_unlocked |
| 1p chneg(2) 3p stream_read(3) stream_byte(3) r_null *fgets |
| fgetwc |
| 1p stream_read(1) stream_wide(1) |
| fgetwc_unlocked |
| 1p stream_read(1) stream_wide(1) |
| fgetws |
| 1p chneg(2) 3p stream_read(3) stream_wide(3) r_null *fgets |
| fgetws_unlocked |
| 1p chneg(2) 3p stream_read(3) stream_wide(3) r_null *fgets |
| fileno |
| 1p stream_placid(1) |
| fileno_unlocked |
| 1p stream_placid(1) |
| flockfile |
| 1p stream_placid(1) |
| fopen |
| 1p 2p r_null *fopen |
| fopen_s |
| 1p 2p 3p |
| fprintf |
| 1p stream_write(1) stream_byte(1) 2p printf(2) |
| fprintf_s |
| 1p stream_write(1) stream_byte(1) 2p printf(2) |
| fputc |
| 2p stream_write(2) stream_byte(2) |
| fputc_unlocked |
| 2p stream_write(2) stream_byte(2) |
| fputs |
| 1p 2p stream_write(2) stream_byte(2) |
| fputs_unlocked |
| 1p 2p stream_write(2) stream_byte(2) |
| fputwc |
| 2p stream_write(2) stream_wide(2) |
| fputwc_unlocked |
| 2p stream_write(2) stream_wide(2) |
| fputws |
| 1p 2p stream_write(2) stream_wide(2) |
| fputws_unlocked |
| 1p 2p stream_write(2) stream_wide(2) |
| fread |
| 1p chneg(2) chneg(3) 4p stream_read(4) stream_byte(4) *fread |
| fread_unlocked |
| 1p chneg(2) chneg(3) 4p stream_read(4) stream_byte(4) *fread |
| free |
| *free |
| freopen |
| 2p 3p r_null *freopen |
| freopen_s |
| 1p 3p 4p |
| frexp |
| 2p |
| frexpf |
| 2p |
| frexpl |
| 2p |
| fscanf |
| 1p stream_read(1) stream_byte(1) 2p scanf(2) |
| fscanf_s |
| 1p stream_read(1) stream_byte(1) 2p scanf(2) |
| fseek |
| 1p stream_pos(1) |
| fsetpos |
| 1p stream_pos(1) 2p |
| ftell |
| 1p |
| ftrylockfile |
| 1p stream_placid(1) |
| ftw |
| 1p 2p |
| funlockfile |
| 1p stream_placid(1) |
| fwide |
| 1p stream_placid(1) |
| fwprintf |
| 1p stream_write(1) stream_wide(1) 2p |
| fwprintf_s |
| 1p stream_write(1) stream_wide(1) 2p |
| fwrite |
| 1p chneg(2) chneg(3) 4p stream_write(4) stream_byte(4) *fwrite |
| fwrite_unlocked |
| 1p chneg(2) chneg(3) 4p stream_write(4) stream_byte(4) *fwrite |
| fwscanf |
| 1p stream_read(1) stream_wide(1) 2p |
| fwscanf_s |
| 1p stream_read(1) stream_wide(1) 2p |
| getc |
| 1p stream_read(1) stream_byte(1) |
| getc_unlocked |
| 1p stream_read(1) stream_byte(1) |
| getdate |
| 1p |
| getenv |
| 1p r_null |
| getenv_s |
| chneg(3) 4p |
| getgrnam |
| 1p |
| getnetbyname |
| 1p |
| getopt |
| 2p 3p |
| getprotobyname |
| 1p |
| getpwnam |
| 1p |
| gets |
| 1p dangerous r_null |
| gets_s |
| 1p chneg(2) |
| getservbyname |
| 1p |
| getutxid |
| 1p |
| getutxline |
| 1p |
| getwc |
| 1p stream_read(1) stream_wide(1) |
| getwc_unlocked |
| 1p stream_read(1) stream_wide(1) |
| gmtime |
| 1p r_null |
| gmtime_s |
| 1p 2p |
| localtime |
| 1p r_null |
| localtime_s |
| 1p 2p |
| log |
| *dom_lt0 |
| log10 |
| *dom_lt0 |
| log10f |
| *dom_lt0 |
| log10l |
| *dom_lt0 |
| log1p |
| *dom_ltn1 |
| log1pf |
| *dom_ltn1 |
| log1pl |
| *dom_ltn1 |
| log2 |
| *dom_lt0 |
| log2f |
| *dom_lt0 |
| log2l |
| *dom_lt0 |
| logf |
| *dom_lt0 |
| logl |
| *dom_lt0 |
| longjmp |
| 1p r_no |
| malloc |
| chneg(1) r_null *malloc |
| mbsnrtowcs |
| 2p |
| mbsrtowcs |
| 2p chneg(3) |
| mbsrtowcs_s |
| 1p chneg(3) 4p chneg(5) 6p |
| mbstowcs |
| 1p 2p chneg(3) *mbstowcs |
| mbstowcs_s |
| 1p chneg(3) 4p chneg(5) |
| memchr |
| 1p pod(1) chneg(3) r_null *memchr |
| memcmp |
| 1p pod(1) 2p pod(2) chneg(3) *memcmp |
| memcpy |
| 1p pod(1) 2p pod(2) chneg(3) *memcpy |
| memcpy_s |
| 1p pod(1) chneg(2) 3p pod(3) chneg(4) |
| memmove |
| 1p pod(1) 2p pod(2) chneg(3) *memmove |
| memmove_s |
| 1p pod(1) chneg(2) 3p pod(3) chneg(4) |
| memset |
| 1p pod(1) chneg(3) *memset |
| memset_s |
| 1p pod(1) chneg(2) chneg(3) |
| mktime |
| 1p inout(1) |
| modf |
| 2p |
| modff |
| 2p |
| modfl |
| 2p |
| mtx_destroy |
| 1p mutex(1) mutex_destroy |
| mtx_init |
| 1p mutex(1) mutex_is_recursive(2) mutex_initialize_std_c |
| mtx_lock |
| 1p mutex(1) try_lock_std_c mutex_lock |
| mtx_timedlock |
| 1p mutex(1) 2p try_lock_std_c mutex_lock |
| mtx_trylock |
| 1p mutex(1) try_lock_std_c mutex_lock |
| mtx_unlock |
| 1p mutex(1) mutex_unlock |
| nftw |
| 1p 2p |
| perror |
| 1p |
| printf |
| 1p printf(1) |
| printf_s |
| 1p printf(1) |
| pthread_cond_broadcast |
| 1p thread_immune(1) mutex_ignore |
| pthread_cond_destroy |
| 1p mutex_ignore |
| pthread_cond_init |
| 1p mutex_ignore |
| pthread_cond_signal |
| 1p thread_immune(1) mutex_ignore |
| pthread_cond_timedwait |
| 1p 2p mutex(2) 3p mutex_validate |
| pthread_cond_wait |
| 1p 2p mutex(2) mutex_validate |
| pthread_create |
| 1p 3p thread_create(3) thread_args(4) mutex_ignore |
| pthread_mutex_consistent |
| 1p mutex(1) mutex_validate |
| pthread_mutex_destroy |
| 1p mutex(1) mutex_destroy |
| pthread_mutex_getprioceiling |
| 1p mutex(1) 2p mutex_validate |
| pthread_mutex_init |
| 1p mutex(1) mutex_attribute(2) mutex_initialize |
| pthread_mutex_lock |
| 1p mutex(1) try_lock_zero mutex_lock |
| pthread_mutex_setprioceiling |
| 1p mutex(1) 3p mutex_validate |
| pthread_mutex_timedlock |
| 1p mutex(1) 2p try_lock_zero mutex_lock |
| pthread_mutex_trylock |
| 1p mutex(1) try_lock_zero mutex_lock |
| pthread_mutex_unlock |
| 1p mutex(1) mutex_unlock |
| pthread_mutexattr_destroy |
| 1p mutex_attribute(1) mutex_attribute_destroy |
| pthread_mutexattr_getprioceiling |
| 1p mutex_attribute(1) 2p mutex_validate |
| pthread_mutexattr_getprotocol |
| 1p mutex_attribute(1) 2p mutex_validate |
| pthread_mutexattr_getpshared |
| 1p mutex_attribute(1) 2p mutex_validate |
| pthread_mutexattr_getrobust |
| 1p mutex_attribute(1) 2p mutex_validate |
| pthread_mutexattr_gettype |
| 1p mutex_attribute(1) 2p mutex_validate |
| pthread_mutexattr_init |
| 1p mutex_attribute(1) mutex_attribute_initialize |
| pthread_mutexattr_setprioceiling |
| 1p mutex_attribute(1) mutex_validate |
| pthread_mutexattr_setprotocol |
| 1p mutex_attribute(1) mutex_validate |
| pthread_mutexattr_setpshared |
| 1p mutex_attribute(1) mutex_validate |
| pthread_mutexattr_setrobust |
| 1p mutex_attribute(1) mutex_validate |
| pthread_mutexattr_settype |
| 1p mutex_attribute(1) mutex_is_recursive(2) mutex_attribute_set |
| pthread_rwlock_destroy |
| 1p mutex(1) mutex_destroy |
| pthread_rwlock_init |
| 1p mutex(1) mutex_is_shared(1) mutex_attribute(2) mutex_initialize |
| pthread_rwlock_rdlock |
| 1p mutex(1) try_lock_zero mutex_lock_shared |
| pthread_rwlock_tryrdlock |
| 1p mutex(1) try_lock_zero mutex_lock_shared |
| pthread_rwlock_trywrlock |
| 1p mutex(1) try_lock_zero mutex_lock |
| pthread_rwlock_unlock |
| 1p mutex(1) mutex_unlock |
| pthread_rwlock_wrlock |
| 1p mutex(1) try_lock_zero mutex_lock |
| pthread_rwlockattr_destroy |
| 1p mutex_attribute(1) mutex_attribute_destroy |
| pthread_rwlockattr_getpshared |
| 1p mutex_attribute(1) 2p mutex_validate |
| pthread_rwlockattr_init |
| 1p mutex_attribute(1) mutex_is_recursive(1) mutex_is_shared(1) mutex_attribute_initialize |
| pthread_rwlockattr_setpshared |
| 1p mutex_attribute(1) mutex_validate |
| putc |
| 2p stream_write(2) stream_byte(2) |
| putc_unlocked |
| 2p stream_write(2) stream_byte(2) |
| putenv |
| 1p |
| puts |
| 1p |
| pututxline |
| 1p |
| putwc |
| 2p stream_write(2) stream_wide(2) |
| putwc_unlocked |
| 2p stream_write(2) stream_wide(2) |
| qsort |
| 1p inout(1) chneg(2) chneg(3) 4p |
| qsort_s |
| inout(1) chneg(2) chneg(3) |
| quick_exit |
| async_signal_safe r_no |
| readdir |
| 1p |
| realloc |
| r_null *realloc |
| remove |
| 1p |
| rename |
| 1p 2p |
| rewind |
| 1p stream_pos(1) |
| scanf |
| 1p scanf(1) |
| scanf_s |
| 1p scanf(1) |
| setbuf |
| 1p stream_placid(1) |
| setbuffer |
| 1p stream_placid(1) |
| setenv |
| 1p 2p |
| setkey |
| 1p |
| setlinebuf |
| 1p stream_placid(1) |
| setlocale |
| r_null |
| setvbuf |
| 1p stream_placid(1) |
| sigaction |
| signal_register(2) |
| signal |
| signal_register(2) |
| snprintf |
| chneg(2) 3p printf(3) *sprintf |
| snprintf_s |
| 1p chneg(2) 3p printf(3) *sprintf |
| snwprintf_s |
| 1p chneg(2) 3p printf(3) *sprintf |
| sprintf |
| 1p 2p printf(2) *sprintf |
| sprintf_s |
| 1p chneg(2) 3p printf(3) *sprintf |
| sqrt |
| *dom_lt0 |
| sqrtf |
| *dom_lt0 |
| sqrtl |
| *dom_lt0 |
| sscanf |
| 1p 2p scanf(2) |
| sscanf_s |
| 1p 2p scanf(2) |
| std::async |
| async |
| std::atomic::atomic |
| thread_non_atomic(t) |
| std::atomic_flag::atomic_flag |
| thread_non_atomic(t) |
| std::auto_ptr::auto_ptr |
| unique_ptr_constructor |
| std::condition_variable::condition_variable |
| mutex_ignore |
| std::condition_variable::native_handle |
| mutex_ignore |
| std::condition_variable::notify_all |
| thread_immune(t) mutex_ignore |
| std::condition_variable::notify_one |
| thread_immune(t) mutex_ignore |
| std::condition_variable::wait |
| thread_immune(t) mutex_locker(1) mutex_must_be_locked(1) mutex_validate |
| std::condition_variable::wait_for |
| thread_immune(t) mutex_locker(1) mutex_must_be_locked(1) mutex_validate |
| std::condition_variable::wait_until |
| thread_immune(t) mutex_locker(1) mutex_must_be_locked(1) mutex_validate |
| std::condition_variable_any::condition_variable_any |
| mutex_ignore |
| std::condition_variable_any::native_handle |
| mutex_ignore |
| std::condition_variable_any::notify_all |
| thread_immune(t) mutex_ignore |
| std::condition_variable_any::notify_one |
| thread_immune(t) mutex_ignore |
| std::condition_variable_any::wait |
| thread_immune(t) mutex(1) mutex_must_be_locked(1) mutex_validate |
| std::condition_variable_any::wait_for |
| thread_immune(t) mutex(1) mutex_must_be_locked(1) mutex_validate |
| std::condition_variable_any::wait_until |
| thread_immune(t) mutex(1) mutex_must_be_locked(1) mutex_validate |
| std::forward |
| forward(1) |
| std::forward_like |
| forward(1) |
| std::jthread::jthread |
| thread_create(1) thread_args(2) mutex_ignore |
| std::lock |
| mutex_remaining(1) mutex_lock |
| std::lock_guard::lock_guard |
| mutex_locker(t) mutex(1) mutex_tag_adopt(2) locker_create |
| std::move |
| move(1) |
| std::move_if_noexcept |
| move(1) |
| std::mutex::lock |
| mutex(t) mutex_lock |
| std::mutex::mutex |
| mutex(t) mutex_attribute(t) mutex_initialize |
| std::mutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| std::mutex::unlock |
| mutex(t) mutex_unlock |
| std::recursive_mutex::lock |
| mutex(t) mutex_lock |
| std::recursive_mutex::recursive_mutex |
| mutex(t) mutex_is_recursive(t) mutex_initialize |
| std::recursive_mutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| std::recursive_mutex::unlock |
| mutex(t) mutex_unlock |
| std::recursive_timed_mutex::lock |
| mutex(t) mutex_lock |
| std::recursive_timed_mutex::recursive_timed_mutex |
| mutex(t) mutex_is_recursive(t) mutex_initialize |
| std::recursive_timed_mutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| std::recursive_timed_mutex::try_lock_for |
| mutex(t) try_lock_true mutex_lock |
| std::recursive_timed_mutex::try_lock_until |
| mutex(t) try_lock_true mutex_lock |
| std::recursive_timed_mutex::unlock |
| mutex(t) mutex_unlock |
| std::scoped_lock::scoped_lock |
| mutex_locker(t) mutex_remaining(1) mutex_tag_adopt(1) locker_create |
| std::shared_lock::lock |
| mutex_locker(t) locker_lock_shared |
| std::shared_lock::mutex |
| mutex_locker(t) locker_fetch |
| std::shared_lock::owns_lock |
| mutex_locker(t) try_lock_true locker_owns |
| std::shared_lock::release |
| mutex_locker(t) locker_release |
| std::shared_lock::shared_lock |
| mutex_is_shared(t) mutex_locker(t) mutex(1) mutex_tag_adopt(2) mutex_tag_defer(2) mutex_tag_try_to_lock(2) locker_create |
| std::shared_lock::swap |
| mutex_locker(t) mutex_locker(1) locker_swap |
| std::shared_lock::try_lock |
| mutex_locker(t) try_lock_true locker_lock_shared |
| std::shared_lock::try_lock_for |
| mutex_locker(t) try_lock_true locker_lock_shared |
| std::shared_lock::try_lock_until |
| mutex_locker(t) try_lock_true locker_lock_shared |
| std::shared_lock::unlock |
| mutex_locker(t) locker_unlock |
| std::shared_mutex::lock |
| mutex(t) mutex_lock |
| std::shared_mutex::lock_shared |
| mutex(t) mutex_lock_shared |
| std::shared_mutex::shared_mutex |
| mutex(t) mutex_is_shared(t) mutex_initialize |
| std::shared_mutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| std::shared_mutex::try_lock_shared |
| mutex(t) try_lock_true mutex_lock |
| std::shared_mutex::unlock |
| mutex(t) mutex_unlock |
| std::shared_mutex::unlock_shared |
| mutex(t) mutex_unlock_shared |
| std::shared_ptr::shared_ptr |
| shared_ptr_constructor |
| std::shared_timed_mutex::lock |
| mutex(t) mutex_lock |
| std::shared_timed_mutex::lock_shared |
| mutex(t) mutex_lock_shared |
| std::shared_timed_mutex::shared_timed_mutex |
| mutex(t) mutex_is_shared(t) mutex_initialize |
| std::shared_timed_mutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| std::shared_timed_mutex::try_lock_for |
| mutex(t) try_lock_true mutex_lock |
| std::shared_timed_mutex::try_lock_shared |
| mutex(t) try_lock_true mutex_lock_shared |
| std::shared_timed_mutex::try_lock_shared_for |
| mutex(t) try_lock_true mutex_lock_shared |
| std::shared_timed_mutex::try_lock_shared_until |
| mutex(t) try_lock_true mutex_lock_shared |
| std::shared_timed_mutex::try_lock_until |
| mutex(t) try_lock_true mutex_lock |
| std::shared_timed_mutex::unlock |
| mutex(t) mutex_unlock |
| std::shared_timed_mutex::unlock_shared |
| mutex(t) mutex_unlock_shared |
| std::thread::thread |
| thread_create(1) thread_args(2) mutex_ignore |
| std::timed_mutex::lock |
| mutex(t) mutex_lock |
| std::timed_mutex::timed_mutex |
| mutex(t) mutex_attribute(t) mutex_initialize |
| std::timed_mutex::try_lock |
| mutex(t) try_lock_true mutex_lock |
| std::timed_mutex::try_lock_for |
| mutex(t) try_lock_true mutex_lock |
| std::timed_mutex::try_lock_until |
| mutex(t) try_lock_true mutex_lock |
| std::timed_mutex::unlock |
| mutex(t) mutex_unlock |
| std::try_lock |
| mutex_remaining(1) try_lock_neg_1 mutex_lock |
| std::uncaught_exception |
| dangerous |
| std::unique_lock::lock |
| mutex_locker(t) locker_lock |
| std::unique_lock::mutex |
| mutex_locker(t) locker_fetch |
| std::unique_lock::owns_lock |
| mutex_locker(t) try_lock_true locker_owns |
| std::unique_lock::release |
| mutex_locker(t) locker_release |
| std::unique_lock::swap |
| mutex_locker(t) mutex_locker(1) locker_swap |
| std::unique_lock::try_lock |
| mutex_locker(t) try_lock_true locker_lock |
| std::unique_lock::try_lock_for |
| mutex_locker(t) try_lock_true locker_lock |
| std::unique_lock::try_lock_until |
| mutex_locker(t) try_lock_true locker_lock |
| std::unique_lock::unique_lock |
| mutex_locker(t) mutex(1) mutex_tag_adopt(2) mutex_tag_defer(2) mutex_tag_try_to_lock(2) locker_create |
| std::unique_lock::unlock |
| mutex_locker(t) locker_unlock |
| std::unique_ptr::unique_ptr |
| unique_ptr_constructor |
| std::weak_ptr::weak_ptr |
| weak_ptr_constructor |
| strcat |
| 1p inout(1) 2p *strcat |
| strcat_s |
| 1p inout(1) chneg(2) 3p |
| strchr |
| 1p type(1) r_null |
| strcmp |
| 1p 2p |
| strcoll |
| 1p 2p |
| strcpy |
| 1p 2p *strcpy |
| strcpy_s |
| 1p chneg(2) 3p |
| strcspn |
| 1p 2p |
| strerror_r |
| 2p chneg(3) |
| strerror_s |
| 1p chneg(2) |
| strftime |
| 1p chneg(2) 3p 4p |
| strlen |
| 1p *strlen |
| strncat |
| 1p inout(1) 2p chneg(3) *strncat |
| strncat_s |
| 1p inout(1) chneg(2) 3p chneg(4) |
| strncmp |
| 1p 2p chneg(3) |
| strncpy |
| 1p 2p chneg(3) *strncpy |
| strncpy_s |
| 1p chneg(2) 3p chneg(4) |
| strpbrk |
| 1p type(1) 2p r_null |
| strrchr |
| 1p type(1) r_null |
| strspn |
| 1p 2p |
| strstr |
| 1p type(1) 2p r_null |
| strtod |
| 1p |
| strtof |
| 1p |
| strtok |
| inout(1) 2p r_null |
| strtok_s |
| inout(1) 2p 3p 4p |
| strtol |
| 1p |
| strtold |
| 1p |
| strtoll |
| 1p |
| strtoul |
| 1p |
| strtoull |
| 1p |
| strxfrm |
| 2p chneg(3) *strxfrm |
| swprintf |
| 1p chneg(2) 3p printf(3) *sprintf |
| swprintf_s |
| 1p chneg(2) 3p printf(3) *sprintf |
| swscanf |
| 1p 2p |
| swscanf_s |
| 1p 2p |
| thrd_create |
| 1p 2p thread_create(2) thread_args(3) |
| thrd_sleep |
| 1p |
| timespec_get |
| 1p |
| tmpfile |
| r_null |
| tmpfile_s |
| 1p |
| tmpnam_s |
| 1p chneg(2) |
| tss_create |
| 1p |
| ungetc |
| 2p stream_push(2) stream_byte(2) |
| ungetwc |
| 2p stream_push(2) stream_wide(2) |
| unsetenv |
| 1p |
| vfprintf |
| 1p stream_write(1) stream_byte(1) 2p printf(2) |
| vfprintf_s |
| 1p stream_write(1) stream_byte(1) 2p printf(2) |
| vfscanf |
| 1p stream_read(1) stream_byte(1) 2p scanf(2) |
| vfscanf_s |
| 1p stream_read(1) stream_byte(1) 2p scanf(2) |
| vfwprintf |
| 1p stream_write(1) stream_wide(1) 2p |
| vfwprintf_s |
| 1p stream_write(1) stream_wide(1) 2p |
| vfwscanf |
| 1p stream_read(1) stream_wide(1) 2p |
| vfwscanf_s |
| 1p stream_read(1) stream_wide(1) 2p |
| vprintf |
| 1p printf(1) |
| vprintf_s |
| 1p printf(1) |
| vscanf |
| 1p scanf(1) |
| vscanf_s |
| 1p scanf(1) |
| vsnprintf |
| chneg(2) 3p printf(3) |
| vsnprintf_s |
| 1p chneg(2) 3p printf(3) |
| vsnwprintf_s |
| 1p chneg(2) 3p printf(3) |
| vsprintf |
| 1p 2p printf(2) |
| vsprintf_s |
| 1p chneg(2) 3p printf(3) |
| vsscanf |
| 1p 2p scanf(2) |
| vsscanf_s |
| 1p 2p scanf(2) |
| vswprintf |
| 1p chneg(2) 3p |
| vswprintf_s |
| 1p chneg(2) 3p printf(3) |
| vswscanf |
| 1p 2p |
| vswscanf_s |
| 1p 2p |
| vwprintf |
| 1p |
| vwprintf_s |
| 1p |
| vwscanf |
| 1p |
| vwscanf_s |
| 1p |
| wcrtomb_s |
| 1p chneg(3) 5p |
| wcscat_s |
| 1p chneg(2) 3p |
| wcscoll |
| 1p 2p |
| wcscpy_s |
| 1p chneg(2) 3p |
| wcsftime |
| 1p chneg(2) 3p 4p |
| wcsncat_s |
| 1p chneg(2) 3p chneg(4) |
| wcsncpy_s |
| 1p chneg(2) 3p chneg(4) |
| wcsnrtombs |
| 2p |
| wcsrtombs |
| 2p |
| wcsrtombs_s |
| 1p chneg(3) 4p chneg(5) 6p |
| wcstod |
| 1p |
| wcstof |
| 1p |
| wcstok_s |
| 2p 3p 4p |
| wcstold |
| 1p |
| wcstombs |
| 1p 2p chneg(3) *wcstombs |
| wcstombs_s |
| 1p chneg(3) 4p chneg(5) *wcstombs |
| wcsxfrm |
| 2p chneg(3) *strxfrm |
| wctomb |
| 1p |
| wctomb_s |
| 1p chneg(3) *wcstombs |
| wctrans |
| 1p |
| wctype |
| 1p |
| wmemcpy_s |
| 1p chneg(2) 3p chneg(4) |
| wmemmove_s |
| 1p chneg(2) 3p chneg(4) |
| wprintf |
| 1p |
| wprintf_s |
| 1p |
| wscanf |
| 1p 2p |
| wscanf_s |
| 1p |
Semantics
| assert | The function argument can be assumed to be true (non-zero). |
| calloc | The length of the returned buffer is the product of the first and second arguments or the returned pointer is NULL. |
| dom_1 | The specified argument(s) must be in the range of [-1, 1] as a value outside this range is not defined for this function and may result in a domain error; violations will be diagnosed with message 2423 /2623 . |
| dom_lt1 | The specified argument(s) must not be less than 1 as such a value is not defined for this function and may result in a domain error; violations will be diagnosed with message 2423 /2623 . |
| dom_ltn1 | The specified argument(s) must not be less than -1 as such a value is not defined for this function and may result in a domain error; violations will be diagnosed with message 2423 /2623 . |
| dom_lt0 | The specified argument(s) must not be less than 0 as such a value is not defined for this function and may result in a domain error; violations will be diagnosed with message 2423 /2623 . |
| exit | The function never returns. |
| fclose | Pointer argument 1 is regarded as being uninitialized after the function returns. |
| fgets | Integer argument 2 should not exceed the size of the buffer pointed to by argument 1; violations will be diagnosed with message 419 /669 (data overrun). |
| fread | The product of the integer arguments 2 and 3 should not exceed the size of buffer argument 1; violations will be diagnosed with message 419 /669 (data overrun). |
| free | Pointer argument 1 is regarded as being uninitialized after the function returns and the pointed to memory is marked as having been freed (attempting to free the same memory a second time will be diagnosed by message 449 ). Additionally, if the memory pointed to by argument 1 was derived from an allocation source not appropriate for deallocation via the free function, this will be diagnosed via message 424 . |
| fwrite | The product of the integer arguments 2 and 3 should not exceed the size of buffer argument 1; violations will be diagnosed with message 420 /670 (access beyond end of array). |
| malloc | The length of the buffer returned is the value of integer argument 1 or the returned pointer is NULL. |
| memchr | Integer argument 3 should not be larger than the size of the buffer pointed to by argument 1; violations will be diagnosed with message 420 /670 (access beyond end of array). |
| memcmp | Integer argument 3 should not be larger than the size of the buffer pointed to by either argument 1 or argument 2; violations will be diagnosed with message 420 /670 (access beyond end of array). |
| memcpy | Integer argument 3 should not be larger than the size of the buffer pointed to by either argument 1 or argument 2; a value that exceeds argument 1 will be diagnosed with message 419 /669 (data overrun), a value that exceeds argument 2 will be diagnosed with message 420 /670 (access beyond end of array). |
| memset | Integer argument 3 should not be larger than the size of the buffer pointed to by argument 1; violations will be diagnosed with message 419 /669 (data overrun). |
| sprintf | Message 464 will be issued if the call to this sprintf-like function will result in the destination string being written onto itself. |
| reall_1 | Pointer argument 1 is regarded as being possibly uninitialized after the function returns. |
| realloc | The length of the buffer returned is the value of integer argument 2 or the returned pointer is NULL. |
| strcat | The size of buffer argument 2 should not be larger than the size of buffer argument 1; violations will be diagnosed with message 419 /669 (data overrun). |
| strcpy | The size of buffer argument 2 should not be larger than the size of buffer argument 1; violations will be diagnosed with message 419 /669 (data overrun). |
| strncat | Integer argument 3 should not be larger than the size of buffer argument 1; violations will be diagnosed with message 419 /669 (data overrun). |
| strncpy | Integer argument 3 should not be larger than the size of buffer argument 1; violations will be diagnosed with message 419 /669 (data overrun). |
A function named main at global scope is treated as a special case in certain contexts where the usual behavior
would be inappropriate. For example, message 714 will not be issued as main is typically not explicitly called (and
such a call is prohibited in C++ [?, basic.start.main]).
The functions rand, srand, random, srandom, and time at global scope or within namespace std are specifically
considered by name for the purposes of messages 2460 , 2461 , 2760 , and 2960 .
A call to the function std::addressof is specifically considered by name to constitute taking the address of a
sub-expression in certain contexts where unary & would otherwise be expected.
The -sem() option allows the user to endow his functions with user-defined semantics. This may be considered an
extension of the -function() option (See Section 9.1 Function Mimicry (-function )). Recall that with the
-function() option the user may copy the semantics of a built-in function to any other function but new semantics
cannot be created.
With the -sem option, entirely new checks can be created; integral and pointer arguments can be checked in
combination with each other using usual C operators and syntax. Also, you can specify some constraints upon the
return value.
The format of the -sem() option is:
-sem( function[,sem] ...)
This associates the semantics sem ... with the named function function. The semantics sem are defined below. If no sem is given, i.e. if only function is given, the option is taken as a request to remove semantics from the named function. Once semantics have been given to a named function, the -function() option may be used to copy the semantics in whole or in part to other functions.
sem may be one of:
r_null the function may return the null pointer.
This information is used in subsequent value tracking. For example:
/*lint -sem( f, r_null ) */ char *f(); char *p = f(); *p = 0; /* warning, p may be null */
This is the same semantic that is employed for the builtin function semantics such as bsearch, calloc,
and fgets in Section 9.1.2 Function Listing , and it is considered a Return semantic. See Section 9.1
Function Mimicry (-function ) for the definition of Return semantic. A more flexible way to provide
Return semantics is given below under expressions (exp).
r_no the function does not return.
Code following such a function is considered unreachable. This semantic is identical to the semantic
used for the exit() function as shown in Section 9.1.2 Function Listing . This also is considered a
Return semantic. Note that the GCC attribute syntax can also be used to include the r_no semantic
when declaring a function, see the documentation of the fca flag for more information.
ip (e.g. 3p) the ith argument should be checked for null.
If the ith argument could possibly be null this will be reported. For example:
/*lint -sem( g, 1p ) warn if g()is passed a NULL */ /*lint -sem( f, r_null ) f() may return NULL */ char *f(); void g(char *); void fn() { g( f() ); } /* warning, g is passed a possible null */
The above example can also use the GCC attribute syntax to include the ip semantic when declaring g() e.g.
/*lint -sem( f, r_null ) f() may return NULL */ char *f(); void g(char *)__attribute__((nonnull)); /* nonnull attribute indicates that the function * should not be passed any pointer argument which is a null pointer */ void fn() { g( f() ); } /* warning, g is passed a possible null */
The __attribute__((nonnull)) is automatically recognized by PC-lint Plus and converted to -sem(
g, 1p ). The fca flag must be active for the conversion to take place (which it is by default).
initializer
Some member functions are used to initialize members. They may be called from constructors or called directly when the programmer wants to reset the state of a class to what it would have been immediately after construction. In most cases, PC-lint Plus can automatically determine when such a function initializes class state, even if the initializing function calls other functions to perform parts of the initialization. When the body of the initializer function is not available to PC-lint Plus, such a determination cannot be made. In such cases, you may designate the member as an initializer using the -sem option. (The initializer semantic is a flag semantic). If a member is designated as an initializer function and the body is available to PC-lint Plus, a complaint will be issued if it fails to initialize all of the data members.
cleanup
The cleanup semantic does for destructors what initializer does for constructors. A function
designated as cleanup is expected to process each (non-static) member pointer by either freeing it
(in any of the various ways of releasing storage) or, at least, zeroing it. Failure to do this will merit
Warning 1578 . A function that is a candidate for this semantic will be pointed out by Warning 1579
. cleanup is a flag semantic.
inout(i)
A semantic expression of the form inout(i) where i is a constant designating a parameter, indicates
that an indirect object passed to that parameter will be both read and written by the function. Thus
the ith parameter must be either a pointer (or, equivalently an array) or a reference.
This should not be used with pointers or references to const objects, since, in this case, it is assumed
that the object referenced is only read by the function. It is considered an in parameter. If the
parameter is a pointer or reference to a non-const it is assumed by default to be an out parameter.
That is, the function will only write to the referenced object but will not read from it.
But there is no linguistic way to deduce that the argument will be both read and written such as, for
example, the first argument to strcat(). Hence the need for this semantic.
For example:
//lint -sem( addto, inout(1) ) void addto( int *p, int b ); // add b to the object pointed to // by the first argument. void f() { int n; addto( &n, 12 ); // Warning, n is likely uninitialized }
custodial(i) where i is some integer denoting the ith argument or the letter ’t’ denoting the this
pointer.
It indicates that a called function will take ’custody’ of a pointer passed to argument i. More accurately, it removes the burden of custody from its caller. For example,
//lint -sem(push,custodial(1)) void f() { int *p = new int; push(p); }
Function f would normally draw a complaint (Warning 429 ) that custodial pointer p had not been
freed or returned. However, with the custodial semantic applied to the first argument of push, the call
to push removes from f the responsibility of disposing of the storage allocated to p.
To identify the implicit argument of a (non-static) member function you may use the ’t’ subscript. Thus:
//lint -sem( A::push, custodial(t) ) struct A { void push(); ... }; void g( ) { A *p = new A; p->push(); }
You can combine the custodial semantic with a test for NULL. For example,
-sem( push, 1p, custodial(1) )
will complain about NULL pointers being passed as first argument to push as well as giving the custodial
property to this argument.
The custodial semantic is an argument semantic meaning that it can be passed on to another function using the argument number as subscript. Thus:
function( push(1), append(1) )
transfers the custodial property of the 1st argument of push (as well as the test for NULL) on to the 1st
argument of function append. But note you may not transfer this semantics using a 0 subscript as that refers
to function wide semantics.
An example of the use of the letter t to report this is as follows
/lint -sem( A::push, custodial(t) ) struct A { void push(); ... }; void g( ) { A *p = new A; p->push(); }
Note that for the purposes of these examples, we have placed the -sem options within lint comments. They
may also be placed in a project-wide options file (.lnt file).
non_custodial(i) where i is some integer denoting the ith argument.
This argument semantic is the opposite of the custodial semantic and can be used to indicate that a function does not take custody of the memory pointed to by its argument. When the ffc flag is ON (which it is by default), non-library functions that accept pointers to non-const are assumed to take custody of the pointed-to memory. This semantic can be used to individually specify functions for which this assumption is not appropriate. For example:
//lint -sem(f, non_custodial(1)) void f(int*); void g() { int* p = new int; f(p); }
will report:
warning 429: custodial pointer 'p' likely not freed nor returned
If the non_custodial semantic had not been used, the message would not have been issued because f would
have taken custody due to the ffc flag.
pod(i) A semantic expression of the form pod(i) where i is a constant designating a parameter, indicates that the
argument is expected to be a pointer to a POD.
A POD is an abbreviation for Plain Old Datatype. In brief, an object of POD can be treated as so many bytes, copyable by memcpy, clearable by memset, etc. For example:
//lint -sem( clear, 1p, pod(1) ) wants a non-null pointer to POD class A { A(); int data; } a; class B { public: int data; } b; void clear( void *, size_t ); void f() { clear( &a, sizeof(a) ); // Warning clear( &b, sizeof(b) ); // no Warning }
pure This semantic will designate a function as being pure (see definition below).
Normally functions are determined to be pure or impure automatically through an analysis of their
definition. However, if a function is external to the source files being linted, this analysis cannot be
made and the function is by default considered impure. This semantic can be used to reverse this
assumption so that the function is regarded as pure. Note that the GCC attribute syntax can also be
used to include the pure semantic when declaring a function, see the documentation of the fca flag
for more information.
The significance of a pure function is that it lacks internal side-effects and this can be used to diagnose code redundancies. There are a number of places in the language (left hand side of a comma, first or third expression of a for clause, the expression statement) when it makes no sense to have an expression unless some side-effect is to be achieved. As an example
void f() {} void g() { f(); // Warning 522 }
Because we can deduce f to be pure, a warning is issued. In general, we may not be aware until pass
1 is finished that a function is pure. You can use the pure semantic to hasten the process of detection.
Another use of this semantic can be to determine on what grounds PC-lint Plus considers a function
to be pure. If a function is designated as being pure and is later deemed to have impure properties
Warning 453 will be issued with a detailed explanation as to why the function is impure.
Definition of a pure function: A function is said to be pure if it is not impure. A function is said to
be impure if it modifies a static or global variable or accesses a volatile variable or contains any I/O
operation, or makes a call to any impure function.
A function call is said to have side-effects if it is a call to an impure function or if it is a call to a pure function that modifies its arguments.
Example:
int n; void e1() { n++; } void e2() { static k; k++ } void e3() { printf ( "hello" ); } double e4( double x ) { return sqrt(x); } void e5( volatile int k ) { k++; } void e6() {e1(); }
Each of the functions e1 through e6 is impure because it satisfies one of the above conditions of being an impure function. (This assumes that both printf and sqrt are external functions.) On the other hand, in the following:
int f1() { int n = 0; n++; return n; } void f2( int*p ) { *p = f1(); }
both f1 and f2 are pure functions because there is nothing to designate them impure.
Consider:
//lint -sem( sqrt, pure ) void compute() { double x = sqrt( 2.0 ); } void m() { compute(); }
Here, because of the pure semantic given to sqrt, we get a deserved diagnostic (522 , Highest
operation, function ’compute’, lacks side-effects) at the call to compute. I’m sure the reader will agree
that the function compute shows evidence of a lack of completeness. The author may have been
side-tracked during development and never got back to completing the function. But as we indicated
earlier sqrt would by default be considered impure since it is external. It may actually be impure
since on error conditions it needs to set the external variable errno to EDOM.
Nonetheless, from the standpoint of desired functionability, compute comes up short. This can be
traced to sqrt not offering any desired functionality as a side-effect. Since this is the case, the
programmer was justified in inserting the semantic for sqrt.
Consider the following example:
int f() { int n = 0; n++; return n; }
f() is considered to be a pure function. True it modifies n but n is an automatic variable. The
increment operator is not considered impure but it is regarded as having side-effects.
Consider the following pair of functions:
void h(int *p) { (*p)++; } int g() { int n=0; h(&n); return n;}
Here the function h() is considered pure but note that the call h(&n) has side-effects. Function g()
is exactly analogous to f() above and so must be considered pure. Function g() calls upon h() to
modify variable n in much the same way that f() earlier employed the increment operator. If g() had
provided the address of a global variable to h() then g() would have been considered impure but not
h(). Had we considered h() to be impure irregardless of the nature of its argument then, since g() is
pure, we would have had to give up the principle that impurity is inherited up the call chain.
pure_exception_constructor This semantic will designate a constructor as instantiating a pure exception (see
definition below).
The definition of a pure exception constructor is that it consistently constructs the same object,
which always produces the same exception message, when this constructor is invoked with the same
argument(s). This usually involves not referencing static or global variables in the constructor or any
message producing functions.
This will be used to diagnose duplicate exceptions, such as in message 9302 or 9303 . An example of a pure exception constructor and an impure exception constructor is:
class MyPureException : public std::logic_error { const char* msg; public: MyPureException(const char* m) : msg(m) {} const char* what() const override { return msg; } }; class MyImpureException : public std::logic_error { static int i = 0; public: MyImpureException() { ++i; } const char* what() const override { auto err = std::string("Exception #") + std::to_string(i); return err.c_str(); } };
This semantic is manually assigned, because PC-Lint Plus cannot determine which methods are used
for exception messages.
chneg(i) A semantic expression indicating that the ith argument is expected to be non-negative.
Calling the function with a negative or possibly negative value will be diagnosed with message 422
or 671 , as appropriate.
dangerous
A function designated with the dangerous semantic will cause message 421 to be issued when the
function is called. This is similar to function deprecation 17.8 Deprecation of Entities but using a
semantic allows specific function overloads to be specified.
noliteral(i)
A parameter with the noliteral semantic will report message 2460 if the argument is a literal.
Message 2960 will be reported if the argument is an integer constant expression.
forward(i)
A semantic expression indicating that this is a std::forward-like function whose argument to be
forwarded is the ith argument.
A semantic expression indicating that this is a std::move-like function whose argument to be moved
is the ith argument.
printf(i)
A semantic expression indicating that this is a printf-like function whose format argument is the
ith argument. An option of the form -sem(func, printf(i)) is functionally equivalent to the option
-printf(i,func).
scanf(i)
A semantic expression indicating that this is a scanf-like function whose format argument is the
ith argument. An option of the form -sem(func, scanf(i)) is functionally equivalent to the option
-scanf(i,func).
signal_register(i)
A semantic expression indicating that this is a signal handler registration function. The ith parameter
type should be a function pointer (or a pointer to a structure containing a function pointer) whose
argument will be registered as a signal handler.
signal_handler
Designates a function as a signal handler which is subject to the signal messages 2670 , 2761 , 2762 ,
2763 , and 2765 . If a function has not been designated with this semantic it may still be implicitly
treated as a signal handler due to registration with the signal function, the sigaction function, or
another function with the signal_register semantic. If a function is known to be a signal handler
within the module in which it is defined, the signal messages will be issued at module wrap-up. If a
function is only known to be a signal handler due to its use as an argument to a signal registration
function within a module other than the one in which it is defined, the signal messages will be issued
at global wrap-up.
exception_signal_handler
In addition to all behavior specified for the signal_handler semantic, the exception signal messages
2671 and 2764 will be issued for designated exception signal handlers. A signal handler may implicitly
be considered an exception signal handler if it was registered to handle SIGBUS, SIGSEGV, SIGFPE, or
SIGILL.
async_signal_safe
An async-signal-safe function can be safely called within a signal handler. Messages 2670 and 2761
will not be issued for calls to such functions within signal handlers. Unless otherwise specified, a
function is neither async-signal-safe nor async-signal-unsafe and message 2761 will be issued for calls
within signal handlers.
async_signal_unsafe
An async-signal-unsafe function cannot be safely called within a signal handler. Message 2670 will
be issued for calls to such functions within signal handlers. Unless otherwise specified, a function is
neither async-signal-safe nor async-signal-unsafe and message 2761 will be issued for calls within
signal handlers.
stream_read(i)
Specifies that the function reads from the file stream provided as the ith argument. Message 2476 will
be issued if the stream wasn’t open for reading. Message 2478 will be issued if the previous operation
on the stream was by a function with the stream_write semantic.
stream_write(i)
Specifies that the function writes to the file stream provided as the ith argument. Message 2477 will
be issued if the stream wasn’t open for writing. Message 2479 will be issued if the previous operation
on the stream was by a function with the stream_read semantic.
stream_push(i)
Specifies that the function pushes a character back onto the file stream provided as the ith argument.
Message 2470 will be issued for multiple consecutive calls to a function with this semantic using the
same stream argument.
stream_pos(i)
Specifies that the function repositions the file stream provided as the ith argument. A call to a function
that repositions or flushes a stream is required between read and writes to the same stream.
stream_flush(i)
Specifies that the function flushes the file stream provided as the ith argument. A call to a function
that repositions or flushes a stream is required between a write operation and subsequent read.
stream_close(i)
Specifies that the function closes the file stream provided as the ith argument. Message 2471 is issued
for any stream operations performed on a file stream that has been closed.
stream_wide(i)
Specifies that the function performs a wide-oriented stream operation on the file stream provided as
the ith argument. Message 2481 is issued for an attempt to perform a wide-oriented operation on a
byte-oriented file stream.
stream_byte(i)
Specifies that the function performs a byte-oriented stream operation on the file stream provided as
the ith argument. Message 2480 is issued for an attempt to perform a byte-oriented operation on a
wide-oriented file stream.
stream_placid(i)
Specifies that the function which accepts a file stream as the ith argument does not perform any
operations that modify the state of the stream (state-modifying operations include reading, writing,
pushing, repositioning, flushing, and closing). When a file stream is passed to a function taking a
pointer to a non-const FILE object, it is assumed that the function modifies the state of the stream
if the body of the function is not visible to PC-lint Plus unless the function has the stream_placid
semantic.
no_ptr_to_auto(i)
Specifies that the pointer provided as the ith argument should not be a pointer to automatic storage.
Violations are reported by message 2601 .
no_specific_walk
Specifies that Value Tracking should skip specific walks of the function. The general walk of the
function will still occur. The semantic name nowalk is recognized as a historical alias for this semantic
for backwards compatibility.
unique_ptr_constructor
This semantic will designate a constructor as instantiating a unique pointer. A unique pointer is a class
which manages ownership of an object through a pointer. The object that a given class instantiation
manages ownership of cannot share ownership with another pointer.
The class that this is the constructor for must be a class template whose first template parameter is
the type of the object being managed and pointed to.
shared_ptr_constructor
This semantic will designate a constructor as instantiating a shared pointer. A shared pointer is
a class which manages ownership of an object through a pointer. The object that a given class
instantiation manages ownership of can be shared with another shared pointer, or a weak pointer. See
the weak_pointer_constructor semantic for the definition of a weak pointer.
The class that this is the constructor for must be a class template whose first template parameter is
the type of the object being managed and pointed to.
weak_ptr_constructor
This semantic will designate a constructor as instantiating a weak pointer. A weak pointer is a class
which holds a reference to an object through a pointer without having ownership over it. The object
that a given class instantiation holds a reference to may also be accessed through shared pointers that
do share ownership over the object. See the shared_pointer_constructor semantic for the definition
of a shared pointer.
The class that this is the constructor for must be a class template whose first template parameter is
the type of the object being managed and pointed to.
exp a semantic expression involving the expression elements described below:
in denotes the ith argument, which must be integral (E.g. 3n refers to the 3rd argument). An argument is integral if it is typed int or some variation of integral such as char, unsigned long, an enumeration, etc.
i may be @ (commercial at) in which case the return value is implied. For example, the expression:
@n == 4 || @n > 1n
states that the return value will either be equal to 4 or will be greater than the first argument.
ip denotes the ith argument, which must be some form of pointer (or array). The value of this variable is the number of items pointed to by the pointer (or in the array). For example, the expression:
2p == 10
specifies a constraint that the 2nd argument, which happens to be a pointer, should have exactly 10 items. The number of items "pointed to" by a string constant is 1 plus the number of characters between quotes.
Just as with in, i may be @ in which case the return value is indicated.
iP is like ip except that all values are specified in bytes. For example, the semantic:
2P == 10
specifies that the size in bytes of the area pointed to by the 2nd argument is 10. To specify a return pointer where the area pointed to is measured in bytes we use @P.
integer (any C/C++ integral or character constant) denotes itself.
identifier that refers to a macro that evaluates to a constant expression. The identifier is retained at option processing time and evaluated at the time of function call.
malloc( exp) attaches a malloc allocation flag to the expression. See the discussion of Return Semantics below.
new( exp ) attaches a new allocation flag to the expression.
new[]( exp ) attaches a new[] allocation flag to the expression.
( )
Unary operators: + - ! ~
Binary operators:
+ - * / % < <= == != > >= | & ^ < < >> || &&
Ternary operator: ?:
Operators, parentheses and constants have their usual C/C++ meaning. Also the precedence of operators are
identical to C/C++.
There may be at most two expressions in any one -sem option, one expressing Return semantics and one expressing
Function-wide semantics.
Return Semantics An expression involving the return value (one of @n, @p, @P) is a Return semantic and indicates something about the returned value. For example, if the semantics for strlen() were given explicitly, they might be given as:
-sem( strlen, @n < 1p, 1p )
In words, the return value is strictly less than the size of the buffer given as first argument. Also the first argument
should not be null.
To express further uncertainty about the return value, one or more expressions involving the return value may be alternated using the || operator. For example:
-sem( fgets, @p == 1p || @p == 0 )
represents a possible Return semantic for the built-in function fgets. Recall that the fgets function returns the
address of the buffer passed as first argument unless an end of file (or error) occurs in which case the null pointer is
returned. If the Return semantic indicates, in the case of a pointer, that the return value may possibly be zero (by
explicitly using either the test @p == 0 or @P == 0 as in this example) this is taken as a possibility of returning the
null pointer.
As another example:
-sem( lookup, 2n == LOCATE ? (@p==0||@p==1) : @p==1)
This is a Return semantic that says that if the 2nd argument of the function lookup is equal to LOCATE then the return pointer may or may not be null. Otherwise we may assume that the return value is a valid non-null pointer. This could be used as follows:
#define LOCATE 1 #define INSTALL 2 Symbol *lookup (const char *, int ); ... sym = lookup("main", INSTALL); sym->value=0; /*OK*/ sym = lookup("help", LOCATE); v = sym -> value; /* warning - could be NULL */
Here the first return value from lookup is guaranteed to be non-null, whereas the second may be null or may not
be.
We caution the reader that the following, apparently equivalent, semantic does not work.
-sem( lookup, @p == (2n != LOCATE) || @p == 1 )
The OR (||) is taken to mean that either side or both could be true with some probability but there is no certainty
deduced that one or the other must be true.
When @p (lowercase p) is used, the pointee type of the return value must be a complete type at the point the
function is called since PC-lint Plus needs to be able to calculate the size of the returned buffer to use such a
semantic. If the type return type is not complete, a 686 message will be issued.
Flagging the return value
Consider the example:
char *p, *q = 0; p = malloc(10); p = q; // Warning -- memory leak
We are able to issue a Warning because the return from malloc has an allocation flag that indicated that the
returned value points to a freshly allocated region that is not going to be freed by itself.
The seemingly equivalent semantic option:
-sem( my_alloc, @P == 1n )
associates no allocation flag with the returned pointer (only the size of the area in bytes).
To identify the kind of storage that a function may return, three flag-endowing functions have been added to the
allowed expression syntax of the -sem option:
| a region allocated by malloc to be released through free |
|
| a region allocated by new to be released through delete |
|
| a region allocated by new[] to be released through delete[] |
|
In each case, the exp is the size of the area to be allocated. For example, to simulate malloc we may have:
-sem( my_alloc, @P == malloc(1n) )
By contrast the semantic:
-sem( some_alloc, @p == malloc(1n) )
indicates, because of the lower case ’p’, that the size of the allocated region is measured in allocation units. Thus the
malloc here is taken to indicate the type of storage (freshly allocated that should be free) and not as a literal call
to malloc to allocate so many bytes.
As another example:
-sem( newstr, @p == (1p ? new[](1p) : 0) )
In words, this says that newstr is a function whose return value will, if the first argument is a non-null pointer, be
the equivalent of a new[] of that size. Otherwise a NULL will be returned.
Return Semantic Validation
Return semantics are typically employed on functions for which the body is not available to PC-lint Plus but they
can also be applied to functions that do have a visible definition. In this case, the return semantics will be validated
against the actual function definition when analyzing specific calls. This can be used, for example, to document the
return conditions that the function should employ and to have PC-lint Plus diagnose deviations from this
contract.
Violation of return semantics are reported via Warning 2426 . In the following example, the semantic @p > 0 specifies that the pointer return value is never null. The implementation of this function contains a path that violates this semantic. PC-lint Plus will now report when such a path is taken causing the return value semantic to be violated:
//lint -sem(f, @p > 0) return value should never be null void *f(int a, void *p) { if (a < 0) return 0; return p; } void g(void *p) { void *ptr = f(-1, p); }
PC-lint Plus produces:
warning 2426: return value (nullptr) of call to function 'f(int, void *)' conflicts with return semantic '(@p>0)' void *ptr = f(-1, p); ^
to indicate the violation of the semantic that specified the return value is never null.
When the return semantic conflicts with information collected during a specific call, the latter overrides the former. For example, despite the existence of the semantic claiming that the return value is not null, after the call to f PC-lint Plus will retain the knowledge gleaned from the actual call to f and diagnose an attempt to dereference the pointer. For example, if after the call to f(-1, p) we had:
int *iptr = ptr; *ptr = 1; void
PC-lint Plus would issue:
warning 413: likely use of null pointer *iptr = 1; ^ supplemental 831: initialization yields nullptr int *iptr = ptr; ~~~~~^~~~~~~~~~ supplemental 831: initialization yields nullptr void *ptr = f(-1, p); ~~~~~~^~~~~~~~~~~~~~ supplemental 831: null to pointer conversion yields nullptr return 0; ^
If the fso flag is turned ON, return semantics will override any conflicting information obtaining during a specific
walk although message 2426 will still be issued.
Function-wide semantics
An expression that is not a Return semantic is a ’Function-wide’ semantic (to use the terminology of Section 9.1.1
Special Functions ). It indicates a predicate that should be true. If there is a decided possibility that it is false, a
diagnostic is issued.
What constitutes a "decided possibility"? This is determined by considerations described in Section 8 Value
Tracking . If nothing is known about a situation, no diagnostic is issued. If what we do know suggests the possibility
of a violation of the Function-wide semantic, a diagnostic is issued.
For example, to check to see if the region of storage passed to function g() is at least 6 bytes you may use the following:
//lint -sem( g, 1P >= 6 ) 1st arg. must have at least 6 bytes void g(short *); void f() { short a[3]; // a[] has 6 bytes short *p = a + 1; // p points to 4 bytes g(a); // OK g(p); // Warning }
Several constraints may be AND’ed using the && operator. For example, to check that fread( buffer, size, count, stream ) has non-zero second and third arguments and that their product exactly equals the size of the buffer you may use the following option.
-sem( fread, 1P==2n*3n && 2n>0 && 3n>0 )
Note that we rely on C’s operator precedence to properly group operator arguments.
To continue with our example we should add Return Semantics. fread returns a value no greater than the third argument (count). Also, the first and fourth arguments should be checked for null. A complete semantic option for fread becomes:
-sem( fread, 1P==2n*3n && 2n>0 && 3n>0, @n<=3n, 1p, 4p )
It is possible to employ macros in semantic expressions rather than hard numbers. For example:
//lint -sem( X::cpy, 1P <= BUFLEN ) char *strcpy(char *dest, const char *src); #define BUFLEN 4 class X { public: char buf[BUFLEN]; void cpy(char *p) { strcpy(buf, p); } void slen(char *p); }; void f(X &x) { x.cpy("abcd"); // Warning x.cpy("abc"); // OK }
Just as is the case with -function, -sem may be applied to member functions. For example:
//lint -sem( X::cpy, 1P <= BUFLEN ) const int BUFLEN = 4; class X { public: char buf[BUFLEN]; void cpy( char * p ) { strcpy( buf, p ); } void slen( char * p ); }; void f( X &x ) { x.cpy( "abcd" ); // Warning x.cpy( "abc" ); // OK }
In this example, the argument to X::cpy must be less than or equal to BUFLEN. The byte requirements of
"abcd" are 5 (including the nul character) and BUFLEN is defined to be 4. Hence a warning is issued
here.
To specify semantics for template members, simply ignore the angle brackets in the name given to -sem. The semantics will apply to each template instantiation. For example, in the code below the user wants to assign the custodial semantic to the first argument of the push_back function in every instantiation of template list. This will avoid a Warning 429 when the pointer is not deleted in f().
//lint -sem( std::list::push_back, custodial(1) ) namespace std { template< class T > class list { public: void push_back( const int * ); }; } std::list<int*> l; void f() { int *p = new int; l.push_back( p ); // OK, push_back takes custody }
Overload-Specific Semantics A user-defined semantic may be applied to a specific function overload by including the function’s parameter list in the semantic specification (where a parameter list of (void) represents a function taking no arguments). For example:
//lint -sem(foo(int, int), chneg(1)) void foo(int); void foo(int, int); void bar() { foo(-8); // Okay foo(-8, 20); // Warning }
A semantic can be applied to a specific function template instantiation by specifying the substituted template parameter types in the function parameter list:
//lint -sem(A1::rocker(int, char *, int), 3n <= 2P) struct A1 { template <typename T2> int rocker(T2, char *, int); }; void g() { char buf[10]; A1 a1; a1.rocker(1, buf, 20); // Warning a1.rocker(1.2, buf, 20); // Okay }
A semantic can be applied to all templated versions of a function by referencing the names of the template parameters in the argument list:
//lint -sem(A1::rocker(T2, char *, int), 3n <= 2P) struct A1 { template <typename T2> int rocker(T2, char *, int); }; void g() { char buf[10]; A1 a1; a1.rocker(1, buf, 20); // Warning a1.rocker(1.2, buf, 20); // Warning }
Every function call has 2 or 3 distinct monikers that can be used in a -sem option. Since the correct monikers might not be obvious in some scenarios, the monikers associated for each call will be provided via message 879 when the fsf flag is ON. If the fsf flag was enabled for the above example, the corresponding 879 messages would look like this:
info 879: semantic monikers are 'A1::rocker(int, char *, int)', 'A1::rocker(T2, char *, int)', and 'A1::rocker' a1.rocker(1, buf, 20); ^ info 879: semantic monikers are 'A1::rocker(double, char *, int)', 'A1::rocker(T2, char *, int)', and 'A1::rocker' a1.rocker(1.2, buf, 20); ^
The monikers are provided in order of decreasing specificity. The most specific moniker contains the
complete parameter list. The next moniker contains the non-substituted template parameter names, this
moniker does not exist for non-template functions. The most generic moniker is just the name of the
function.
An overload set may have multiple semantics associated with it although only one semantic will be applied to a given function call, the semantic with the most specific matching function designator. For example:
//lint -sem(slow(T1, T1), 1n != 2n) //lint -sem(slow(int, double), 1n > 0) //lint -sem(slow, 1n > 1) template <typename T1> void slow(T1, T1); void slow(int, double); void slow(int); void h() { slow(1, 0); // Okay slow(0, 0); // Warning, violates semantic for slow(T1, T1) slow(1, 3.0); // Okay slow(0, 3.0); // Warning, violates semantic for slow(int, double) slow(2); // Okay slow(1); // Warning, violates default semantic for slow }
Note the difference between foo() and foo(void) in a semantic option. The former specifies that the semantic should apply to the C function named foo that does not have a prototype whereas the latter specifies a semantic for a function foo declared as taking no arguments (either by being declared as foo(void) in C or C++ or as foo() in C++).
-sem( f, 2p, 1p > 0 )
2p becomes an Argument semantic (the pointer should not be NULL) for argument 2. We can transfer this semantic to, say, the 3rd argument of function g by using the option
-function( f(2), g(3) )
The expression 1p>0 becomes the Function-wide semantic for function f and can be transferred via the 0 subscript as in:
-function( f(0), g(0) )
We could have placed these two together as one large semantic as in:
-sem( f, 2p && 1p > 0 )
The earlier rendition is preferred because there is a specialized set of warning messages for the argument semantic of passing null pointers to functions.
-sem( f, r_null, @p = 1p )
It is easy to convert this into an acceptable semantic as follows:
-sem( f, @p == 0 || @p == 1p )
-sem( strlen, @n < 1p )
We are not comparing here integers with pointers. Rather we are comparing the number of items that a
pointer points to (an integer) with an integral return value.
For uniformity, the arithmetic of semantics is signed integral arithmetic, usually long precision. This means that greater-than comparisons with numbers higher than the largest signed long will not work.