浏览代码

Added control messages to warnings

Added the concept of control messages to the warning system, plus the
implementation of the controls "@on"/"@off" to turn warnings on/off.
Moreover, the warning system in the test library adds some other
controls to ease the test of warnings.
Roberto Ierusalimschy 6 年之前
父节点
当前提交
a1d8eb2743
共有 9 个文件被更改,包括 161 次插入49 次删除
  1. 24 10
      lauxlib.c
  2. 2 2
      lbaselib.c
  3. 47 17
      ltests.c
  4. 24 12
      lua.c
  5. 22 7
      manual/manual.of
  6. 4 0
      testes/all.lua
  7. 2 0
      testes/api.lua
  8. 7 1
      testes/gc.lua
  9. 29 0
      testes/main.lua

+ 24 - 10
lauxlib.c

@@ -1002,29 +1002,43 @@ static int panic (lua_State *L) {
 
 
 
 
 /*
 /*
-** Emit a warning. '*previoustocont' signals whether previous message
-** was to be continued by the current one.
+** Emit a warning. '*warnstate' means:
+** 0 - warning system is off;
+** 1 - ready to start a new message;
+** 2 - previous message is to be continued.
 */
 */
 static void warnf (void *ud, const char *message, int tocont) {
 static void warnf (void *ud, const char *message, int tocont) {
-  int *previoustocont = (int *)ud;
-  if (!*previoustocont)  /* previous message was the last? */
+  int *warnstate = (int *)ud;
+  if (*warnstate != 2 && !tocont && *message == '@') {  /* control message? */
+    if (strcmp(message + 1, "off") == 0)
+      *warnstate = 0;
+    else if (strcmp(message + 1, "on") == 0)
+      *warnstate = 1;
+    return;
+  }
+  else if (*warnstate == 0)  /* warnings off? */
+    return;
+  if (*warnstate == 1)  /* previous message was the last? */
     lua_writestringerror("%s", "Lua warning: ");  /* start a new warning */
     lua_writestringerror("%s", "Lua warning: ");  /* start a new warning */
   lua_writestringerror("%s", message);  /* write message */
   lua_writestringerror("%s", message);  /* write message */
-  if (!tocont)  /* is this the last part? */
+  if (tocont)  /* not the last part? */
+    *warnstate = 2;  /* to be continued */
+  else {  /* last part */
     lua_writestringerror("%s", "\n");  /* finish message with end-of-line */
     lua_writestringerror("%s", "\n");  /* finish message with end-of-line */
-  *previoustocont = tocont;
+    *warnstate = 1;  /* ready to start a new message */
+  }
 }
 }
 
 
 
 
 LUALIB_API lua_State *luaL_newstate (void) {
 LUALIB_API lua_State *luaL_newstate (void) {
   lua_State *L = lua_newstate(l_alloc, NULL);
   lua_State *L = lua_newstate(l_alloc, NULL);
   if (L) {
   if (L) {
-    int *previoustocont;  /* space for warning state */
+    int *warnstate;  /* space for warning state */
     lua_atpanic(L, &panic);
     lua_atpanic(L, &panic);
-    previoustocont = (int *)lua_newuserdatauv(L, sizeof(int), 0);
+    warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0);
     luaL_ref(L, LUA_REGISTRYINDEX);  /* make sure it won't be collected */
     luaL_ref(L, LUA_REGISTRYINDEX);  /* make sure it won't be collected */
-    *previoustocont = 0;  /* next message starts a new warning */
-    lua_setwarnf(L, warnf, previoustocont);
+    *warnstate = 1;  /* next message starts a new warning */
+    lua_setwarnf(L, warnf, warnstate);
   }
   }
   return L;
   return L;
 }
 }

+ 2 - 2
lbaselib.c

@@ -48,9 +48,9 @@ static int luaB_warn (lua_State *L) {
   luaL_checkstring(L, 1);  /* at least one argument */
   luaL_checkstring(L, 1);  /* at least one argument */
   for (i = 2; i <= n; i++)
   for (i = 2; i <= n; i++)
     luaL_checkstring(L, i);  /* make sure all arguments are strings */
     luaL_checkstring(L, i);  /* make sure all arguments are strings */
-  for (i = 1; i <= n; i++)  /* compose warning */
+  for (i = 1; i < n; i++)  /* compose warning */
     lua_warning(L, lua_tostring(L, i), 1);
     lua_warning(L, lua_tostring(L, i), 1);
-  lua_warning(L, "", 0);  /* close warning */
+  lua_warning(L, lua_tostring(L, n), 0);  /* close warning */
   return 0;
   return 0;
 }
 }
 
 

+ 47 - 17
ltests.c

@@ -79,32 +79,62 @@ static int tpanic (lua_State *L) {
 
 
 /*
 /*
 ** Warning function for tests. Fist, it concatenates all parts of
 ** Warning function for tests. Fist, it concatenates all parts of
-** a warning in buffer 'buff'. Then:
-** - messages starting with '#' are shown on standard output (used to
-** test explicit warnings);
-** - messages containing '@' are stored in global '_WARN' (used to test
-** errors that generate warnings);
+** a warning in buffer 'buff'. Then, it has three modes:
+** - 0.normal: messages starting with '#' are shown on standard output;
 ** - other messages abort the tests (they represent real warning
 ** - other messages abort the tests (they represent real warning
 ** conditions; the standard tests should not generate these conditions
 ** conditions; the standard tests should not generate these conditions
-** unexpectedly).
+** unexpectedly);
+** - 1.allow: all messages are shown;
+** - 2.store: all warnings go to the global '_WARN';
 */
 */
 static void warnf (void *ud, const char *msg, int tocont) {
 static void warnf (void *ud, const char *msg, int tocont) {
   static char buff[200] = "";  /* should be enough for tests... */
   static char buff[200] = "";  /* should be enough for tests... */
+  static int onoff = 1;
+  static int mode = 0;  /* start in normal mode */
+  static int lasttocont = 0;
+  if (!lasttocont && !tocont && *msg == '@') {  /* control message? */
+    if (buff[0] != '\0')
+      badexit("Control warning during warning: %s\naborting...\n", msg);
+    if (strcmp(msg + 1, "off") == 0)
+      onoff = 0;
+    else if (strcmp(msg + 1, "on") == 0)
+      onoff = 1;
+    else if (strcmp(msg + 1, "normal") == 0)
+      mode = 0;
+    else if (strcmp(msg + 1, "allow") == 0)
+      mode = 1;
+    else if (strcmp(msg + 1, "store") == 0)
+      mode = 2;
+    else
+      badexit("Invalid control warning in test mode: %s\naborting...\n", msg);
+    return;
+  }
+  lasttocont = tocont;
   if (strlen(msg) >= sizeof(buff) - strlen(buff))
   if (strlen(msg) >= sizeof(buff) - strlen(buff))
     badexit("%s", "warnf-buffer overflow");
     badexit("%s", "warnf-buffer overflow");
   strcat(buff, msg);  /* add new message to current warning */
   strcat(buff, msg);  /* add new message to current warning */
   if (!tocont) {  /* message finished? */
   if (!tocont) {  /* message finished? */
-    if (buff[0] == '#')  /* expected warning? */
-      printf("Expected Lua warning: %s\n", buff);  /* print it */
-    else if (strchr(buff, '@') != NULL) {  /* warning for test purposes? */
-      lua_State *L = cast(lua_State *, ud);
-      lua_unlock(L);
-      lua_pushstring(L, buff);
-      lua_setglobal(L, "_WARN");  /* assign message to global '_WARN' */
-      lua_lock(L);
-    }
-    else  /* a real warning; should not happen during tests */
-      badexit("Unexpected warning in test mode: %s\naborting...\n", buff);
+    switch (mode) {
+      case 0: {  /* normal */
+        if (buff[0] != '#' && onoff)  /* unexpected warning? */
+          badexit("Unexpected warning in test mode: %s\naborting...\n", buff);
+        /* else */ /* FALLTHROUGH */
+      }
+      case 1: {  /* allow */
+        if (onoff)
+          fprintf(stderr, "Lua warning: %s\n", buff);  /* print warning */
+        break;
+      }
+      case 2: {  /* store */
+        lua_State *L = cast(lua_State *, ud);
+        lua_unlock(L);
+        lua_pushstring(L, buff);
+        lua_setglobal(L, "_WARN");  /* assign message to global '_WARN' */
+        lua_lock(L);
+        buff[0] = '\0';  /* prepare buffer for next warning */
+        break;
+      }
+    }
     buff[0] = '\0';  /* prepare buffer for next warning */
     buff[0] = '\0';  /* prepare buffer for next warning */
   }
   }
 }
 }

+ 24 - 12
lua.c

@@ -73,6 +73,7 @@ static void print_usage (const char *badoption) {
   "  -l name  require library 'name' into global 'name'\n"
   "  -l name  require library 'name' into global 'name'\n"
   "  -v       show version information\n"
   "  -v       show version information\n"
   "  -E       ignore environment variables\n"
   "  -E       ignore environment variables\n"
+  "  -q       turn warnings off\n"
   "  --       stop handling options\n"
   "  --       stop handling options\n"
   "  -        stop handling options and execute stdin\n"
   "  -        stop handling options and execute stdin\n"
   ,
   ,
@@ -259,14 +260,18 @@ static int collectargs (char **argv, int *first) {
       case '\0':  /* '-' */
       case '\0':  /* '-' */
         return args;  /* script "name" is '-' */
         return args;  /* script "name" is '-' */
       case 'E':
       case 'E':
-        if (argv[i][2] != '\0')  /* extra characters after 1st? */
+        if (argv[i][2] != '\0')  /* extra characters? */
           return has_error;  /* invalid option */
           return has_error;  /* invalid option */
         args |= has_E;
         args |= has_E;
         break;
         break;
+      case 'q':
+        if (argv[i][2] != '\0')  /* extra characters? */
+          return has_error;  /* invalid option */
+        break;
       case 'i':
       case 'i':
         args |= has_i;  /* (-i implies -v) *//* FALLTHROUGH */
         args |= has_i;  /* (-i implies -v) *//* FALLTHROUGH */
       case 'v':
       case 'v':
-        if (argv[i][2] != '\0')  /* extra characters after 1st? */
+        if (argv[i][2] != '\0')  /* extra characters? */
           return has_error;  /* invalid option */
           return has_error;  /* invalid option */
         args |= has_v;
         args |= has_v;
         break;
         break;
@@ -289,7 +294,8 @@ static int collectargs (char **argv, int *first) {
 
 
 
 
 /*
 /*
-** Processes options 'e' and 'l', which involve running Lua code.
+** Processes options 'e' and 'l', which involve running Lua code, and
+** 'q', which also affects the state.
 ** Returns 0 if some code raises an error.
 ** Returns 0 if some code raises an error.
 */
 */
 static int runargs (lua_State *L, char **argv, int n) {
 static int runargs (lua_State *L, char **argv, int n) {
@@ -297,15 +303,21 @@ static int runargs (lua_State *L, char **argv, int n) {
   for (i = 1; i < n; i++) {
   for (i = 1; i < n; i++) {
     int option = argv[i][1];
     int option = argv[i][1];
     lua_assert(argv[i][0] == '-');  /* already checked */
     lua_assert(argv[i][0] == '-');  /* already checked */
-    if (option == 'e' || option == 'l') {
-      int status;
-      const char *extra = argv[i] + 2;  /* both options need an argument */
-      if (*extra == '\0') extra = argv[++i];
-      lua_assert(extra != NULL);
-      status = (option == 'e')
-               ? dostring(L, extra, "=(command line)")
-               : dolibrary(L, extra);
-      if (status != LUA_OK) return 0;
+    switch (option) {
+      case 'e':  case 'l': {
+        int status;
+        const char *extra = argv[i] + 2;  /* both options need an argument */
+        if (*extra == '\0') extra = argv[++i];
+        lua_assert(extra != NULL);
+        status = (option == 'e')
+                 ? dostring(L, extra, "=(command line)")
+                 : dolibrary(L, extra);
+        if (status != LUA_OK) return 0;
+        break;
+      }
+      case 'q':
+        lua_warning(L, "@off", 0);  /* no warnings */
+        break;
     }
     }
   }
   }
   return 1;
   return 1;

+ 22 - 7
manual/manual.of

@@ -4370,6 +4370,8 @@ The third parameter is a boolean that
 indicates whether the message is
 indicates whether the message is
 to be continued by the message in the next call.
 to be continued by the message in the next call.
 
 
+See @Lid{warn} for more details about warnings.
+
 }
 }
 
 
 @APIEntry{
 @APIEntry{
@@ -4380,6 +4382,8 @@ Emits a warning with the given message.
 A message in a call with @id{tocont} true should be
 A message in a call with @id{tocont} true should be
 continued in another call to this function.
 continued in another call to this function.
 
 
+See @Lid{warn} for more details about warnings.
+
 }
 }
 
 
 @APIEntry{
 @APIEntry{
@@ -6355,6 +6359,16 @@ The current value of this variable is @St{Lua 5.4}.
 Emits a warning with a message composed by the concatenation
 Emits a warning with a message composed by the concatenation
 of all its arguments (which should be strings).
 of all its arguments (which should be strings).
 
 
+By convention,
+a one-piece message starting with @Char{@At}
+is intended to be a @emph{control message},
+which is a message to the warning system itself.
+In particular, the standard warning function in Lua
+recognizes the control messages @St{@At{}off},
+to stop the emission of warnings,
+and @St{@At{}on}, to (re)start the emission;
+it ignores unknown control messages.
+
 }
 }
 
 
 @LibEntry{xpcall (f, msgh [, arg1, @Cdots])|
 @LibEntry{xpcall (f, msgh [, arg1, @Cdots])|
@@ -7293,7 +7307,7 @@ stored as the first capture, and therefore has @N{number 1};
 the character matching @St{.} is captured with @N{number 2},
 the character matching @St{.} is captured with @N{number 2},
 and the part matching @St{%s*} has @N{number 3}.
 and the part matching @St{%s*} has @N{number 3}.
 
 
-As a special case, the empty capture @T{()} captures
+As a special case, the capture @T{()} captures
 the current string position (a number).
 the current string position (a number).
 For instance, if we apply the pattern @T{"()aa()"} on the
 For instance, if we apply the pattern @T{"()aa()"} on the
 string @T{"flaaap"}, there will be two captures: @N{3 and 5}.
 string @T{"flaaap"}, there will be two captures: @N{3 and 5}.
@@ -7858,7 +7872,6 @@ they are compared as @x{unsigned integers}.
 
 
 }
 }
 
 
-
 @sect2{iolib| @title{Input and Output Facilities}
 @sect2{iolib| @title{Input and Output Facilities}
 
 
 The I/O library provides two different styles for file manipulation.
 The I/O library provides two different styles for file manipulation.
@@ -8150,7 +8163,6 @@ There are three available modes:
 @item{@St{line}| line buffering.}
 @item{@St{line}| line buffering.}
 }
 }
 
 
-}
 For the last two cases,
 For the last two cases,
 @id{size} is a hint for the size of the buffer, in bytes.
 @id{size} is a hint for the size of the buffer, in bytes.
 The default is an appropriate size.
 The default is an appropriate size.
@@ -8708,6 +8720,7 @@ The options are:
 @item{@T{-i}| enters interactive mode after running @rep{script};}
 @item{@T{-i}| enters interactive mode after running @rep{script};}
 @item{@T{-v}| prints version information;}
 @item{@T{-v}| prints version information;}
 @item{@T{-E}| ignores environment variables;}
 @item{@T{-E}| ignores environment variables;}
+@item{@T{-q}| turn warnings off;}
 @item{@T{--}| stops handling options;}
 @item{@T{--}| stops handling options;}
 @item{@T{-}| executes @id{stdin} as a file and stops handling options.}
 @item{@T{-}| executes @id{stdin} as a file and stops handling options.}
 }
 }
@@ -8733,12 +8746,13 @@ setting the values of
 @Lid{package.path} and @Lid{package.cpath}
 @Lid{package.path} and @Lid{package.cpath}
 with the default paths defined in @id{luaconf.h}.
 with the default paths defined in @id{luaconf.h}.
 
 
-All options are handled in order, except @T{-i} and @T{-E}.
+The options @T{-e}, @T{-l}, and @T{-q} are handled in
+the order they appear.
 For instance, an invocation like
 For instance, an invocation like
 @verbatim{
 @verbatim{
-$ lua -e'a=1' -e 'print(a)' script.lua
+$ lua -e 'a=1' -llib1 script.lua
 }
 }
-will first set @id{a} to 1, then print the value of @id{a},
+will first set @id{a} to 1, then require the library @id{lib1},
 and finally run the file @id{script.lua} with no arguments.
 and finally run the file @id{script.lua} with no arguments.
 (Here @T{$} is the shell prompt. Your prompt may be different.)
 (Here @T{$} is the shell prompt. Your prompt may be different.)
 
 
@@ -8798,7 +8812,8 @@ has a metamethod @idx{__tostring},
 the interpreter calls this metamethod to produce the final message.
 the interpreter calls this metamethod to produce the final message.
 Otherwise, the interpreter converts the error object to a string
 Otherwise, the interpreter converts the error object to a string
 and adds a stack traceback to it.
 and adds a stack traceback to it.
-Warnings are simply printed in the standard error output.
+When warnings are on,
+they are simply printed in the standard error output.
 
 
 When finishing normally,
 When finishing normally,
 the interpreter closes its main Lua state
 the interpreter closes its main Lua state

+ 4 - 0
testes/all.lua

@@ -209,6 +209,10 @@ if #msgs > 0 then
   warn("#tests not performed:\n  ", m, "\n")
   warn("#tests not performed:\n  ", m, "\n")
 end
 end
 
 
+warn("@off")
+warn("******** THIS WARNING SHOULD NOT APPEAR **********")
+warn("******** THIS WARNING ALSO SHOULD NOT APPEAR **********")
+warn("@on")
 print("(there should be two warnings now)")
 print("(there should be two warnings now)")
 warn("#This is ", "an expected", " warning")
 warn("#This is ", "an expected", " warning")
 warn("#This is", " another one")
 warn("#This is", " another one")

+ 2 - 0
testes/api.lua

@@ -977,6 +977,7 @@ assert(t[7] == nil)
 
 
 -------------------------------------------------------------------------
 -------------------------------------------------------------------------
 do   -- testing errors during GC
 do   -- testing errors during GC
+  warn("@off")
   collectgarbage("stop")
   collectgarbage("stop")
   local a = {}
   local a = {}
   for i=1,20 do
   for i=1,20 do
@@ -994,6 +995,7 @@ do   -- testing errors during GC
   collectgarbage()
   collectgarbage()
   assert(A == 10)  -- number of normal collections
   assert(A == 10)  -- number of normal collections
   collectgarbage("restart")
   collectgarbage("restart")
+  warn("@on")
 end
 end
 -------------------------------------------------------------------------
 -------------------------------------------------------------------------
 -- test for userdata vals
 -- test for userdata vals

+ 7 - 1
testes/gc.lua

@@ -369,6 +369,7 @@ if T then
     s[n] = i
     s[n] = i
   end
   end
 
 
+  warn("@store")
   collectgarbage()
   collectgarbage()
   assert(string.find(_WARN, "error in __gc metamethod"))
   assert(string.find(_WARN, "error in __gc metamethod"))
   assert(string.match(_WARN, "@(.-)@") == "expected")
   assert(string.match(_WARN, "@(.-)@") == "expected")
@@ -383,6 +384,7 @@ if T then
   for i = 1, 10 do assert(s[i]) end
   for i = 1, 10 do assert(s[i]) end
 
 
   getmetatable(u).__gc = nil
   getmetatable(u).__gc = nil
+  warn("@normal")
 
 
 end
 end
 print '+'
 print '+'
@@ -475,9 +477,11 @@ end
 
 
 -- errors during collection
 -- errors during collection
 if T then
 if T then
+  warn("@store")
   u = setmetatable({}, {__gc = function () error "@expected error" end})
   u = setmetatable({}, {__gc = function () error "@expected error" end})
   u = nil
   u = nil
   collectgarbage()
   collectgarbage()
+  warn("@normal")
 end
 end
 
 
 
 
@@ -645,7 +649,7 @@ end
 
 
 -- create several objects to raise errors when collected while closing state
 -- create several objects to raise errors when collected while closing state
 if T then
 if T then
-  local error, assert, find = error, assert, string.find
+  local error, assert, find, warn = error, assert, string.find, warn
   local n = 0
   local n = 0
   local lastmsg
   local lastmsg
   local mt = {__gc = function (o)
   local mt = {__gc = function (o)
@@ -659,7 +663,9 @@ if T then
     else
     else
       assert(lastmsg == _WARN)  -- subsequent error messages are equal
       assert(lastmsg == _WARN)  -- subsequent error messages are equal
     end
     end
+    warn("@store")
     error"@expected warning"
     error"@expected warning"
+    warn("@normal")
   end}
   end}
   for i = 10, 1, -1 do
   for i = 10, 1, -1 do
     -- create object and preserve it until the end
     -- create object and preserve it until the end

+ 29 - 0
testes/main.lua

@@ -221,6 +221,28 @@ assert(string.find(getoutput(), "error calling 'print'"))
 RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
 RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
 checkout("lua_debug> 1000lua_debug> ")
 checkout("lua_debug> 1000lua_debug> ")
 
 
+-- test warnings
+RUN('echo "io.stderr:write(1); warn[[XXX]]" | lua -q 2> %s', out)
+checkout("1")
+
+prepfile[[
+warn("@allow")               -- unknown control, ignored
+warn("@off", "XXX", "@off")  -- these are not control messages
+warn("@off")                 -- this one is
+warn("@on", "YYY", "@on")    -- not control, but warn is off
+warn("@off")                 -- keep it off
+warn("@on")                  -- restart warnings
+warn("", "@on")              -- again, no control, real warning
+warn("@on")                  -- keep it "started"
+warn("Z", "Z", "Z")          -- common warning
+]]
+RUN('lua %s 2> %s', prog, out)
+checkout[[
+Lua warning: @offXXX@off
+Lua warning: @on
+Lua warning: ZZZ
+]]
+
 -- test many arguments
 -- test many arguments
 prepfile[[print(({...})[30])]]
 prepfile[[print(({...})[30])]]
 RUN('lua %s %s > %s', prog, string.rep(" a", 30), out)
 RUN('lua %s %s > %s', prog, string.rep(" a", 30), out)
@@ -355,8 +377,15 @@ if T then   -- test library?
   NoRun("not enough memory", "env MEMLIMIT=100 lua")
   NoRun("not enough memory", "env MEMLIMIT=100 lua")
 
 
   -- testing 'warn'
   -- testing 'warn'
+  warn("@store")
   warn("@123", "456", "789")
   warn("@123", "456", "789")
   assert(_WARN == "@123456789")
   assert(_WARN == "@123456789")
+
+  warn("zip", "", " ", "zap")
+  assert(_WARN == "zip zap")
+  warn("ZIP", "", " ", "ZAP")
+  assert(_WARN == "ZIP ZAP")
+  warn("@normal")
 end
 end
 
 
 do
 do