C++: provide macro used-before-defined hint (PR c++/72786)
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 21 Nov 2017 00:40:53 +0000 (00:40 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Tue, 21 Nov 2017 00:40:53 +0000 (00:40 +0000)
This patch uses the name_hint/deferred_diagnostic to provide
a message in the C++ frontend if a macro is used before it is defined
e.g.:

test.c:6:24: error: expected ';' at end of member declaration
   virtual void clone() const OVERRIDE { }
                        ^~~~~
                             ;
test.c:6:30: error: 'OVERRIDE' does not name a type
   virtual void clone() const OVERRIDE { }
                              ^~~~~~~~
test.c:6:30: note: the macro 'OVERRIDE' had not yet been defined
test.c:15:0: note: it was later defined here
 #define OVERRIDE override

It's possible to do it from the C++ frontend as tokenization happens
up-front (and hence the macro already exists when the above is parsed);
I attempted to do it from the C frontend, but because the C frontend only
tokenizes on-demand during parsing, the macro isn't known about until
later.

gcc/cp/ChangeLog:
PR c++/72786
* name-lookup.c (class macro_use_before_def): New class.
(lookup_name_fuzzy): Detect macro that were used before being
defined, and report them as such.

gcc/ChangeLog:
PR c++/72786
* spellcheck.h (best_match::blithely_get_best_candidate): New
accessor.

gcc/testsuite/ChangeLog:
PR c++/72786
* g++.dg/spellcheck-macro-ordering-2.C: New test case.
* g++.dg/spellcheck-macro-ordering.C: Add dg-message directives
for macro used-before-defined.

libcpp/ChangeLog:
PR c++/72786
* include/cpplib.h (cpp_macro_definition_location): New decl.
* macro.c (cpp_macro_definition): New function.

From-SVN: r254978

gcc/ChangeLog
gcc/cp/ChangeLog
gcc/cp/name-lookup.c
gcc/spellcheck.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/spellcheck-macro-ordering-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
libcpp/ChangeLog
libcpp/include/cpplib.h
libcpp/macro.c

index 4589f5b..0255d8a 100644 (file)
@@ -1,3 +1,9 @@
+2017-11-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/72786
+       * spellcheck.h (best_match::blithely_get_best_candidate): New
+       accessor.
+
 2017-11-20  Jakub Jelinek  <jakub@redhat.com>
 
        * config/i386/i386.c (parse_mtune_ctrl_str): Start diagnostics
index 5de732f..908b48e 100644 (file)
@@ -1,3 +1,10 @@
+2017-11-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/72786
+       * name-lookup.c (class macro_use_before_def): New class.
+       (lookup_name_fuzzy): Detect macro that were used before being
+       defined, and report them as such.
+
 2017-11-20  Jason Merrill  <jason@redhat.com>
 
        * decl2.c (constrain_class_visibility): Don't warn about artificial
index 9d97da3..fc317b1 100644 (file)
@@ -5636,12 +5636,49 @@ consider_binding_level (tree name, best_match <tree, const char *> &bm,
     }
 }
 
+/* Subclass of deferred_diagnostic.  Notify the user that the
+   given macro was used before it was defined.
+   This can be done in the C++ frontend since tokenization happens
+   upfront.  */
+
+class macro_use_before_def : public deferred_diagnostic
+{
+ public:
+  /* Ctor.  LOC is the location of the usage.  MACRO is the
+     macro that was used.  */
+  macro_use_before_def (location_t loc, cpp_hashnode *macro)
+  : deferred_diagnostic (loc), m_macro (macro)
+  {
+    gcc_assert (macro);
+  }
+
+  ~macro_use_before_def ()
+  {
+    if (is_suppressed_p ())
+      return;
+
+    source_location def_loc = cpp_macro_definition_location (m_macro);
+    if (def_loc != UNKNOWN_LOCATION)
+      {
+       inform (get_location (), "the macro %qs had not yet been defined",
+               (const char *)m_macro->ident.str);
+       inform (def_loc, "it was later defined here");
+      }
+  }
+
+ private:
+  cpp_hashnode *m_macro;
+};
+
+
 /* Search for near-matches for NAME within the current bindings, and within
    macro names, returning the best match as a const char *, or NULL if
-   no reasonable match is found.  */
+   no reasonable match is found.
+
+   Use LOC for any deferred diagnostics.  */
 
 name_hint
-lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t)
+lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t loc)
 {
   gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
 
@@ -5671,6 +5708,15 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t)
   /* If a macro is the closest so far to NAME, consider it.  */
   if (best_macro)
     bm.consider ((const char *)best_macro->ident.str);
+  else if (bmm.get_best_distance () == 0)
+    {
+      /* If we have an exact match for a macro name, then the
+        macro has been used before it was defined.  */
+      cpp_hashnode *macro = bmm.blithely_get_best_candidate ();
+      if (macro)
+       return name_hint (NULL,
+                         new macro_use_before_def (loc, macro));
+    }
 
   /* Try the "starts_decl_specifier_p" keywords to detect
      "singed" vs "signed" typos.  */
index 2edc695..bad3c1e 100644 (file)
@@ -178,6 +178,13 @@ class best_match
     return m_best_candidate;
   }
 
+  /* Get the closest candidate so far, without applying any filtering.  */
+
+  candidate_t blithely_get_best_candidate () const
+  {
+    return m_best_candidate;
+  }
+
   edit_distance_t get_best_distance () const { return m_best_distance; }
   size_t get_best_candidate_length () const { return m_best_candidate_len; }
 
index 05dd8e0..4c9569f 100644 (file)
@@ -1,3 +1,10 @@
+2017-11-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/72786
+       * g++.dg/spellcheck-macro-ordering-2.C: New test case.
+       * g++.dg/spellcheck-macro-ordering.C: Add dg-message directives
+       for macro used-before-defined.
+
 2017-11-20  Steve Ellcey  <sellcey@cavium.com>
 
        PR target/81356
diff --git a/gcc/testsuite/g++.dg/spellcheck-macro-ordering-2.C b/gcc/testsuite/g++.dg/spellcheck-macro-ordering-2.C
new file mode 100644 (file)
index 0000000..73c0f21
--- /dev/null
@@ -0,0 +1,17 @@
+// PR c++/72786
+
+/* Example of undeffed macro.  */
+
+#define OVERRIDE override
+
+#undef OVERRIDE
+
+class DocTargetDriver {
+  virtual void clone() const OVERRIDE { } // { dg-line usage }
+  /* Offering "OVERRIDE" as a spelling suggestion for "OVERRIDE" would be
+     nonsensical.  */
+  // { dg-bogus "did you mean" "" { target *-*-* } usage }
+  // { dg-error "expected .;. at end of member declaration" "" { target *-*-* } usage }
+  // { dg-error ".OVERRIDE. does not name a type" "" { target *-*-* } usage }
+  // { dg-bogus "macro" "" { target *-*-* } usage }
+};
index 3b888c6..bbd41f4 100644 (file)
@@ -9,7 +9,8 @@ class DocTargetDriver {
   // { dg-bogus "did you mean" "" { target *-*-* } .-3 }
   // { dg-error "expected .;. at end of member declaration" "" { target *-*-* } .-4 }
   // { dg-error ".OVERRIDE. does not name a type" "" { target *-*-* } .-5 }
+  // { dg-message "the macro 'OVERRIDE' had not yet been defined" "" { target *-*-* } .-6 }
 };
 
 #define OVERRIDE override
-
+// { dg-message "-:it was later defined here" "" { target *-*-* } .-1 }
index 70c834c..cc5d4d3 100644 (file)
@@ -1,3 +1,9 @@
+2017-11-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/72786
+       * include/cpplib.h (cpp_macro_definition_location): New decl.
+       * macro.c (cpp_macro_definition): New function.
+
 2017-11-13  Tom Tromey  <tom@tromey.com>
 
        * pch.c (cpp_read_state): Set n__VA_OPT__.
index 101b33a..4d04a48 100644 (file)
@@ -889,6 +889,7 @@ extern const cpp_token *cpp_get_token_with_location (cpp_reader *,
 extern bool cpp_fun_like_macro_p (cpp_hashnode *);
 extern const unsigned char *cpp_macro_definition (cpp_reader *,
                                                  cpp_hashnode *);
+extern source_location cpp_macro_definition_location (cpp_hashnode *);
 extern void _cpp_backup_tokens (cpp_reader *, unsigned int);
 extern const cpp_token *cpp_peek_token (cpp_reader *, int);
 
index bf473ea..43f2baa 100644 (file)
@@ -3646,3 +3646,11 @@ cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node)
   *buffer = '\0';
   return pfile->macro_buffer;
 }
+
+/* Get the line at which the macro was defined.  */
+
+source_location
+cpp_macro_definition_location (cpp_hashnode *node)
+{
+  return node->value.macro->line;
+}