aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2016-08-18 18:52:43 +0000
committerDavid Malcolm <dmalcolm@gcc.gnu.org>2016-08-18 18:52:43 +0000
commitcb18fd07f2962779c2651adc970541210d4ad98f (patch)
treeb325fd07e1c9c2f08551326602a989303e28ddfd
parenta76989dc7c9236214856db196da88a739f0e7baa (diff)
downloadgcc-cb18fd07f2962779c2651adc970541210d4ad98f.zip
gcc-cb18fd07f2962779c2651adc970541210d4ad98f.tar.gz
gcc-cb18fd07f2962779c2651adc970541210d4ad98f.tar.bz2
Spelling suggestions for misspelled preprocessor directives
This patch allows the preprocessor to offer suggestions for misspelled directives, taking us from e.g.: test.c:5:2: error: invalid preprocessing directive #endfi #endfi ^~~~~ to: test.c:5:2: error: invalid preprocessing directive #endfi; did you mean #endif? #endfi ^~~~~ endif gcc/c-family/ChangeLog: * c-common.c: Include "spellcheck.h". (cb_get_suggestion): New function. * c-common.h (cb_get_suggestion): New decl. * c-lex.c (init_c_lex): Initialize cb->get_suggestion to cb_get_suggestion. gcc/testsuite/ChangeLog: * gcc.dg/cpp/misspelled-directive-1.c: New testcase. * gcc.dg/cpp/misspelled-directive-2.c: New testcase. libcpp/ChangeLog: * directives.c (directive_names): New array. (_cpp_handle_directive): Offer spelling suggestions for misspelled directives. * errors.c (cpp_diagnostic_at_richloc): New function. (cpp_error_at_richloc): New function. * include/cpplib.h (struct cpp_callbacks): Add field "get_suggestion". (cpp_error_at_richloc): New decl. From-SVN: r239585
-rw-r--r--gcc/c-family/ChangeLog8
-rw-r--r--gcc/c-family/c-common.c17
-rw-r--r--gcc/c-family/c-common.h5
-rw-r--r--gcc/c-family/c-lex.c1
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.dg/cpp/misspelled-directive-1.c12
-rw-r--r--gcc/testsuite/gcc.dg/cpp/misspelled-directive-2.c21
-rw-r--r--libcpp/ChangeLog11
-rw-r--r--libcpp/directives.c41
-rw-r--r--libcpp/errors.c36
-rw-r--r--libcpp/include/cpplib.h8
11 files changed, 163 insertions, 2 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index cc82370..1ed5268 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,11 @@
+2016-08-18 David Malcolm <dmalcolm@redhat.com>
+
+ * c-common.c: Include "spellcheck.h".
+ (cb_get_suggestion): New function.
+ * c-common.h (cb_get_suggestion): New decl.
+ * c-lex.c (init_c_lex): Initialize cb->get_suggestion to
+ cb_get_suggestion.
+
2016-08-18 Marek Polacek <polacek@redhat.com>
PR c/71514
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 22e3844..9082883 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
#include "opts.h"
#include "gimplify.h"
#include "substring-locations.h"
+#include "spellcheck.h"
cpp_reader *parse_in; /* Declared in c-pragma.h. */
@@ -12948,6 +12949,22 @@ cb_get_source_date_epoch (cpp_reader *pfile ATTRIBUTE_UNUSED)
return (time_t) epoch;
}
+/* Callback for libcpp for offering spelling suggestions for misspelled
+ directives. GOAL is an unrecognized string; CANDIDATES is a
+ NULL-terminated array of candidate strings. Return the closest
+ match to GOAL within CANDIDATES, or NULL if none are good
+ suggestions. */
+
+const char *
+cb_get_suggestion (cpp_reader *, const char *goal,
+ const char *const *candidates)
+{
+ best_match<const char *, const char *> bm (goal);
+ while (*candidates)
+ bm.consider (*candidates++);
+ return bm.get_best_meaningful_candidate ();
+}
+
/* Check and possibly warn if two declarations have contradictory
attributes, such as always_inline vs. noinline. */
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 4673123..31320bf 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1110,6 +1110,11 @@ extern time_t cb_get_source_date_epoch (cpp_reader *pfile);
__TIME__ can store. */
#define MAX_SOURCE_DATE_EPOCH HOST_WIDE_INT_C (253402300799)
+/* Callback for libcpp for offering spelling suggestions for misspelled
+ directives. */
+extern const char *cb_get_suggestion (cpp_reader *, const char *,
+ const char *const *);
+
extern GTY(()) string_concat_db *g_string_concat_db;
/* libcpp can calculate location information about a range of characters
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index 4c7e385..c904ee6 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -81,6 +81,7 @@ init_c_lex (void)
cb->read_pch = c_common_read_pch;
cb->has_attribute = c_common_has_attribute;
cb->get_source_date_epoch = cb_get_source_date_epoch;
+ cb->get_suggestion = cb_get_suggestion;
/* Set the debug callbacks if we can use them. */
if ((debug_info_level == DINFO_LEVEL_VERBOSE
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e37bff4..21e9770 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2016-08-18 David Malcolm <dmalcolm@redhat.com>
+
+ * gcc.dg/cpp/misspelled-directive-1.c: New testcase.
+ * gcc.dg/cpp/misspelled-directive-2.c: New testcase.
+
2016-08-18 Marek Polacek <polacek@redhat.com>
PR c/71514
diff --git a/gcc/testsuite/gcc.dg/cpp/misspelled-directive-1.c b/gcc/testsuite/gcc.dg/cpp/misspelled-directive-1.c
new file mode 100644
index 0000000..f79670a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/misspelled-directive-1.c
@@ -0,0 +1,12 @@
+#ifndef SOME_GUARD /* { dg-error "unterminated" } */
+
+#if 1
+/* Typo here: "endfi" should have been "endif". */
+#endfi /* { dg-error "invalid preprocessing directive #endfi; did you mean #endif?" } */
+
+int make_non_empty;
+
+/* Another transposition typo: */
+#deifne FOO /* { dg-error "invalid preprocessing directive #deifne; did you mean #define?" } */
+
+#endif /* #ifndef SOME_GUARD */
diff --git a/gcc/testsuite/gcc.dg/cpp/misspelled-directive-2.c b/gcc/testsuite/gcc.dg/cpp/misspelled-directive-2.c
new file mode 100644
index 0000000..7ec5dee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/misspelled-directive-2.c
@@ -0,0 +1,21 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+#endfi /* { dg-error "invalid preprocessing directive #endfi; did you mean #endif?" } */
+
+/* Verify that we offer fix-it hints. */
+/* { dg-begin-multiline-output "" }
+ #endfi
+ ^~~~~
+ endif
+ { dg-end-multiline-output "" } */
+
+/* Test coverage for the case of an unrecognized directive where no suggestion
+ is offered. */
+
+#this_does_not_match_anything /* { dg-error "invalid preprocessing directive #this_does_not_match_anything" } */
+/* { dg-begin-multiline-output "" }
+ #this_does_not_match_anything
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+
+int make_non_empty;
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index e700dfe..5d4a094 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -1,3 +1,14 @@
+2016-08-18 David Malcolm <dmalcolm@redhat.com>
+
+ * directives.c (directive_names): New array.
+ (_cpp_handle_directive): Offer spelling suggestions for misspelled
+ directives.
+ * errors.c (cpp_diagnostic_at_richloc): New function.
+ (cpp_error_at_richloc): New function.
+ * include/cpplib.h (struct cpp_callbacks): Add field
+ "get_suggestion".
+ (cpp_error_at_richloc): New decl.
+
2016-08-18 Marek Polacek <polacek@redhat.com>
PR c/7652
diff --git a/libcpp/directives.c b/libcpp/directives.c
index 772b835..c0006a4 100644
--- a/libcpp/directives.c
+++ b/libcpp/directives.c
@@ -188,6 +188,16 @@ static const directive dtable[] =
DIRECTIVE_TABLE
};
#undef D
+
+/* A NULL-terminated array of directive names for use
+ when suggesting corrections for misspelled directives. */
+#define D(name, t, origin, flags) #name,
+static const char * const directive_names[] = {
+DIRECTIVE_TABLE
+ NULL
+};
+#undef D
+
#undef DIRECTIVE_TABLE
/* Wrapper struct directive for linemarkers.
@@ -498,8 +508,35 @@ _cpp_handle_directive (cpp_reader *pfile, int indented)
if (CPP_OPTION (pfile, lang) == CLK_ASM)
skip = 0;
else if (!pfile->state.skipping)
- cpp_error (pfile, CPP_DL_ERROR, "invalid preprocessing directive #%s",
- cpp_token_as_text (pfile, dname));
+ {
+ const char *unrecognized
+ = (const char *)cpp_token_as_text (pfile, dname);
+ const char *hint = NULL;
+
+ /* Call back into gcc to get a spelling suggestion. Ideally
+ we'd just use best_match from gcc/spellcheck.h (and filter
+ out the uncommon directives), but that requires moving it
+ to a support library. */
+ if (pfile->cb.get_suggestion)
+ hint = pfile->cb.get_suggestion (pfile, unrecognized,
+ directive_names);
+
+ if (hint)
+ {
+ rich_location richloc (pfile->line_table, dname->src_loc);
+ source_range misspelled_token_range
+ = get_range_from_loc (pfile->line_table, dname->src_loc);
+ richloc.add_fixit_replace (misspelled_token_range, hint);
+ cpp_error_at_richloc (pfile, CPP_DL_ERROR, &richloc,
+ "invalid preprocessing directive #%s;"
+ " did you mean #%s?",
+ unrecognized, hint);
+ }
+ else
+ cpp_error (pfile, CPP_DL_ERROR,
+ "invalid preprocessing directive #%s",
+ unrecognized);
+ }
}
pfile->directive = dir;
diff --git a/libcpp/errors.c b/libcpp/errors.c
index f7d4112..3b0a0b4 100644
--- a/libcpp/errors.c
+++ b/libcpp/errors.c
@@ -31,6 +31,23 @@ along with this program; see the file COPYING3. If not see
ATTRIBUTE_FPTR_PRINTF(5,0)
static bool
+cpp_diagnostic_at_richloc (cpp_reader * pfile, int level, int reason,
+ rich_location *richloc,
+ const char *msgid, va_list *ap)
+{
+ bool ret;
+
+ if (!pfile->cb.error)
+ abort ();
+ ret = pfile->cb.error (pfile, level, reason, richloc, _(msgid), ap);
+
+ return ret;
+}
+
+/* Print a diagnostic at the given location. */
+
+ATTRIBUTE_FPTR_PRINTF(5,0)
+static bool
cpp_diagnostic_at (cpp_reader * pfile, int level, int reason,
source_location src_loc,
const char *msgid, va_list *ap)
@@ -255,6 +272,25 @@ cpp_error_at (cpp_reader * pfile, int level, source_location src_loc,
return ret;
}
+/* As cpp_error, but use RICHLOC as the location of the error, without
+ a column override. */
+
+bool
+cpp_error_at_richloc (cpp_reader * pfile, int level, rich_location *richloc,
+ const char *msgid, ...)
+{
+ va_list ap;
+ bool ret;
+
+ va_start (ap, msgid);
+
+ ret = cpp_diagnostic_at_richloc (pfile, level, CPP_W_NONE, richloc,
+ msgid, &ap);
+
+ va_end (ap);
+ return ret;
+}
+
/* Print a warning or error, depending on the value of LEVEL. Include
information from errno. */
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 659686b..a497811 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -597,6 +597,9 @@ struct cpp_callbacks
/* Callback to parse SOURCE_DATE_EPOCH from environment. */
time_t (*get_source_date_epoch) (cpp_reader *);
+
+ /* Callback for providing suggestions for misspelled directives. */
+ const char *(*get_suggestion) (cpp_reader *, const char *, const char *const *);
};
#ifdef VMS
@@ -1066,6 +1069,11 @@ extern bool cpp_error_at (cpp_reader * pfile, int level,
source_location src_loc, const char *msgid, ...)
ATTRIBUTE_PRINTF_4;
+extern bool cpp_error_at_richloc (cpp_reader * pfile, int level,
+ rich_location *richloc, const char *msgid,
+ ...)
+ ATTRIBUTE_PRINTF_4;
+
/* In lex.c */
extern int cpp_ideq (const cpp_token *, const char *);
extern void cpp_output_line (cpp_reader *, FILE *);