diff mbox

[2/3] C++: provide macro used-before-defined hint (PR c++/72786).

Message ID 1494006675-28033-2-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm May 5, 2017, 5:51 p.m. UTC
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.
---
 gcc/cp/name-lookup.c                               | 47 +++++++++++++++++++++-
 gcc/spellcheck.h                                   |  7 ++++
 gcc/testsuite/g++.dg/spellcheck-macro-ordering-2.C | 17 ++++++++
 gcc/testsuite/g++.dg/spellcheck-macro-ordering.C   |  3 +-
 libcpp/include/cpplib.h                            |  1 +
 libcpp/macro.c                                     |  8 ++++
 6 files changed, 80 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/spellcheck-macro-ordering-2.C

Comments

Jeff Law July 3, 2017, 4:27 p.m. UTC | #1
On 05/05/2017 11:51 AM, David Malcolm wrote:
> 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.
This is fine once the prereq is approved.

jeff
diff mbox

Patch

diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index de8c267..93bea35 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -4952,12 +4952,46 @@  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);
+  }
+
+  void emit () OVERRIDE FINAL
+  {
+    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);
 
@@ -4987,6 +5021,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.  */
diff --git a/gcc/spellcheck.h b/gcc/spellcheck.h
index 2edc695..bad3c1e 100644
--- a/gcc/spellcheck.h
+++ b/gcc/spellcheck.h
@@ -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; }
 
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
index 0000000..73c0f21
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-macro-ordering-2.C
@@ -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 }
+};
diff --git a/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C b/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
index 3b888c6..a2c7bba 100644
--- a/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
+++ b/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
@@ -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 }
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index b843992..cf2fdc6 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -876,6 +876,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);
 
diff --git a/libcpp/macro.c b/libcpp/macro.c
index de18c22..16fdd2a 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -3472,3 +3472,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;
+}