Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src lint: add new check for strict bool mode
details: https://anonhg.NetBSD.org/src/rev/ccd6f1ab5488
branches: trunk
changeset: 979820:ccd6f1ab5488
user: rillig <rillig%NetBSD.org@localhost>
date: Tue Jan 12 20:42:00 2021 +0000
description:
lint: add new check for strict bool mode
In strict bool mode, bool is considered incompatible with all other
scalar types, just as in Java, C#, Pascal.
The controlling expressions in if statements, while loops, for loops and
the '?:' operator must be of type bool. The logical operators work on
bool instead of int, the bitwise operators accept both integer and bool.
The arithmetic operators don't accept bool.
Since <stdbool.h> implements bool using C preprocessor macros instead of
predefining the identifiers "true" and "false", the integer constants 0
and 1 may be used in all contexts that require a bool expression.
Except from these, no implicit conversion between bool and scalar types
is allowed.
See usr.bin/tests/xlint/lint1/d_c99_bool_strict.c for more details.
The command line option -T has been chosen because all obvious choices
(-b or -B for bool, -s or -S for strict) are already in use. The -T may
stand for "types are checked strictly".
The default behavior of lint doesn't change. The strict bool check is
purely optional.
An example program for strict bool mode is usr.bin/make, which has been
using explicit comparisons such as p != NULL, ch != '\0' or n > 0 in
most places for a long time now, even before the refactoring in 2020.
diffstat:
tests/usr.bin/xlint/check-expect.lua | 135 +++++++++
tests/usr.bin/xlint/lint1/d_c99_bool_strict.c | 357 +++++++++++++++--------
tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp | 109 ++++++-
usr.bin/xlint/common/externs.h | 4 +-
usr.bin/xlint/lint1/cgram.y | 11 +-
usr.bin/xlint/lint1/err.c | 12 +-
usr.bin/xlint/lint1/externs1.h | 3 +-
usr.bin/xlint/lint1/func.c | 10 +-
usr.bin/xlint/lint1/main1.c | 9 +-
usr.bin/xlint/lint1/op.h | 8 +-
usr.bin/xlint/lint1/oper.c | 6 +-
usr.bin/xlint/lint1/ops.def | 140 ++++----
usr.bin/xlint/lint1/tree.c | 200 ++++++++++++-
usr.bin/xlint/lint2/main2.c | 10 +-
usr.bin/xlint/xlint/lint.1 | 12 +-
usr.bin/xlint/xlint/xlint.c | 12 +-
16 files changed, 792 insertions(+), 246 deletions(-)
diffs (truncated from 1621 to 300 lines):
diff -r 8b4798810608 -r ccd6f1ab5488 tests/usr.bin/xlint/check-expect.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/usr.bin/xlint/check-expect.lua Tue Jan 12 20:42:00 2021 +0000
@@ -0,0 +1,135 @@
+#! /usr/bin/lua
+-- $NetBSD: check-expect.lua,v 1.1 2021/01/12 20:42:01 rillig Exp $
+
+--[[
+
+usage: lua ./check-expect.lua *.c
+
+Check that the /* expect: ... */ comments in the .c source files match the
+actual messages found in the corresponding .exp files.
+
+]]
+
+
+local function load_lines(fname)
+ local lines = {}
+
+ local f = io.open(fname, "r")
+ if f == nil then return nil end
+
+ for line in f:lines() do
+ table.insert(lines, line)
+ end
+ f:close()
+
+ return lines
+end
+
+local function load_expect_comments_from_c(fname)
+
+ local lines = load_lines(fname)
+ if lines == nil then return nil end
+
+ local comments_by_line = {}
+ local seen_comment = false
+ for lineno, line in ipairs(lines) do
+ local comments_in_line = {}
+ for comments in line:gmatch("/%* expect: (.-) %*/$") do
+ for comment in comments:gmatch("[^,]+") do
+ table.insert(comments_in_line, comment:match("^%s*(.-)%s*$"))
+ seen_comment = true
+ end
+ end
+ comments_by_line[lineno] = comments_in_line
+ end
+
+ if seen_comment then return comments_by_line else return nil end
+end
+
+
+local function load_actual_messages_from_exp(fname)
+
+ local lines = load_lines(fname)
+ if lines == nil then return nil end
+
+ local messages = {}
+ for lineno, line in ipairs(lines) do
+ for c_lineno, message in line:gmatch("%S+%((%d+)%): (.+)$") do
+ table.insert(messages, {
+ exp_lineno = lineno,
+ c_lineno = tonumber(c_lineno),
+ msg = message
+ })
+ end
+ end
+
+ return messages
+end
+
+
+local function check_test(c_fname, errors)
+ local exp_fname = c_fname:gsub("%.c$", ".exp")
+ local comments = load_expect_comments_from_c(c_fname)
+ if comments == nil or #comments == 0 then return end
+ local messages = load_actual_messages_from_exp(exp_fname)
+ if messages == nil then return end
+
+ local remaining = 0
+ for lineno, exps in ipairs(comments) do
+ for _, msg in ipairs(exps) do
+ -- print("comment", lineno, msg)
+ remaining = remaining + 1
+ end
+ end
+
+ for _, act in ipairs(messages) do
+ -- print("messages", act.exp_lineno, act.c_lineno, act.msg)
+
+ local exp = comments[act.c_lineno] or {}
+
+ local found = false
+ for i, msg in ipairs(exp) do
+ if msg ~= "" and act.msg:find(msg, 1, true) then
+ exp[i] = ""
+ found = true
+ remaining = remaining - 1
+ -- print("found", act.c_lineno, act.msg, msg, remaining)
+ break
+ end
+ end
+
+ if not found then
+ errors:add("error: %s:%d: message \"%s\" is not declared in %s:%d",
+ exp_fname, act.exp_lineno, act.msg, c_fname, act.c_lineno)
+ end
+ end
+
+ for lineno, exps in ipairs(comments) do
+ for _, msg in ipairs(exps) do
+ if msg ~= "" then
+ errors:add("error: %s:%d: declared message \"%s\" is not in the actual output",
+ c_fname, lineno, msg)
+ end
+ end
+ end
+end
+
+
+local function main(args)
+ local errors = {}
+ errors.add = function(self, fmt, ...)
+ table.insert(self, string.format(fmt, ...))
+ end
+
+ for _, name in ipairs(args) do
+ check_test(name, errors)
+ end
+
+ for _, error in ipairs(errors) do
+ print(error)
+ end
+
+ return #errors == 0
+end
+
+os.exit(main(arg))
diff -r 8b4798810608 -r ccd6f1ab5488 tests/usr.bin/xlint/lint1/d_c99_bool_strict.c
--- a/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c Tue Jan 12 19:36:39 2021 +0000
+++ b/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c Tue Jan 12 20:42:00 2021 +0000
@@ -1,9 +1,9 @@
-/* $NetBSD: d_c99_bool_strict.c,v 1.3 2021/01/11 00:28:28 rillig Exp $ */
+/* $NetBSD: d_c99_bool_strict.c,v 1.4 2021/01/12 20:42:01 rillig Exp $ */
# 3 "d_c99_bool_strict.c"
/*
- * Experimental feature: allow to treat _Bool as incompatible with all
- * scalar types. This means:
+ * The option -T treats _Bool as incompatible with all other scalar types.
+ * This means:
*
* SB001: Controlling expressions in 'if', 'while', 'for', '?:' must be of
* type _Bool instead of scalar.
@@ -18,9 +18,9 @@
*
* SB005: There is no implicit conversion from _Bool to any other type.
*
- * SB006: A constant integer expression is compatible with type _Bool if
- * it is an integer constant with value 0 or 1, or if the result type of
- * its main operator is _Bool.
+ * SB006: An expression is compatible with type _Bool if its main operator
+ * returns type _Bool, or if the expression is an integer constant expression
+ * with value 0 or 1.
*
* SB007: Expressions like "flags & FLAG" are compatible with _Bool if
* they appear in a context where they are immediately compared to zero.
@@ -32,7 +32,7 @@
* typically have type _Bool:1 and can be converted to _Bool and back.
*/
-// Not yet implemented: /* lint1-extra-flags: -T */
+/* lint1-extra-flags: -T */
/*
* The header <stdbool.h> defines the macros bool = _Bool, false = 0 and
@@ -61,23 +61,23 @@
return;
/* Not allowed: 2 is not a boolean expression. */
- if (/*CONSTCOND*/2)
+ if (/*CONSTCOND*/2) /* expect: 333 */
return;
/* Not allowed: There is no implicit conversion from scalar to bool. */
- if (i)
+ if (i) /* expect: 333 */
return;
if (i != 0)
return;
/* Not allowed: There is no implicit conversion from scalar to bool. */
- if (d)
+ if (d) /* expect: 333 */
return;
if (d != 0.0)
return;
/* Not allowed: There is no implicit conversion from scalar to bool. */
- if (p)
+ if (p) /* expect: 333 */
return;
if (p != (void *)0)
return;
@@ -88,15 +88,15 @@
}
void
-SB002_operator_result(bool b)
+SB002_operator_result_type(bool b)
{
b = b;
- char c = b;
- int i = b;
- double d = b;
- void *p = b;
+ char c = b; /* expect: 107 */
+ int i = b; /* expect: 107 */
+ double d = b; /* expect: 107 */
+ void *p = b; /* expect: 107 */
- /* These assignments are all ok. */
+ /* The right-hand sides of these assignments are all ok. */
b = !b;
b = i == i;
b = i != i;
@@ -108,59 +108,70 @@
b = b || b;
/*
- * These assignments are not ok, they implicitly convert from bool
- * to int.
+ * The right-hand sides of these assignments are not ok, they
+ * implicitly convert from bool to int.
*/
- i = !b;
- i = i == i;
- i = i != i;
- i = i < i;
- i = i <= i;
- i = i >= i;
- i = i > i;
- i = b && b;
- i = b || b;
+ i = !b; /* expect: 107 */
+ i = i == i; /* expect: 107 */
+ i = i != i; /* expect: 107 */
+ i = i < i; /* expect: 107 */
+ i = i <= i; /* expect: 107 */
+ i = i >= i; /* expect: 107 */
+ i = i > i; /* expect: 107 */
+ i = b && b; /* expect: 107 */
+ i = b || b; /* expect: 107 */
}
-void
+int
SB003_operands(bool b, int i)
{
- /* These assignments are ok. */
+ /* The right-hand sides of these assignments are ok. */
b = !b;
b = b && b;
b = b || b;
- /* These assignments implicitly convert from scalar to bool. */
- b = !i;
- b = i && i;
- b = i || i;
+ /*
+ * The right-hand sides of these assignments implicitly convert from
+ * scalar to bool.
+ */
+ b = !i; /* expect: 330 */
+ b = i && i; /* expect: 331, 332 */
+ b = i || i; /* expect: 331, 332 */
+
+ b = b && 0;
+ b = 0 && b;
+ b = b || 0;
+ b = 0 || b;
+
+ return i;
}
+/*ARGSUSED*/
void
-SB004_non_bool_operands(bool b, unsigned u)
+SB004_operators_and_bool_operands(bool b, unsigned u)
{
b = !b; /* ok */
- b = ~b; /* not ok */
- ++b; /* not ok */
- --b; /* not ok */
- b++; /* not ok */
- b--; /* not ok */
- b = +b; /* not ok */
- b = -b; /* not ok */
Home |
Main Index |
Thread Index |
Old Index