if (1) {
if (1)
reachable();
else
unreachable(); /* expect+0: ... [193] */
if (0)
unreachable(); /* expect+0: ... [193] */
else
reachable();
reachable();
}
reachable();
}
void
test_if_maybe(void)
{
if (maybe()) {
if (0)
unreachable(); /* expect+0: ... [193] */
else
reachable();
reachable();
}
reachable();
if (0) {
if (maybe()) /* expect+0: ... [193] */
unreachable();
else
unreachable();
unreachable();
}
reachable();
if (1) {
if (maybe())
reachable();
else
reachable();
reachable();
}
reachable();
}
/*
* To compute the reachability graph of this little monster, lint would have
* to keep all statements and their relations from the whole function in
* memory. It doesn't do that. Therefore it does not warn about any
* unreachable statements in this function.
*/
void
test_goto_numbers_alphabetically(void)
{
goto one;
eight:
goto nine;
five:
return;
four:
goto five;
nine:
goto ten;
one:
goto two;
seven:
goto eight;
six:
/* expect-1: warning: label 'six' unused in function 'test_goto_numbers_alphabetically' [232] */
goto seven;
ten:
return;
three:
goto four;
two:
goto three;
}
void
test_while_goto(void)
{
while (1) {
goto out;
break; /* lint only warns with the -b option */
}
unreachable(); /* expect+0: ... [193] */
out:
reachable();
}
/* named_label assumes that any label is reachable. */
unreachable:
unreachable();
reachable:
reachable();
}
/* TODO: switch */
/* TODO: system-dependent constant expression (see tn_system_dependent) */
void suppressed(void);
void
lint_annotation_NOTREACHED(void)
{
if (0) {
/* expect+1: warning: 'call' statement not reached [193] */
unreachable();
}
if (0) {
/* NOTREACHED */
suppressed();
}
if (0)
/* NOTREACHED */
suppressed();
if (1) {
reachable();
}
if (1) {
/* NOTREACHED */
suppressed();
}
/*
* Since the condition in the 'if' statement is constant, lint knows
* that the branch is unconditionally taken. The annotation comment
* marks that branch as not reached, which means that any following
* statement cannot be reached as well.
*/
/* expect+1: warning: 'if' statement not reached [193] */
if (1)
/* NOTREACHED */
suppressed();
}
/*
* Since at least 2002 and before cgram.y 1.379 from 2022-01-16, lint did not
* detect a double semicolon. See cgram.y, expression_statement, T_SEMI.
*/
int
test_null_statement(void)
{
/*
* The following 2 semicolons are superfluous but lint doesn't warn
* about them. Probably it should. A null statement as part of a
* block-list has no use.
*/
;;
/*
* If assertions are disabled with -DNDEBUG and __lint__ is defined,
* NetBSD's <assert.h> defines assert(x) to nothing, leaving only
* the trailing semicolon. If there are several assertions next to
* each other, without any whitespace in between (very unusual), the
* GCC preprocessor generates ";;" for them, which makes them
* indistinguishable from the literal ";;" from the typo above.
*
* (echo '#include <assert.h>'; echo 'assert(0);assert(1);') \
* | gcc -DNDEBUG -E - -D__lint__
*
* To actually see the difference, lint would need to look at the
* code before preprocessing and compare it with the preprocessed
* code, which would be a lot of work.
*
* Apart from the above edge case, detecting extra semicolons would
* be possible, but lint would have to look at the whitespace between
* the tokens, and this is something that it doesn't do at all, as of
* 2022-01-16.
*/
/*
* A stand-alone null statement, on the other hand, has its purpose.
* Without it, the 'for' loop would not be complete. The NetBSD
* style is to use 'continue;' instead of a simple ';'.
*/
for (int i = 0; i < 10; i++)
;
/*
* Before func.c 1.149 from 2023-02-21, lint crashed due to a null pointer
* dereference.
*/
void
invalid_case_expression(void)
{
switch (4) {
/* expect+1: error: operand of '~' has invalid type 'double' [108] */
case ~0.0:
;
}
}