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