Vector Help

Reference Manual for PC-lint® Plus

9 Semantics

9.1 Function Mimicry (-function)

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.

9.1.1 Special Functions

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.

9.1.2 Function Listing


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).

9.1.3 Other names with special behavior that cannot be mimicked

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.

9.2 Semantic Specifications (-sem)

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.

9.2.1 Possible Semantics

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.

move(i)

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:

9.2.2 Semantic Expressions

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++).

9.2.3 Notes on Semantic Specifications

1.
Every function has, potentially, a Return semantic (r), a Function-wide semantic (0), flag semantics (f), and Argument semantics for each of the arguments and the implied this argument (t). An expression of the form ip when it stands alone and is not part of another expression becomes an Argument semantic for argument i (presumably a pointer argument). Thus, for the option
-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.

2.
Please note that r_null and an expression involving argument @ are Return semantics. You cannot have both in one option. Thus you cannot have
-sem( f, r_null, @p = 1p )    

It is easy to convert this into an acceptable semantic as follows:

-sem( f, @p == 0 || @p == 1p )  
3.
The notations for arguments and return values was not chosen capriciously. A notation such as @n == 2n may look strange at first but it was chosen so as not to conflict with user identifiers.
4.
Please note that the types of arguments are signed integral values. Thus we may write
-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.