]> granicus.if.org Git - re2c/commitdiff
Improved '-Wmatch-empty-string' warning.
authorUlya Trofimovich <skvadrik@gmail.com>
Thu, 8 Oct 2015 13:38:19 +0000 (14:38 +0100)
committerUlya Trofimovich <skvadrik@gmail.com>
Thu, 8 Oct 2015 13:38:19 +0000 (14:38 +0100)
- recognize empty match with nonempty trailing context
- don't report unreachable empty match

15 files changed:
re2c/Makefile.am
re2c/src/codegen/emit_dfa.cc
re2c/src/codegen/prepare_dfa.cc
re2c/src/codegen/skeleton/control_flow.cc
re2c/src/codegen/skeleton/generate_data.cc
re2c/src/codegen/skeleton/match_empty.cc [new file with mode: 0644]
re2c/src/codegen/skeleton/path.h
re2c/src/codegen/skeleton/skeleton.cc
re2c/src/codegen/skeleton/skeleton.h
re2c/src/codegen/skeleton/unreachable.cc
re2c/test/bug116.c
re2c/test/rexx.--empty-class(error).c
re2c/test/rexx.--empty-class(match-empty).c
re2c/test/rexx.--empty-class(match-none).c
re2c/test/rexx.c

index 561e0be599fafe0ca5568e130a0a35742ca850d7..d5ac3ba1499a64a6d132f8c2f801f7ac02eb7ca7 100644 (file)
@@ -88,6 +88,7 @@ SRC = \
        src/codegen/skeleton/control_flow.cc \
        src/codegen/skeleton/generate_code.cc \
        src/codegen/skeleton/generate_data.cc \
+       src/codegen/skeleton/match_empty.cc \
        src/codegen/skeleton/maxlen.cc \
        src/codegen/skeleton/skeleton.cc \
        src/codegen/skeleton/unreachable.cc \
index a8db944789573be2b10edc009465f624fa0ebcfd..ef0d3106650ec7e906a8d6c0197213c94e66dcb7 100644 (file)
@@ -127,6 +127,7 @@ void DFA::emit(Output & output, uint32_t& ind, bool isLastCond, bool& bPrologBra
 
        skeleton->warn_undefined_control_flow ();
        skeleton->warn_unreachable_rules ();
+       skeleton->warn_match_empty ();
 
        if (opts->target == opt_t::SKELETON)
        {
index 6919ff4075b509a28e60ae10bd3eb9de047be7b8..5a5276364f0e32e1411a3543f8c8331565d27731 100644 (file)
@@ -222,25 +222,6 @@ void DFA::prepare ()
                default_state->action.set_accept (&accepts);
        }
 
-       const bool empty_rule = head->rule;
-
-       std::vector<std::pair<uint32_t, uint32_t> > stray_cunits;
-       uint32_t lb = 0;
-       for (uint32_t i = 0; i < head->go.nSpans; ++i)
-       {
-               if (!head->go.span[i].to->rule)
-               {
-                       stray_cunits.push_back (std::make_pair (lb, head->go.span[i].ub));
-               }
-               lb = head->go.span[i].ub;
-       }
-
-       // warn about not shadowed rule that matches empty string
-       if (empty_rule && !stray_cunits.empty ())
-       {
-               warn.match_empty_string (head->rule->loc.line);
-       }
-
        // split ``base'' states into two parts
        for (State * s = head; s; s = s->next)
        {
index 629e238abe7210d292e6ded2a30fe27ca50420ec..e5fa3fe314f56b5f9b1439278087a6f9b31d324f 100644 (file)
@@ -8,7 +8,7 @@ namespace re2c
 // We only need some examples, the shorter the better.
 Node::nakeds_t Node::naked_ways (const way_t & prefix, std::vector<way_t> & ways)
 {
-       if (!rule.is_none ())
+       if (!rule.rank.is_none ())
        {
                return nakeds_t (0u);
        }
index 0985514ad16a134722f37ccd0462093c712cddf2..e81ca95015289ac29a1f4bae9e729c40ac033d6a 100644 (file)
@@ -111,7 +111,7 @@ template <typename cunit_t, typename key_t>
                for (arcs_t::iterator i = arcs.begin (); i != arcs.end (); ++i)
                {
                        multipath_t new_prefix = prefix;
-                       new_prefix.extend (i->first->rule, i->first->restorectx, i->first->ctx, &i->second);
+                       new_prefix.extend (i->first->rule, i->first->ctx, &i->second);
                        i->first->permutate<cunit_t, key_t> (new_prefix, input, keys);
                }
        }
@@ -152,7 +152,7 @@ template <typename cunit_t, typename key_t>
        }
        else if (end ())
        {
-               suffix = new path_t (rule, restorectx, ctx);
+               suffix = new path_t (rule, ctx);
        }
        else if (loop < 2)
        {
@@ -160,7 +160,7 @@ template <typename cunit_t, typename key_t>
                for (arcs_t::iterator i = arcs.begin (); i != arcs.end (); ++i)
                {
                        multipath_t new_prefix = prefix;
-                       new_prefix.extend (i->first->rule, i->first->restorectx, i->first->ctx, &i->second);
+                       new_prefix.extend (i->first->rule, i->first->ctx, &i->second);
                        size = size + i->first->cover<cunit_t, key_t> (new_prefix, input, keys);
                        if (size.overflow ())
                        {
@@ -168,7 +168,7 @@ template <typename cunit_t, typename key_t>
                        }
                        if (i->first->suffix != NULL && suffix == NULL)
                        {
-                               suffix = new path_t (rule, restorectx, ctx);
+                               suffix = new path_t (rule, ctx);
                                suffix->append (i->second[0], i->first->suffix);
                        }
                }
@@ -179,7 +179,7 @@ template <typename cunit_t, typename key_t>
 template <typename cunit_t, typename key_t>
        void Skeleton::generate_paths_cunit_key (FILE * input, FILE * keys)
 {
-       multipath_t prefix (nodes->rule, nodes->restorectx, nodes->ctx);
+       multipath_t prefix (nodes->rule, nodes->ctx);
        if (nodes->sizeof_permutate (Node::permuts_t (1u), Node::permuts_t (0u)).overflow ())
        {
                if (nodes->cover<cunit_t, key_t> (prefix, input, keys).overflow ())
diff --git a/re2c/src/codegen/skeleton/match_empty.cc b/re2c/src/codegen/skeleton/match_empty.cc
new file mode 100644 (file)
index 0000000..8d2224c
--- /dev/null
@@ -0,0 +1,41 @@
+#include "src/codegen/skeleton/skeleton.h"
+
+namespace re2c
+{
+
+void Skeleton::warn_match_empty ()
+{
+       Node & head = nodes[0];
+
+       head.calc_reachable ();
+       const std::set<rule_t> & reach = head.reachable;
+
+       // warn about rules that match empty string
+       if (!head.rule.rank.is_none ())
+       {
+               bool reachable = head.end ();
+               for (std::set<rule_t>::const_iterator i = reach.begin ();
+                       !reachable && i != reach.end (); ++i)
+               {
+                       reachable |= i->rank.is_none ();
+               }
+               if (reachable)
+               {
+                       warn.match_empty_string (rules[head.rule.rank].line);
+               }
+       }
+
+       // warn about rules that match empty string with nonempty trailing context
+       if (head.ctx)
+       {
+               for (std::set<rule_t>::const_iterator i = reach.begin (); i != reach.end (); ++i)
+               {
+                       if (i->restorectx)
+                       {
+                               warn.match_empty_string (rules[i->rank].line);
+                       }
+               }
+       }
+}
+
+} // namespace re2c
index dc54fd9e6bd495ccb8c7821a30e0a0d416cd016b..5474823d9ca6120d4a4ebb6e0284798508a4c198 100644 (file)
@@ -9,23 +9,40 @@
 namespace re2c
 {
 
+struct rule_t
+{
+       rule_rank_t rank;
+       bool restorectx;
+
+       rule_t (rule_rank_t r, bool c)
+               : rank (r)
+               , restorectx (c)
+       {}
+
+       // needed by STL containers
+       // same as 'std::pair' comparator
+       bool operator < (const rule_t & r) const
+       {
+               return rank < r.rank
+                       || (!(r.rank < rank) && restorectx < r.restorectx);
+       }
+};
+
 template <typename arc_t>
 class generic_path_t
 {
        std::vector<arc_t> arcs;
 
-       rule_rank_t rule;
-       bool restorectx;
+       rule_t rule;
        size_t rule_pos;
 
        bool ctx;
        size_t ctx_pos;
 
 public:
-       explicit generic_path_t (rule_rank_t r, bool rc, bool c)
+       explicit generic_path_t (rule_t r, bool c)
                : arcs ()
                , rule (r)
-               , restorectx (rc)
                , rule_pos (0)
                , ctx (c)
                , ctx_pos (0)
@@ -36,25 +53,24 @@ public:
        }
        size_t len_matching () const
        {
-               return restorectx
+               return rule.restorectx
                        ? ctx_pos
                        : rule_pos;
        }
        rule_rank_t match () const
        {
-               return rule;
+               return rule.rank;
        }
        const arc_t & operator [] (size_t i) const
        {
                return arcs[i];
        }
-       void extend (rule_rank_t r, bool rc, bool c, const arc_t & a)
+       void extend (rule_t r, bool c, const arc_t & a)
        {
                arcs.push_back (a);
-               if (!r.is_none ())
+               if (!r.rank.is_none ())
                {
                        rule = r;
-                       restorectx = rc;
                        rule_pos = arcs.size ();
                }
                if (c)
@@ -66,10 +82,9 @@ public:
        void append (const arc_t & a, const generic_path_t<arc_t> * p)
        {
                arcs.push_back (a);
-               if (!p->rule.is_none ())
+               if (!p->rule.rank.is_none ())
                {
                        rule = p->rule;
-                       restorectx = p->restorectx;
                        rule_pos = arcs.size () + p->rule_pos;
                }
                if (p->ctx)
@@ -89,10 +104,10 @@ public:
 template <typename arc1_t, typename arc2_t>
        size_t len_matching (const generic_path_t<arc1_t> & prefix, const generic_path_t<arc2_t> & suffix)
 {
-       const bool none = suffix.rule.is_none ();
+       const bool none = suffix.rule.rank.is_none ();
        bool restorectx = none
-               ? prefix.restorectx
-               : suffix.restorectx;
+               ? prefix.rule.restorectx
+               : suffix.rule.restorectx;
        const size_t rule_pos = none
                ? prefix.rule_pos
                : prefix.arcs.size () + suffix.rule_pos;
@@ -109,9 +124,9 @@ template <typename arc1_t, typename arc2_t>
 template <typename arc1_t, typename arc2_t>
        rule_rank_t match (const generic_path_t<arc1_t> & prefix, const generic_path_t<arc2_t> & suffix)
 {
-       return suffix.rule.is_none ()
-               ? prefix.rule
-               : suffix.rule;
+       return suffix.rule.rank.is_none ()
+               ? prefix.rule.rank
+               : suffix.rule.rank;
 }
 
 typedef generic_path_t<uint32_t> path_t;
index d64f55a3430574bef901f29a062e3e66ff26cde4..ce0d8589d1abad64e8837061989b930cc92b1ae9 100644 (file)
@@ -11,8 +11,7 @@ Node::Node ()
        : arcs ()
        , arcsets ()
        , loop (0)
-       , rule (rule_rank_t::none ())
-       , restorectx (false)
+       , rule (rule_rank_t::none (), false)
        , ctx (false)
        , dist (DIST_ERROR)
        , reachable ()
@@ -24,8 +23,8 @@ void Node::init (const State * s, const s2n_map & s2n)
        const bool is_accepting = s && s->rule;
        if (is_accepting)
        {
-               rule = s->rule->rank;
-               restorectx = s->rule->ctx->fixedLength () != 0;
+               rule.rank = s->rule->rank;
+               rule.restorectx = s->rule->ctx->fixedLength () != 0;
        }
 
        ctx = s && s->isPreCtxt;
@@ -102,7 +101,7 @@ Skeleton::Skeleton (const DFA & dfa, const rules_t & rs)
        uint32_t maxrule = 0;
        for (uint32_t i = 0; i < nodes_count; ++i)
        {
-               const rule_rank_t r = nodes[i].rule;
+               const rule_rank_t r = nodes[i].rule.rank;
                if (!r.is_none ())
                {
                        maxrule = std::max (maxrule, r.uint32 ());
index c8977774508dfed33b1373b7d2a92eb0ac74142b..cefa2ef402635578da7af73ef9a52936997f3fa9 100644 (file)
@@ -45,10 +45,8 @@ struct Node
        // (controls looping in graph traversals)
        uint8_t loop;
 
-       // rule number for corresponding DFA state (if any)
-       rule_rank_t rule;
-       // whether this rule must rollback input position to the beginnig of trailing context
-       bool restorectx;
+       // rule for corresponding DFA state (if any)
+       rule_t rule;
 
        // start of trailing context
        bool ctx;
@@ -59,7 +57,7 @@ struct Node
        uint32_t dist;
 
        // rules reachable from this node (including absent rule)
-       std::set<rule_rank_t> reachable;
+       std::set<rule_t> reachable;
 
        // path to end node (for constructing path cover)
        path_t * suffix;
@@ -95,6 +93,7 @@ struct Skeleton
        ~Skeleton ();
        void warn_undefined_control_flow ();
        void warn_unreachable_rules ();
+       void warn_match_empty ();
        void emit_data (const char * fname);
        static void emit_prolog (OutputFile & o);
        void emit_start
index 8310ef47235f56820b4725277bc683e404465009..aaada72c7d86fb6e993e26ddad94f3a74134aeb8 100644 (file)
@@ -29,11 +29,11 @@ void Skeleton::warn_unreachable_rules ()
        nodes->calc_reachable ();
        for (uint32_t i = 0; i < nodes_count; ++i)
        {
-               const rule_rank_t r1 = nodes[i].rule;
-               const std::set<rule_rank_t> & rs = nodes[i].reachable;
-               for (std::set<rule_rank_t>::const_iterator j = rs.begin (); j != rs.end (); ++j)
+               const rule_rank_t r1 = nodes[i].rule.rank;
+               const std::set<rule_t> & rs = nodes[i].reachable;
+               for (std::set<rule_t>::const_iterator j = rs.begin (); j != rs.end (); ++j)
                {
-                       const rule_rank_t r2 = *j;
+                       const rule_rank_t r2 = j->rank;
                        if (r1 == r2 || r2.is_none ())
                        {
                                rules[r1].reachable = true;
index 91a630312df222b3bed5920464ce22806724050a..31aa1cd9f23c8e6a185f8e2d784b11bc22c01048 100644 (file)
@@ -1,4 +1,5 @@
 re2c: warning: line 3: control flow is undefined for strings that match '[\x0-\x60\x62-\xFF]', use default rule '*' [-Wundefined-control-flow]
+re2c: warning: line 2: rule matches empty string [-Wmatch-empty-string]
 /* Generated by re2c */
 #line 1 "bug116.re"
 
index d60e0479793b21f3abf31cac2591485cb8b0a156..b83b89e9a8ccccf7e75697a92000fc72d1a4e441 100644 (file)
@@ -1 +1,2 @@
+re2c: warning: line 253: rule matches empty string [-Wmatch-empty-string]
 re2c: error: line 288, column 1: empty character class
index 3e1e2db225525bd0b1558dc6c5d9a302b000181c..cf9fbc18d386cd7b8065847a3953043f3967088a 100644 (file)
@@ -1,4 +1,6 @@
+re2c: warning: line 253: rule matches empty string [-Wmatch-empty-string]
 re2c: warning: line 288: empty character class [-Wempty-character-class]
+re2c: warning: line 289: rule matches empty string [-Wmatch-empty-string]
 /* Generated by re2c */
 #line 1 "rexx.--empty-class(match-empty).re"
 #include "scanio.h"
index f579cef9fcb8cfa27c599de5d2aa7d53a4c87516..d34af568d1e71b81ac6c70ba055f8f452ae490fa 100644 (file)
@@ -1,3 +1,4 @@
+re2c: warning: line 253: rule matches empty string [-Wmatch-empty-string]
 re2c: warning: line 288: empty character class [-Wempty-character-class]
 re2c: warning: line 290: control flow is undefined for strings that match 
        '[\x0-\x8\xA-\xC\xE-\x1F\x21-\x2E\x30-\xFF]'
index 987bc4ea6a75b8a721f7a0cfd081e0ba252e3924..4d0242dbac972faa9fb4884e867fb39761e642f5 100644 (file)
@@ -1,4 +1,6 @@
+re2c: warning: line 253: rule matches empty string [-Wmatch-empty-string]
 re2c: warning: line 288: empty character class [-Wempty-character-class]
+re2c: warning: line 289: rule matches empty string [-Wmatch-empty-string]
 /* Generated by re2c */
 #line 1 "rexx.re"
 #include "scanio.h"