From 79674b4530e6d213da5e1e3d5647436cfeae1a0d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 5 Jun 2017 13:30:09 -0400
Subject: [PATCH 05/44] spellchecker for comments v3

Changes in v3:
  * moved back to spellcheck-enchant.h/.c, and
converted to a warning: -Wspellcheck-comments
  * added "--with-enchant" autoconfery, making the spellchecker
code optional, using PKG_CHECK_MODULES to invoke pkg-config
  * print details about the spellchecking provider info
if "-v" was parsed
  * if there's more than one suggestion, mark their fix-it
hints as mutually-incompatible (to keep
-fdiagnostics-generate-patch sane)
  * moved testcases to c-c++-common, and added a
dg-require-enchant for skipping them when gcc was not configured
with --with-enchant.
  * documented it within invoke.texi

DONE:
  * ChangeLog

TODO:
  * what about bootstraps?
  * what about cross-compilation?
  * test during a bootstrap?
  * what needs fixing?
  * what's the signal:noise ratio like?
  * blurb
  * reinstate -Wfixme and -Wtodo?

Changes in v2:
  * moved it all to a plugin, within the testsuite
  * added test coverage
  * added option -fplugin-arg-spellcheck-show-provider
  * added option -fplugin-arg-spellcheck-language
  * fixed bug with misspelling at very end of a comment
  * fixed bug with apostrophes breaking words
  * fixed quoting of suggestions
  * don't spellcheck within system headers
  * don't spellcheck URLs
  * removed the -Wfixme and -Wtodo idea

config/ChangeLog:
	* pkg.m4: New file, taken from pkg-config-0.24.

gcc/ChangeLog:
	* Makefile.in (ENCHANT_CFLAGS, ENCHANT_LIBS): Add.
	(BACKENDLIBS): Add $(ENCHANT_LIBS).
	(OBJS): Add spellcheck-enchant.o.
	(CFLAGS-spellcheck-enchant.o): Add $(ENCHANT_CFLAGS).
	* aclocal.m4: Include ../config/pkg.m4.
	* config.in: Regenerate.
	* configure.ac: Add --with-enchant.
	* doc/invoke.texi (Warning Options): Add -Wspellcheck-comments and
	-Wspellcheck-comments=.
	(-Wspellcheck-comments): New option.
	* spellcheck-enchant.c: New file.
	* spellcheck-enchant.h: New file.

gcc/c-family/ChangeLog:
	* c-lex.c: Include spellcheck-enchant.h.
	(init_c_lex): Wire up the spellchecking callback if requested,
	and configured with --with-enchant.
	* c-opts.c: Include spellcheck-enchant.h.
	(c_common_post_options): If -Wspellcheck-comments was requested,
	call spellcheck_enchant_init, if available.
	(c_common_finish): Call spellcheck_enchant_finish if available.
	* c.opt (warn_spellcheck_comments): Mark this as a string.
	(Wspellcheck-comments): New.
	(Wspellcheck-comments=): New.

gcc/testsuite/ChangeLog:
	* c-c++-common/Wspellcheck-comments-1.c: New test case.
	* c-c++-common/Wspellcheck-comments-2.c (UNKNOWN): Likewise.
	* c-c++-common/Wspellcheck-comments-3.c (UNKNOWN): Likewise.
	* c-c++-common/Wspellcheck-comments-4.c (UNKNOWN): Likewise.
	* c-c++-common/Wspellcheck-comments-5.c (UNKNOWN): Likewise.
	* lib/target-supports-dg.exp (dg-require-enchant): New procedure.
	* lib/target-supports.exp (check_configured_with_enchant): New
	procedure.
---
 config/pkg.m4                                      | 214 +++++++++++++
 gcc/Makefile.in                                    |   9 +-
 gcc/aclocal.m4                                     |   1 +
 gcc/c-family/c-lex.c                               |   9 +
 gcc/c-family/c-opts.c                              |   9 +
 gcc/c-family/c.opt                                 |  11 +
 gcc/config.in                                      |   6 +
 gcc/configure.ac                                   |  14 +
 gcc/doc/invoke.texi                                |  22 ++
 gcc/spellcheck-enchant.c                           | 345 +++++++++++++++++++++
 gcc/spellcheck-enchant.h                           |  37 +++
 .../c-c++-common/Wspellcheck-comments-1.c          |  22 ++
 .../c-c++-common/Wspellcheck-comments-2.c          |  14 +
 .../c-c++-common/Wspellcheck-comments-3.c          |  21 ++
 .../c-c++-common/Wspellcheck-comments-4.c          |   5 +
 .../c-c++-common/Wspellcheck-comments-5.c          |   6 +
 gcc/testsuite/lib/target-supports-dg.exp           |   9 +
 gcc/testsuite/lib/target-supports.exp              |   4 +
 18 files changed, 757 insertions(+), 1 deletion(-)
 create mode 100644 config/pkg.m4
 create mode 100644 gcc/spellcheck-enchant.c
 create mode 100644 gcc/spellcheck-enchant.h
 create mode 100644 gcc/testsuite/c-c++-common/Wspellcheck-comments-1.c
 create mode 100644 gcc/testsuite/c-c++-common/Wspellcheck-comments-2.c
 create mode 100644 gcc/testsuite/c-c++-common/Wspellcheck-comments-3.c
 create mode 100644 gcc/testsuite/c-c++-common/Wspellcheck-comments-4.c
 create mode 100644 gcc/testsuite/c-c++-common/Wspellcheck-comments-5.c

diff --git a/config/pkg.m4 b/config/pkg.m4
new file mode 100644
index 0000000..c5b26b5
--- /dev/null
+++ b/config/pkg.m4
@@ -0,0 +1,214 @@
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# serial 1 (pkg-config-0.24)
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+# only at the first occurence in configure.ac, so if the first place
+# it's called might be skipped (such as if it is within an "if", you
+# have to call PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_default([$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes ],
+		     [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+   	AC_MSG_RESULT([no])
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+        else 
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+        ])
+elif test $pkg_failed = untried; then
+     	AC_MSG_RESULT([no])
+	m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+        ])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	$3
+fi[]dnl
+])# PKG_CHECK_MODULES
+
+
+# PKG_INSTALLDIR(DIRECTORY)
+# -------------------------
+# Substitutes the variable pkgconfigdir as the location where a module
+# should install pkg-config .pc files. By default the directory is
+# $libdir/pkgconfig, but the default can be changed by passing
+# DIRECTORY. The user can override through the --with-pkgconfigdir
+# parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+    [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+    [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+]) dnl PKG_INSTALLDIR
+
+
+# PKG_NOARCH_INSTALLDIR(DIRECTORY)
+# -------------------------
+# Substitutes the variable noarch_pkgconfigdir as the location where a
+# module should install arch-independent pkg-config .pc files. By
+# default the directory is $datadir/pkgconfig, but the default can be
+# changed by passing DIRECTORY. The user can override through the
+# --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+    [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+    [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+]) dnl PKG_NOARCH_INSTALLDIR
+
+
+# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# -------------------------------------------
+# Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])# PKG_CHECK_VAR
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6e0e55a..fd4cce6 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -381,6 +381,10 @@ GMPINC = @GMPINC@
 ISLLIBS = @ISLLIBS@
 ISLINC = @ISLINC@
 
+# Enchant
+ENCHANT_CFLAGS = @ENCHANT_CFLAGS@
+ENCHANT_LIBS = @ENCHANT_LIBS@
+
 # Set to 'yes' if the LTO front end is enabled.
 enable_lto = @enable_lto@
 
@@ -1051,7 +1055,7 @@ BUILD_LIBDEPS= $(BUILD_LIBIBERTY)
 LIBS = @LIBS@ libcommon.a $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBBACKTRACE) \
 	$(LIBIBERTY) $(LIBDECNUMBER) $(HOST_LIBS)
 BACKENDLIBS = $(ISLLIBS) $(GMPLIBS) $(PLUGINLIBS) $(HOST_LIBS) \
-	$(ZLIB)
+	$(ZLIB) $(ENCHANT_LIBS)
 # Any system libraries needed just for GNAT.
 SYSLIBS = @GNAT_LIBEXC@
 
@@ -1455,6 +1459,7 @@ OBJS = \
 	simplify-rtx.o \
 	sparseset.o \
 	spellcheck.o \
+	spellcheck-enchant.o \
 	spellcheck-tree.o \
 	sreal.o \
 	stack-ptr-mod.o \
@@ -2060,6 +2065,8 @@ gcc-ranlib$(exeext): gcc-ranlib.o $(AR_OBJS) $(LIBDEPS)
 	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) gcc-ranlib.o -o $@ \
 		$(AR_OBJS) $(LIBS) $(AR_LIBS)
 
+CFLAGS-spellcheck-enchant.o += $(ENCHANT_CFLAGS)
+
 CFLAGS-gcc-ar.o += $(DRIVER_DEFINES) \
 	-DTARGET_MACHINE=\"$(target_noncanonical)\" \
 	@TARGET_SYSTEM_ROOT_DEFINE@ -DPERSONALITY=\"ar\"
diff --git a/gcc/aclocal.m4 b/gcc/aclocal.m4
index 84f8abb..dd93a0e 100644
--- a/gcc/aclocal.m4
+++ b/gcc/aclocal.m4
@@ -32,6 +32,7 @@ m4_include([../config/lib-prefix.m4])
 m4_include([../config/mmap.m4])
 m4_include([../config/override.m4])
 m4_include([../config/picflag.m4])
+m4_include([../config/pkg.m4])
 m4_include([../config/progtest.m4])
 m4_include([../config/stdint.m4])
 m4_include([../config/warnings.m4])
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index e1c8bdf..ed2617c 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "debug.h"
 
 #include "attribs.h"
+#include "spellcheck-enchant.h"
 
 /* We may keep statistics about how long which files took to compile.  */
 static int header_time, body_time;
@@ -82,6 +83,14 @@ init_c_lex (void)
   cb->has_attribute = c_common_has_attribute;
   cb->get_source_date_epoch = cb_get_source_date_epoch;
   cb->get_suggestion = cb_get_suggestion;
+  if (warn_spellcheck_comments)
+    {
+#ifdef HAVE_ENCHANT
+      cb->comment = spellcheck_enchant_check_comment;
+#else
+      sorry ("this build of gcc was not configured with libenchant");
+#endif
+    }
 
   /* Set the debug callbacks if we can use them.  */
   if ((debug_info_level == DINFO_LEVEL_VERBOSE
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index be4478f..7ebfe01 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"		/* For PLUGIN_INCLUDE_FILE event.  */
 #include "mkdeps.h"
 #include "dumpfile.h"
+#include "spellcheck-enchant.h"
 
 #ifndef DOLLARS_IN_IDENTIFIERS
 # define DOLLARS_IN_IDENTIFIERS true
@@ -993,6 +994,10 @@ c_common_post_options (const char **pfilename)
     }
   else
     {
+#ifdef HAVE_ENCHANT
+      if (warn_spellcheck_comments)
+	spellcheck_enchant_init (warn_spellcheck_comments);
+#endif
       init_c_lex ();
 
       /* When writing a PCH file, avoid reading some other PCH file,
@@ -1163,6 +1168,10 @@ c_common_finish (void)
      with cpp_destroy ().  */
   cpp_finish (parse_in, deps_stream);
 
+#ifdef HAVE_ENCHANT
+  spellcheck_enchant_finish ();
+#endif
+
   if (deps_stream && deps_stream != out_stream
       && (ferror (deps_stream) || fclose (deps_stream)))
     fatal_error (input_location, "closing dependency file %s: %m", deps_file);
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 37bb236..d3354b6 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -33,6 +33,9 @@ C++
 Language
 ObjC++
 
+Variable
+char *warn_spellcheck_comments
+
 -all-warnings
 C ObjC C++ ObjC++ Warning Alias(Wall)
 
@@ -722,6 +725,14 @@ Wsizeof-array-argument
 C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
 Warn when sizeof is applied on a parameter declared as an array.
 
+Wspellcheck-comments
+C ObjC C++ ObjC++ Warning Alias(Wspellcheck-comments=, en_US, 0)
+Warn about words in comments that may be misspelled.
+
+Wspellcheck-comments=
+C ObjC C++ ObjC++ Joined Warning Var(warn_spellcheck_comments)
+Warn about words in comments that may be misspelled.
+
 Wstringop-overflow
 C ObjC C++ ObjC++ Warning Alias(Wstringop-overflow=, 2, 0)
 Warn about buffer overflow in string manipulation functions like memcpy
diff --git a/gcc/config.in b/gcc/config.in
index bf2aa7b..9f0f407 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1134,6 +1134,12 @@
 #endif
 
 
+/* Define if libenchant is available */
+#ifndef USED_FOR_TARGET
+#undef HAVE_ENCHANT
+#endif
+
+
 /* Define to 1 if you have the <ext/hash_map> header file. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_EXT_HASH_MAP
diff --git a/gcc/configure.ac b/gcc/configure.ac
index acfe979..ae6e8bf 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -6359,6 +6359,20 @@ if test x"$ld_pushpopstate_support" = xyes; then
 fi
 AC_MSG_RESULT($ld_pushpopstate_support)
 
+# Optional spellchecking support, via libenchant
+
+AC_ARG_WITH([enchant],
+  AS_HELP_STRING([--with-enchant],
+                 [Build with the Enchant spellchecking library]))
+
+if test "x$with_enchant" = "xyes"; then
+   PKG_CHECK_MODULES([ENCHANT], [enchant])
+   AC_DEFINE(HAVE_ENCHANT, 1, [Define if libenchant is available])
+fi
+
+AC_SUBST([ENCHANT_CFLAGS])
+AC_SUBST([ENCHANT_LIBS])
+
 # Configure the subdirectories
 # AC_CONFIG_SUBDIRS($subdirs)
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 819e800..0714343 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -308,6 +308,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wsign-compare  -Wsign-conversion  -Wfloat-conversion @gol
 -Wno-scalar-storage-order  -Wsizeof-pointer-div @gol
 -Wsizeof-pointer-memaccess  -Wsizeof-array-argument @gol
+-Wspellcheck-comments -Wspellcheck-comments=@var{language-tag} @gol
 -Wstack-protector  -Wstack-usage=@var{len}  -Wstrict-aliasing @gol
 -Wstrict-aliasing=n  -Wstrict-overflow  -Wstrict-overflow=@var{n} @gol
 -Wstringop-overflow=@var{n} @gol
@@ -4629,6 +4630,27 @@ This warning level also warns about left-shifting 1 into the sign bit,
 unless C++14 mode is active.
 @end table
 
+@item -Wspellcheck-comments @r{(C and C++ only)}
+@itemx -Wspellcheck-comments=@var{language-tag} @r{(C and C++ only)}
+@opindex Wspellcheck-comments
+@opindex Wno-spellcheck-comments
+Warn about words in comments that may be misspelled.
+
+This warning is only available if GCC was configured with the Enchant
+spellchecking library (via @option{--with-enchant}).
+
+The option @option{-Wspellcheck-comments=} selects with human language
+to use (e.g. @code{en_GB}).
+
+If enabled via @option{-Wspellcheck-comments}, the warning defaults
+to using @code{en_US}.
+
+If an unrecognized language tag is used, a list of available
+spellchecking providers is printed.
+
+Details of which spellchecking-provider is in use can be seen
+via @option{-v}.
+
 @item -Wswitch
 @opindex Wswitch
 @opindex Wno-switch
diff --git a/gcc/spellcheck-enchant.c b/gcc/spellcheck-enchant.c
new file mode 100644
index 0000000..f62965a
--- /dev/null
+++ b/gcc/spellcheck-enchant.c
@@ -0,0 +1,345 @@
+/* Spellchecking using the Enchant "meta-library".
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "options.h"
+
+/* This file is guarded by "#ifdef HAVE_ENCHANT".  */
+
+#ifdef HAVE_ENCHANT
+
+/* Enchant is LGPL 2.1 "or (at your option) any later version." with
+   a special exception to allow non-LGPL spellchecking providers.  */
+
+#include <enchant.h>
+
+/* Class wrapping the result of enchant_dict_suggest, ensuring that
+   the suggestions are cleaned up.  */
+
+class suggestions
+{
+ public:
+  suggestions (EnchantDict *dict, const char *const word, ssize_t len)
+    : m_dict (dict), m_suggestions (NULL)
+    {
+      m_suggestions = enchant_dict_suggest (m_dict, word, len, &m_num);
+    }
+  ~suggestions ()
+    {
+      enchant_dict_free_string_list (m_dict, m_suggestions);
+    }
+
+  size_t get_count () const { return m_num; }
+  const char *get_suggestion (size_t index) const
+  {
+    return m_suggestions[index];
+  }
+
+ private:
+  EnchantDict *m_dict;
+  size_t m_num;
+  char **m_suggestions;
+};
+
+/* Callback for describing metadata about the Enchant provider in use.  */
+
+static void
+describe_broker_cb (const char * const provider_name,
+		    const char * const provider_desc,
+		    const char * const provider_dll_file,
+		    void *)
+{
+  inform (UNKNOWN_LOCATION,
+	  "spellcheck provider: %qs  description: %qs  file: %qs",
+	  provider_name, provider_desc, provider_dll_file);
+}
+
+/* A class for wrapping access to the Enchant API (both the broker
+   and the dict).  */
+
+class checker
+{
+ public:
+  checker (const char *language_tag);
+  ~checker ();
+
+  bool misspelled_p (const char *const word, ssize_t len);
+  suggestions get_suggestions (const char *const word, ssize_t len);
+
+  static checker *singleton;
+
+ private:
+  EnchantBroker *m_broker;
+  EnchantDict *m_dict;
+};
+
+/* Callback for describing the available languages.  */
+
+static void
+describe_dict_cb (const char * const lang_tag,
+		  const char * const provider_name,
+		  const char * const provider_desc,
+		  const char * const provider_file,
+		  void *)
+{
+  inform (UNKNOWN_LOCATION,
+	  "available language: %qs"
+	  " (provider: %qs  description: %qs  file: %qs)",
+	  lang_tag, provider_name, provider_desc, provider_file);
+}
+
+/* checker's ctor.  */
+
+checker::checker (const char *language_tag)
+: m_broker (NULL), m_dict (NULL)
+{
+  m_broker = enchant_broker_init ();
+  if (!m_broker)
+    {
+      error_at (UNKNOWN_LOCATION, "enchant_broker_init failed");
+      return;
+    }
+  m_dict = enchant_broker_request_dict (m_broker, language_tag);
+  if (!m_dict)
+    {
+      error_at (UNKNOWN_LOCATION, "unknown language tag: %qs", language_tag);
+      enchant_broker_list_dicts (m_broker, describe_dict_cb, NULL);
+    }
+
+  if (verbose_flag)
+    enchant_broker_describe (m_broker, describe_broker_cb, NULL);
+
+  // TODO: will probably want to support local wordlists also?
+}
+
+/* checker's dtor.  */
+
+checker::~checker ()
+{
+  if (m_dict)
+    enchant_broker_free_dict (m_broker, m_dict);
+  if (m_broker)
+    enchant_broker_free (m_broker);
+}
+
+/* Return true if WORD of length LEN appears to be misspelled.  */
+
+bool
+checker::misspelled_p (const char *const word, ssize_t len)
+{
+  if (!m_dict)
+    return false;
+
+  int result = enchant_dict_check (m_dict, word, len);
+  return result != 0;
+}
+
+/* Get suggestions for misspelled WORD of length LEN.  */
+
+suggestions
+checker::get_suggestions (const char *const word, ssize_t len)
+{
+  gcc_assert (m_dict);
+  return suggestions (m_dict, word, len);
+}
+
+/* Singleton instance of checker.  */
+
+checker *checker::singleton = NULL;
+
+/* Generate a location for a word of length LEN within ORD_MAP at the
+   given LINE AND COLUMN.  */
+
+static location_t
+make_word_location (const line_map_ordinary *ord_map, int line, int column,
+		    size_t len)
+{
+  location_t start_loc
+    = linemap_position_for_line_and_column (line_table, ord_map, line, column);
+  location_t end_loc
+    = linemap_position_for_line_and_column (line_table, ord_map, line,
+					    column + len - 1);
+  return make_location (start_loc, start_loc, end_loc);
+}
+
+/* Spellcheck the word at WORD of length LEN, with a location
+   within ORD_MAP at the given LINE AND COLUMN.
+   Issue warnings and suggestions for possibly misspelled words.  */
+
+static void
+spellcheck_word (const unsigned char *word, size_t len,
+		 const line_map_ordinary *ord_map, int line, int column)
+{
+  /* Don't bother spellchecking words with underscores or any upper case,
+     as these may refer to code entities.
+     TODO: should we also merge in names from code?  */
+  for (size_t i = 0; i < len; i++)
+    {
+      if (word[i] == '_' || ISUPPER (word[i]))
+	return;
+    }
+
+  if (!checker::singleton->misspelled_p ((const char *)word, len))
+    return;
+
+  location_t word_loc = make_word_location (ord_map, line, column, len);
+
+  if (warning_at (word_loc, OPT_Wspellcheck_comments_,
+		  "possible spelling mistake in comment: %q.*s",
+		  (int)len, word))
+    {
+      suggestions s
+	= checker::singleton->get_suggestions ((const char *)word, len);
+      for (size_t i = 0; i < s.get_count (); i++)
+	{
+	  rich_location richloc (line_table, word_loc);
+	  richloc.add_fixit_replace (s.get_suggestion (i));
+
+	  /* If there's more than one suggestion, then the
+	     suggestions are mutually exclusive.  */
+	  if (s.get_count () > 1)
+	    richloc.fixits_cannot_be_auto_applied ();
+
+	  inform_at_rich_loc (&richloc, "suggestion: %qs",
+			      s.get_suggestion (i));
+	}
+    }
+}
+
+/* Initialize this module, using the given LANGUAGE_TAG as the
+   language to use for spellchecking (e.g. "en_US").  */
+
+void
+spellcheck_enchant_init (const char *language_tag)
+{
+  gcc_assert (language_tag);
+  checker::singleton = new checker (language_tag);
+}
+
+/* Callback for cpp_callbacks::comments for spellchecking comments
+   using Enchant.  */
+
+void
+spellcheck_enchant_check_comment (cpp_reader *, source_location loc,
+				  const unsigned char *content, size_t len)
+{
+  /* Don't spellcheck within system headers.  */
+  if (in_system_header_at (loc))
+    return;
+
+  /* Split into words, tracking the location, and call spellcheck_word
+     on each word.  */
+
+  /* CONTENT contains the opening slash-star (or slash-slash),
+     and for C-style comments contains the closing star-slash.  */
+  gcc_assert (len >= 2);
+  gcc_assert (content[0] == '/');
+  gcc_assert (content[1] == '*' || content[1] == '/');
+  bool c_style = (content[1] == '*');
+  if (c_style)
+    {
+      gcc_assert (content[len - 2] == '*');
+      gcc_assert (content[len - 1] == '/');
+    }
+  const unsigned char *iter = content + 2;
+  const unsigned char *after_last_char = content + len;
+  if (c_style)
+    after_last_char -= 2;
+  const unsigned char *word_start = NULL;
+
+  const struct line_map *map = linemap_lookup (line_table, loc);
+  const line_map_ordinary *ord_map = linemap_check_ordinary (map);
+
+  int column = LOCATION_COLUMN (loc) + 2;
+  int line = LOCATION_LINE (loc);
+  int word_start_column = column;
+  int word_start_line = line;
+
+  bool within_url = false;
+  while (iter < after_last_char)
+    {
+      unsigned char ch = *iter;
+      if (ISALPHA (ch) || ch == '_' || ch == '\'')
+	{
+	  if (!word_start)
+	    {
+	      /* We have the start of a word.  */
+	      word_start = iter;
+	      word_start_column = column;
+	      word_start_line = line;
+	    }
+	}
+      else
+	{
+	  if (word_start)
+	    {
+	      /* We have a word ending.  */
+	      size_t word_len = iter - word_start;
+	      /* Don't check any comments that appear to contain DejaGnu
+		 directives.  */
+	      if (word_len >= 2)
+		if (0 == strncmp ((const char *)word_start, "dg", 2))
+		  return;
+
+	      /* Detect things that look like URLs.  */
+	      /* "www."  */
+	      if (word_len == 3 && ch == '.')
+		if (0 == strncmp ((const char *)word_start, "www", 3))
+		  within_url = true;
+
+	      /* "http:"  */
+	      if (word_len == 4 && ch == ':')
+		if (0 == strncmp ((const char *)word_start, "http", 4))
+		  within_url = true;
+
+	      if (!within_url)
+		spellcheck_word (word_start, word_len, ord_map,
+				 word_start_line, word_start_column);
+	      word_start = NULL;
+	    }
+	}
+      if (ISSPACE (ch))
+	within_url = false;
+      if (ch == '\n')
+	{
+	  column = 1;
+	  line++;
+	}
+      else
+	column++;
+      iter++;
+    }
+  if (word_start)
+    if (!within_url)
+      spellcheck_word (word_start, iter - word_start, ord_map,
+		       word_start_line, word_start_column);
+}
+
+/* Clean up this module.  */
+
+void
+spellcheck_enchant_finish (void)
+{
+  delete checker::singleton;
+}
+
+#endif /* #ifdef HAVE_ENCHANT */
diff --git a/gcc/spellcheck-enchant.h b/gcc/spellcheck-enchant.h
new file mode 100644
index 0000000..ac069f7
--- /dev/null
+++ b/gcc/spellcheck-enchant.h
@@ -0,0 +1,37 @@
+/* Spellchecking using the Enchant "meta-library".
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_SPELLCHECK_ENCHANT_H
+#define GCC_SPELLCHECK_ENCHANT_H
+
+#ifdef HAVE_ENCHANT
+
+extern void spellcheck_enchant_init (const char *language_tag);
+
+/* Callback for cpp_callbacks::comments for spellchecking comments
+   using Enchant.  */
+
+extern void spellcheck_enchant_check_comment (cpp_reader *, source_location,
+					      const unsigned char *, size_t);
+
+extern void spellcheck_enchant_finish (void);
+
+#endif /* #ifdef HAVE_ENCHANT  */
+
+#endif  /* GCC_SPELLCHECK_ENCHANT_H  */
diff --git a/gcc/testsuite/c-c++-common/Wspellcheck-comments-1.c b/gcc/testsuite/c-c++-common/Wspellcheck-comments-1.c
new file mode 100644
index 0000000..690fd66
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wspellcheck-comments-1.c
@@ -0,0 +1,22 @@
+/* { dg-require-enchant "" } */
+/* { dg-options "-Wspellcheck-comments" }  */
+
+/* When NONCONST_PRED is false the code will evaulate to constant.  */
+/* { dg-warning "46: possible spelling mistake in comment: 'evaulate'" "" { target *-*-* } .-1 } */
+/* { dg-message "46: suggestion: 'evaluate'" "" { target *-*-* } .-2 } */
+/* There may be other suggestions in each of these cases.  */
+
+// Example of a boogus C++-style comment
+/* { dg-warning "17: possible spelling mistake in comment: 'boogus'" "" { target *-*-* } .-1 } */
+/* { dg-message "17: suggestion: 'bogus'" "" { target *-*-* } .-2 } */
+
+/* Exercise the code path for the final word in a commment*/
+/* { dg-warning "51: possible spelling mistake in comment: 'commment'" "" { target *-*-* } .-1 } */
+/* { dg-message "51: suggestion: 'comment'" "" { target *-*-* } .-2 } */
+
+// Exercise the code path for the final word in a C++ commment
+/* { dg-warning "55: possible spelling mistake in comment: 'commment'" "" { target *-*-* } .-1 } */
+/* { dg-message "55: suggestion: 'comment'" "" { target *-*-* } .-2 } */
+
+/* Examples of words that are correctly spelled, containing apostrophes:
+   don't doesn't is isn't.  */
diff --git a/gcc/testsuite/c-c++-common/Wspellcheck-comments-2.c b/gcc/testsuite/c-c++-common/Wspellcheck-comments-2.c
new file mode 100644
index 0000000..10c1825
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wspellcheck-comments-2.c
@@ -0,0 +1,14 @@
+/* { dg-require-enchant "" } */
+/* { dg-options "-Wspellcheck-comments -fdiagnostics-show-caret" } */
+
+/* Verify that misspelled words are underlined correctly.  We need
+   a word that the checker won't offer a suggestion for, to avoid
+   having to specify checker output within the test case.
+   Hence the use of splendiferouslitudinalistic here.  */
+#if 0
+{ dg-warning "possible spelling mistake in comment: 'splendiferouslitudinalistic'" "" { target *-*-* } .-2 }
+{ dg-begin-multiline-output "" }
+    Hence the use of splendiferouslitudinalistic here.
+                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+{ dg-end-multiline-output "" }
+#endif
diff --git a/gcc/testsuite/c-c++-common/Wspellcheck-comments-3.c b/gcc/testsuite/c-c++-common/Wspellcheck-comments-3.c
new file mode 100644
index 0000000..385f84c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wspellcheck-comments-3.c
@@ -0,0 +1,21 @@
+/* Verify that we don't complain about our legal boilerplate.  */
+/* { dg-require-enchant "" } */
+/* { dg-options "-Wspellcheck-comments" } */
+
+/* Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
diff --git a/gcc/testsuite/c-c++-common/Wspellcheck-comments-4.c b/gcc/testsuite/c-c++-common/Wspellcheck-comments-4.c
new file mode 100644
index 0000000..02a64f4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wspellcheck-comments-4.c
@@ -0,0 +1,5 @@
+/* { dg-require-enchant "" } */
+/* { dg-options "-Wspellcheck-comments=this_is_not_valid" } */
+// { dg-error "unknown language tag: 'this_is_not_valid'" "" { target *-*-* } 0 }
+
+/* A test comment.  */
diff --git a/gcc/testsuite/c-c++-common/Wspellcheck-comments-5.c b/gcc/testsuite/c-c++-common/Wspellcheck-comments-5.c
new file mode 100644
index 0000000..0f6fa8e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wspellcheck-comments-5.c
@@ -0,0 +1,6 @@
+/* Example of specifying language.  */
+/* { dg-require-enchant "" } */
+/* { dg-options "-Wspellcheck-comments=en_GB" } */
+// Is it color or colour?
+// { dg-warning "possible spelling mistake in comment: 'color'" "" { target *-*-* } .-1 }
+// { dg-message "suggestion: 'colour'" "" { target *-*-* } .-2 }
diff --git a/gcc/testsuite/lib/target-supports-dg.exp b/gcc/testsuite/lib/target-supports-dg.exp
index 6400d64..1648436 100644
--- a/gcc/testsuite/lib/target-supports-dg.exp
+++ b/gcc/testsuite/lib/target-supports-dg.exp
@@ -265,6 +265,15 @@ proc dg-require-linker-plugin { args } {
     }
 }
 
+# If this host does not support enchant, skip this test.
+
+proc dg-require-enchant { args } {
+    if { ![check_configured_with_enchant] } {
+	upvar dg-do-what dg-do-what
+        set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
+    }
+}
+
 # Add any target-specific flags needed for accessing the given list
 # of features.  This must come after all dg-options.
 
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index ded6383..b23ff0e 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -8391,3 +8391,7 @@ proc check_effective_target_arm_coproc4_ok { } {
     return [check_cached_effective_target arm_coproc4_ok \
 		check_effective_target_arm_coproc4_ok_nocache]
 }
+
+proc check_configured_with_enchant {} {
+    return [check_configured_with "-with-enchant"]
+}
-- 
1.8.5.3

