From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13237 invoked by alias); 3 Jul 2016 22:46:16 -0000 Mailing-List: contact fortran-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: fortran-owner@gcc.gnu.org Received: (qmail 13215 invoked by uid 89); 3 Jul 2016 22:46:15 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.1 required=5.0 tests=BAYES_00,FREEMAIL_FROM,KAM_STOCKGEN,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=no version=3.3.2 spammy=helpers, MEMBER, covers, 2803 X-Spam-User: qpsmtpd, 2 recipients X-HELO: mail-wm0-f50.google.com Received: from mail-wm0-f50.google.com (HELO mail-wm0-f50.google.com) (74.125.82.50) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Sun, 03 Jul 2016 22:46:05 +0000 Received: by mail-wm0-f50.google.com with SMTP id f126so83987758wma.1; Sun, 03 Jul 2016 15:46:05 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:user-agent:in-reply-to:references:mime-version :content-transfer-encoding:subject:from:date:to:cc:message-id; bh=K3bctihhdr1X3wgWPK0oiDOZyPkKhfZr3t3Gxh5g+8U=; b=Ju7RZSOW3tOgcHcyn4hG/0SF3dcKXvC3t1dTn9I+th7PXsfgGHkRwqMsjJKVLDn7Cm n3G9m9qt4fZ4wki6RVMNSacNWrtjlju1qsoZjkRKKgvkHEXG9qSA6BMvNR6RlERzNkHL n4RJFFsDQjYWX9nkwf1jOtoAZQUBYdhxpv7KOLsx+tslAUyeVPY8XCP0VWNG1D2eXjrl tDqri6h+dDnLHOvT2A4ga6EcQyAnxNlgKvPE5mZ60ThjsYOB0GKEX3wsucuZWREGSfC5 qOeezWmC3pvOm8oi/RXXGUKIf7ECsZLVXTjPA2y1Mo7CAcnFgoCFHQSqwYyadUeoJagO LDiQ== X-Gm-Message-State: ALyK8tINONKlEz78jl9JkgcC4nrCAXqaPj9V0eH3eLu9A6RmIb7ve96qnXLk0FYVx2juIw== X-Received: by 10.28.140.202 with SMTP id o193mr8178955wmd.55.1467585962418; Sun, 03 Jul 2016 15:46:02 -0700 (PDT) Received: from [10.57.110.157] (089144211157.atnat0020.highway.a1.net. [89.144.211.157]) by smtp.gmail.com with ESMTPSA id s67sm567853wmf.3.2016.07.03.15.45.59 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 03 Jul 2016 15:46:00 -0700 (PDT) User-Agent: K-9 Mail for Android In-Reply-To: <1466279927-9661-1-git-send-email-rep.dot.nop@gmail.com> References: <1461604041.4417.14.camel@redhat.com> <1466279927-9661-1-git-send-email-rep.dot.nop@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=UTF-8 Subject: Ping: [Re: [PATCH, fortran, v4] Use Levenshtein spelling suggestions in Fortran FE] From: Bernhard Reutner-Fischer Date: Sun, 03 Jul 2016 22:46:00 -0000 To: fortran@gcc.gnu.org CC: gcc-patches@gcc.gnu.org,David Malcolm ,VandeVondele Joost Message-ID: <6C3ABDCF-FBB7-45E2-9AFD-5C0082FC27D7@gmail.com> X-IsSubscribed: yes X-SW-Source: 2016-07/txt/msg00005.txt.bz2 Ping On June 18, 2016 9:58:47 PM GMT+02:00, Bernhard Reutner-Fischer wrote: >Hi, > >Ok for trunk? > >Changes for v4 -> v3: > >- rebased >- Use 4 argument levenshtein_distance() to save multiple strlen(typo) > calls as suggested by dmalcolm > >Changes for v2 -> v3: > >- rebased > >Changes for v1 -> v2: > >- subroutines using interfaces >- keyword arguments (named parameters) > >Rewrite C++ autovec in plain C. >Factor out levenshtein distance handling into a commonly used >gfc_closest_fuzzy_match(). > >gcc/fortran/ChangeLog > >2015-12-27 Bernhard Reutner-Fischer > > * gfortran.h (gfc_lookup_function_fuzzy): New declaration. > (gfc_closest_fuzzy_match): New declaration. > (vec_push): New definition. > * misc.c (gfc_closest_fuzzy_match): New definition. > * resolve.c: Include spellcheck.h. > (lookup_function_fuzzy_find_candidates): New static function. > (lookup_uop_fuzzy_find_candidates): Likewise. > (lookup_uop_fuzzy): Likewise. > (resolve_operator) : Call lookup_uop_fuzzy. > (gfc_lookup_function_fuzzy): New definition. > (resolve_unknown_f): Call gfc_lookup_function_fuzzy. > * interface.c (check_interface0): Likewise. > (lookup_arg_fuzzy_find_candidates): New static function. > (lookup_arg_fuzzy ): Likewise. > (compare_actual_formal): Call lookup_arg_fuzzy. > * symbol.c: Include spellcheck.h. > (lookup_symbol_fuzzy_find_candidates): New static function. > (lookup_symbol_fuzzy): Likewise. > (gfc_set_default_type): Call lookup_symbol_fuzzy. > (lookup_component_fuzzy_find_candidates): New static function. > (lookup_component_fuzzy): Likewise. > (gfc_find_component): Call lookup_component_fuzzy. > >gcc/testsuite/ChangeLog > >2015-12-27 Bernhard Reutner-Fischer > > * gfortran.dg/spellcheck-operator.f90: New testcase. > * gfortran.dg/spellcheck-procedure_1.f90: New testcase. > * gfortran.dg/spellcheck-procedure_2.f90: New testcase. > * gfortran.dg/spellcheck-structure.f90: New testcase. > * gfortran.dg/spellcheck-parameter.f90: New testcase. > >--- > >David Malcolm's nice Levenshtein distance spelling check helpers >were used in some parts of other frontends. This proposed patch adds >some spelling corrections to the fortran frontend. > >Suggestions are printed if we can find a suitable name, currently >perusing a very simple cutoff factor: >/* If more than half of the letters were misspelled, the suggestion is > likely to be meaningless. */ >cutoff = MAX (strlen (typo), strlen (best_guess)) / 2; >which effectively skips names with less than 4 characters. >For e.g. structures, one could try to be much smarter in an attempt to >also provide suggestions for single-letter members/components. > >This patch covers (at least partly): >- user-defined operators >- structures (types and their components) >- functions >- symbols (variables) > >If anybody has a testcase where a spelling-suggestion would make sense >then please pass it along so we maybe can add support for GCC-7. > >Signed-off-by: Bernhard Reutner-Fischer >--- > gcc/fortran/gfortran.h | 12 +++ >gcc/fortran/interface.c | 72 >+++++++++++++-- > gcc/fortran/misc.c | 41 +++++++++ >gcc/fortran/resolve.c | 100 >++++++++++++++++++++- >gcc/fortran/symbol.c | 86 >+++++++++++++++++- > gcc/testsuite/gfortran.dg/spellcheck-operator.f90 | 30 +++++++ > gcc/testsuite/gfortran.dg/spellcheck-parameter.f90 | 15 ++++ > .../gfortran.dg/spellcheck-procedure_1.f90 | 41 +++++++++ > .../gfortran.dg/spellcheck-procedure_2.f90 | 35 ++++++++ > gcc/testsuite/gfortran.dg/spellcheck-structure.f90 | 35 ++++++++ > 10 files changed, 450 insertions(+), 17 deletions(-) > create mode 100644 gcc/testsuite/gfortran.dg/spellcheck-operator.f90 > create mode 100644 gcc/testsuite/gfortran.dg/spellcheck-parameter.f90 >create mode 100644 gcc/testsuite/gfortran.dg/spellcheck-procedure_1.f90 >create mode 100644 gcc/testsuite/gfortran.dg/spellcheck-procedure_2.f90 > create mode 100644 gcc/testsuite/gfortran.dg/spellcheck-structure.f90 > >diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h >index 0bb71cb..5d43c2d 100644 >--- a/gcc/fortran/gfortran.h >+++ b/gcc/fortran/gfortran.h >@@ -2682,6 +2682,17 @@ void gfc_done_2 (void); > > int get_c_kind (const char *, CInteropKind_t *); > >+const char *gfc_closest_fuzzy_match (const char *, char **); >+static inline void >+vec_push (char **&optr, size_t &osz, const char *elt) >+{ >+ /* {auto,}vec.safe_push () replacement. Don't ask.. */ >+ // if (strlen (elt) < 4) return; premature optimization: eliminated >by cutoff >+ optr = XRESIZEVEC (char *, optr, osz + 2); >+ optr[osz] = CONST_CAST (char *, elt); >+ optr[++osz] = NULL; >+} >+ > /* options.c */ > unsigned int gfc_option_lang_mask (void); > void gfc_init_options_struct (struct gcc_options *); >@@ -3103,6 +3114,7 @@ bool gfc_type_is_extensible (gfc_symbol *); > bool gfc_resolve_intrinsic (gfc_symbol *, locus *); > bool gfc_explicit_interface_required (gfc_symbol *, char *, int); > extern int gfc_do_concurrent_flag; >+const char* gfc_lookup_function_fuzzy (const char *, gfc_symtree *); > > > /* array.c */ >diff --git a/gcc/fortran/interface.c b/gcc/fortran/interface.c >index b012de5..bef514c 100644 >--- a/gcc/fortran/interface.c >+++ b/gcc/fortran/interface.c >@@ -1694,13 +1694,27 @@ check_interface0 (gfc_interface *p, const char >*interface_name) > || !p->sym->attr.if_source) > && !gfc_fl_struct (p->sym->attr.flavor)) > { >+ const char *guessed >+ = gfc_lookup_function_fuzzy (p->sym->name, p->sym->ns->sym_root); >+ > if (p->sym->attr.external) >- gfc_error ("Procedure %qs in %s at %L has no explicit interface", >- p->sym->name, interface_name, &p->sym->declared_at); >+ if (guessed) >+ gfc_error ("Procedure %qs in %s at %L has no explicit >interface" >+ "; did you mean %qs?", >+ p->sym->name, interface_name, &p->sym->declared_at, >+ guessed); >+ else >+ gfc_error ("Procedure %qs in %s at %L has no explicit >interface", >+ p->sym->name, interface_name, &p->sym->declared_at); > else >- gfc_error ("Procedure %qs in %s at %L is neither function nor " >- "subroutine", p->sym->name, interface_name, >- &p->sym->declared_at); >+ if (guessed) >+ gfc_error ("Procedure %qs in %s at %L is neither function nor " >+ "subroutine; did you mean %qs?", p->sym->name, >+ interface_name, &p->sym->declared_at, guessed); >+ else >+ gfc_error ("Procedure %qs in %s at %L is neither function nor " >+ "subroutine", p->sym->name, interface_name, >+ &p->sym->declared_at); > return 1; > } > >@@ -2684,6 +2698,31 @@ is_procptr_result (gfc_expr *expr) > } > > >+/* Recursively append candidate argument ARG to CANDIDATES. Store the >+ number of total candidates in CANDIDATES_LEN. */ >+ >+static void >+lookup_arg_fuzzy_find_candidates (gfc_formal_arglist *arg, >+ char **&candidates, >+ size_t &candidates_len) >+{ >+ for (gfc_formal_arglist *p = arg; p && p->sym; p = p->next) >+ vec_push (candidates, candidates_len, p->sym->name); >+} >+ >+ >+/* Lookup argument ARG fuzzily, taking names in ARGUMENTS into >account. */ >+ >+static const char* >+lookup_arg_fuzzy (const char *arg, gfc_formal_arglist *arguments) >+{ >+ char **candidates = NULL; >+ size_t candidates_len = 0; >+ lookup_arg_fuzzy_find_candidates (arguments, candidates, >candidates_len); >+ return gfc_closest_fuzzy_match (arg, candidates); >+} >+ >+ > /* Given formal and actual argument lists, see if they are compatible. > If they are compatible, the actual argument list is sorted to > correspond with the formal list, and elements for missing optional >@@ -2736,8 +2775,16 @@ compare_actual_formal (gfc_actual_arglist **ap, >gfc_formal_arglist *formal, > if (f == NULL) > { > if (where) >- gfc_error ("Keyword argument %qs at %L is not in " >- "the procedure", a->name, &a->expr->where); >+ { >+ const char *guessed = lookup_arg_fuzzy (a->name, formal); >+ if (guessed) >+ gfc_error ("Keyword argument %qs at %L is not in " >+ "the procedure; did you mean %qs?", >+ a->name, &a->expr->where, guessed); >+ else >+ gfc_error ("Keyword argument %qs at %L is not in " >+ "the procedure", a->name, &a->expr->where); >+ } > return 0; > } > >@@ -3436,8 +3483,15 @@ gfc_procedure_use (gfc_symbol *sym, >gfc_actual_arglist **ap, locus *where) > { >if (sym->ns->has_implicit_none_export && sym->attr.proc == >PROC_UNKNOWN) > { >- gfc_error ("Procedure %qs called at %L is not explicitly declared", >- sym->name, where); >+ const char *guessed >+ = gfc_lookup_function_fuzzy (sym->name, sym->ns->sym_root); >+ if (guessed) >+ gfc_error ("Procedure %qs called at %L is not explicitly >declared" >+ "; did you mean %qs?", >+ sym->name, where, guessed); >+ else >+ gfc_error ("Procedure %qs called at %L is not explicitly >declared", >+ sym->name, where); > return false; > } > if (warn_implicit_interface) >diff --git a/gcc/fortran/misc.c b/gcc/fortran/misc.c >index 1747ff2..dd17f46 100644 >--- a/gcc/fortran/misc.c >+++ b/gcc/fortran/misc.c >@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see > #include "system.h" > #include "coretypes.h" > #include "gfortran.h" >+#include "spellcheck.h" > > > /* Initialize a typespec to unknown. */ >@@ -280,3 +281,43 @@ get_c_kind(const char *c_kind_name, CInteropKind_t >kinds_table[]) > > return ISOCBINDING_INVALID; > } >+ >+ >+/* For a given name TYPO, determine the best candidate from CANDIDATES >+ perusing Levenshtein distance. Frees CANDIDATES before returning. >*/ >+ >+const char * >+gfc_closest_fuzzy_match (const char *typo, char **candidates) >+{ >+ /* Determine closest match. */ >+ const char *best = NULL; >+ char **cand = candidates; >+ edit_distance_t best_distance = MAX_EDIT_DISTANCE; >+ const size_t tl = strlen (typo); >+ >+ while (cand && *cand) >+ { >+ edit_distance_t dist = levenshtein_distance (typo, tl, *cand, >+ strlen (*cand)); >+ if (dist < best_distance) >+ { >+ best_distance = dist; >+ best = *cand; >+ } >+ cand++; >+ } >+ /* If more than half of the letters were misspelled, the suggestion >is >+ likely to be meaningless. */ >+ if (best) >+ { >+ unsigned int cutoff = MAX (tl, strlen (best)) / 2; >+ >+ if (best_distance > cutoff) >+ { >+ XDELETEVEC (candidates); >+ return NULL; >+ } >+ XDELETEVEC (candidates); >+ } >+ return best; >+} >diff --git a/gcc/fortran/resolve.c b/gcc/fortran/resolve.c >index 77f8c10..089afa3 100644 >--- a/gcc/fortran/resolve.c >+++ b/gcc/fortran/resolve.c >@@ -2693,6 +2693,43 @@ resolve_specific_f (gfc_expr *expr) > return true; > } > >+/* Recursively append candidate SYM to CANDIDATES. Store the number >of >+ candidates in CANDIDATES_LEN. */ >+ >+static void >+lookup_function_fuzzy_find_candidates (gfc_symtree *sym, >+ char **&candidates, >+ size_t &candidates_len) >+{ >+ gfc_symtree *p; >+ >+ if (sym == NULL) >+ return; >+ if ((sym->n.sym->ts.type != BT_UNKNOWN || sym->n.sym->attr.external) >+ && sym->n.sym->attr.flavor == FL_PROCEDURE) >+ vec_push (candidates, candidates_len, sym->name); >+ >+ p = sym->left; >+ if (p) >+ lookup_function_fuzzy_find_candidates (p, candidates, >candidates_len); >+ >+ p = sym->right; >+ if (p) >+ lookup_function_fuzzy_find_candidates (p, candidates, >candidates_len); >+} >+ >+ >+/* Lookup function FN fuzzily, taking names in SYMROOT into account. >*/ >+ >+const char* >+gfc_lookup_function_fuzzy (const char *fn, gfc_symtree *symroot) >+{ >+ char **candidates = NULL; >+ size_t candidates_len = 0; >+ lookup_function_fuzzy_find_candidates (symroot, candidates, >candidates_len); >+ return gfc_closest_fuzzy_match (fn, candidates); >+} >+ > > /* Resolve a procedure call not known to be generic nor specific. */ > >@@ -2743,8 +2780,15 @@ set_type: > > if (ts->type == BT_UNKNOWN) > { >- gfc_error ("Function %qs at %L has no IMPLICIT type", >- sym->name, &expr->where); >+ const char *guessed >+ = gfc_lookup_function_fuzzy (sym->name, sym->ns->sym_root); >+ if (guessed) >+ gfc_error ("Function %qs at %L has no IMPLICIT type" >+ "; did you mean %qs?", >+ sym->name, &expr->where, guessed); >+ else >+ gfc_error ("Function %qs at %L has no IMPLICIT type", >+ sym->name, &expr->where); > return false; > } > else >@@ -3516,6 +3560,46 @@ compare_shapes (gfc_expr *op1, gfc_expr *op2) > } > > >+/* Recursively append candidate UOP to CANDIDATES. Store the number >of >+ candidates in CANDIDATES_LEN. */ >+static void >+lookup_uop_fuzzy_find_candidates (gfc_symtree *uop, >+ char **&candidates, >+ size_t &candidates_len) >+{ >+ gfc_symtree *p; >+ >+ if (uop == NULL) >+ return; >+ >+ /* Not sure how to properly filter here. Use all for a start. >+ n.uop.op is NULL for empty interface operators (is that legal?) >disregard >+ these as i suppose they don't make terribly sense. */ >+ >+ if (uop->n.uop->op != NULL) >+ vec_push (candidates, candidates_len, uop->name); >+ >+ p = uop->left; >+ if (p) >+ lookup_uop_fuzzy_find_candidates (p, candidates, candidates_len); >+ >+ p = uop->right; >+ if (p) >+ lookup_uop_fuzzy_find_candidates (p, candidates, candidates_len); >+} >+ >+/* Lookup user-operator OP fuzzily, taking names in UOP into account. >*/ >+ >+static const char* >+lookup_uop_fuzzy (const char *op, gfc_symtree *uop) >+{ >+ char **candidates = NULL; >+ size_t candidates_len = 0; >+ lookup_uop_fuzzy_find_candidates (uop, candidates, candidates_len); >+ return gfc_closest_fuzzy_match (op, candidates); >+} >+ >+ >/* Resolve an operator expression node. This can involve replacing the > operation with a user defined function call. */ > >@@ -3714,8 +3798,16 @@ resolve_operator (gfc_expr *e) > > case INTRINSIC_USER: > if (e->value.op.uop->op == NULL) >- sprintf (msg, _("Unknown operator %%<%s%%> at %%L"), >- e->value.op.uop->name); >+ { >+ const char *name = e->value.op.uop->name; >+ const char *guessed; >+ guessed = lookup_uop_fuzzy (name, e->value.op.uop->ns->uop_root); >+ if (guessed) >+ sprintf (msg, _("Unknown operator %%<%s%%> at %%L; did you mean >'%s'?"), >+ name, guessed); >+ else >+ sprintf (msg, _("Unknown operator %%<%s%%> at %%L"), name); >+ } > else if (op2 == NULL) > sprintf (msg, _("Operand of user operator %%<%s%%> at %%L is %s"), > e->value.op.uop->name, gfc_typename (&op1->ts)); >diff --git a/gcc/fortran/symbol.c b/gcc/fortran/symbol.c >index 0ee7dec..776610c 100644 >--- a/gcc/fortran/symbol.c >+++ b/gcc/fortran/symbol.c >@@ -236,6 +236,44 @@ gfc_get_default_type (const char *name, >gfc_namespace *ns) > } > > >+/* Recursively append candidate SYM to CANDIDATES. Store the number >of >+ candidates in CANDIDATES_LEN. */ >+ >+static void >+lookup_symbol_fuzzy_find_candidates (gfc_symtree *sym, >+ char **&candidates, >+ size_t &candidates_len) >+{ >+ gfc_symtree *p; >+ >+ if (sym == NULL) >+ return; >+ >+ if (sym->n.sym->ts.type != BT_UNKNOWN && sym->n.sym->ts.type != >BT_PROCEDURE) >+ vec_push (candidates, candidates_len, sym->name); >+ p = sym->left; >+ if (p) >+ lookup_symbol_fuzzy_find_candidates (p, candidates, >candidates_len); >+ >+ p = sym->right; >+ if (p) >+ lookup_symbol_fuzzy_find_candidates (p, candidates, >candidates_len); >+} >+ >+ >+/* Lookup symbol SYM_NAME fuzzily, taking names in SYMBOL into >account. */ >+ >+static const char* >+lookup_symbol_fuzzy (const char *sym_name, gfc_symbol *symbol) >+{ >+ char **candidates = NULL; >+ size_t candidates_len = 0; >+ lookup_symbol_fuzzy_find_candidates (symbol->ns->sym_root, >candidates, >+ candidates_len); >+ return gfc_closest_fuzzy_match (sym_name, candidates); >+} >+ >+ > /* Given a pointer to a symbol, set its type according to the first > letter of its name. Fails if the letter in question has no default > type. */ >@@ -254,8 +292,14 @@ gfc_set_default_type (gfc_symbol *sym, int >error_flag, gfc_namespace *ns) > { > if (error_flag && !sym->attr.untyped) > { >- gfc_error ("Symbol %qs at %L has no IMPLICIT type", >- sym->name, &sym->declared_at); >+ const char *guessed = lookup_symbol_fuzzy (sym->name, sym); >+ if (guessed) >+ gfc_error ("Symbol %qs at %L has no IMPLICIT type" >+ "; did you mean %qs?", >+ sym->name, &sym->declared_at, guessed); >+ else >+ gfc_error ("Symbol %qs at %L has no IMPLICIT type", >+ sym->name, &sym->declared_at); > sym->attr.untyped = 1; /* Ensure we only give an error once. */ > } > >@@ -2233,6 +2277,32 @@ find_union_component (gfc_symbol *un, const char >*name, > } > > >+/* Recursively append candidate COMPONENT structures to CANDIDATES. >Store >+ the number of total candidates in CANDIDATES_LEN. */ >+ >+static void >+lookup_component_fuzzy_find_candidates (gfc_component *component, >+ char **&candidates, >+ size_t &candidates_len) >+{ >+ for (gfc_component *p = component; p; p = p->next) >+ vec_push (candidates, candidates_len, p->name); >+} >+ >+ >+/* Lookup component MEMBER fuzzily, taking names in COMPONENT into >account. */ >+ >+static const char* >+lookup_component_fuzzy (const char *member, gfc_component *component) >+{ >+ char **candidates = NULL; >+ size_t candidates_len = 0; >+ lookup_component_fuzzy_find_candidates (component, candidates, >+ candidates_len); >+ return gfc_closest_fuzzy_match (member, candidates); >+} >+ >+ > /* Given a derived type node and a component name, try to locate the > component structure. Returns the NULL pointer if the component is >not found or the components are private. If noaccess is set, no access >@@ -2330,8 +2400,16 @@ gfc_find_component (gfc_symbol *sym, const char >*name, > } > > if (p == NULL && !silent) >- gfc_error ("%qs at %C is not a member of the %qs structure", >- name, sym->name); >+ { >+ const char *guessed = lookup_component_fuzzy (name, >sym->components); >+ if (guessed) >+ gfc_error ("%qs at %C is not a member of the %qs structure" >+ "; did you mean %qs?", >+ name, sym->name, guessed); >+ else >+ gfc_error ("%qs at %C is not a member of the %qs structure", >+ name, sym->name); >+ } > > /* Component was found; build the ultimate component reference. */ > if (p != NULL && ref) >diff --git a/gcc/testsuite/gfortran.dg/spellcheck-operator.f90 >b/gcc/testsuite/gfortran.dg/spellcheck-operator.f90 >new file mode 100644 >index 0000000..810a770 >--- /dev/null >+++ b/gcc/testsuite/gfortran.dg/spellcheck-operator.f90 >@@ -0,0 +1,30 @@ >+! { dg-do compile } >+! test levenshtein based spelling suggestions >+ >+module mymod1 >+ implicit none >+ contains >+ function something_good (iarg1) >+ integer :: something_good >+ integer, intent(in) :: iarg1 >+ something_good = iarg1 + 42 >+ end function something_good >+end module mymod1 >+ >+program spellchekc >+ use mymod1 >+ implicit none >+ >+ interface operator (.mywrong.) >+ module procedure something_wring ! { dg-error "Procedure >.something_wring. in operator interface .mywrong. at .1. is neither >function nor subroutine; did you mean .something_good.\\?|User operator >procedure .something_wring. at .1. must be a FUNCTION" } >+ end interface >+ >+ interface operator (.mygood.) >+ module procedure something_good >+ end interface >+ >+ integer :: i, j, added >+ i = 0 >+ j = 0 >+ added = .mygoof. j ! { dg-error "Unknown operator .mygoof. at .1.; >did you mean .mygood.\\?" } >+end program spellchekc >diff --git a/gcc/testsuite/gfortran.dg/spellcheck-parameter.f90 >b/gcc/testsuite/gfortran.dg/spellcheck-parameter.f90 >new file mode 100644 >index 0000000..715c5ab >--- /dev/null >+++ b/gcc/testsuite/gfortran.dg/spellcheck-parameter.f90 >@@ -0,0 +1,15 @@ >+! { dg-do compile } >+! Contributed by Joost VandeVondele >+! test levenshtein based spelling suggestions for keyword arguments >+ >+module test >+contains >+ subroutine mysub(iarg1) >+ integer :: iarg1 >+ end subroutine >+end module >+ >+use test >+call mysub(iarg=1) ! { dg-error "Keyword argument .iarg. at .1. is not >in the procedure; did you mean .iarg1.\\?" } >+ >+end >diff --git a/gcc/testsuite/gfortran.dg/spellcheck-procedure_1.f90 >b/gcc/testsuite/gfortran.dg/spellcheck-procedure_1.f90 >new file mode 100644 >index 0000000..3b7f716 >--- /dev/null >+++ b/gcc/testsuite/gfortran.dg/spellcheck-procedure_1.f90 >@@ -0,0 +1,41 @@ >+! { dg-do compile } >+! test levenshtein based spelling suggestions >+ >+module mymod1 >+ implicit none >+ contains >+ function something_else (iarg1) >+ integer :: something_else >+ integer, intent(in) :: iarg1 >+ something_else = iarg1 + 42 >+ end function something_else >+ function add_fourtytwo (iarg1) >+ integer :: add_fourtytwo >+ integer, intent(in) :: iarg1 >+ add_fourtytwo = iarg1 + 42 >+ end function add_fourtytwo >+end module mymod1 >+ >+function myadd(iarg1, iarg2) >+ implicit none >+ integer :: myadd >+ integer, intent(in) :: iarg1, iarg2 >+ myadd = iarg1 + iarg2 >+end function myadd >+ >+program spellchekc >+ use mymod1, something_good => something_else >+ implicit none >+ >+ integer :: myadd, i, j, myvar >+ i = 0 >+ j = 0 >+ >+ j = something_goof(j) ! { dg-error "no IMPLICIT type; did you mean >.something_good.\\?" } >+ j = myaddd(i, j) ! { dg-error "no IMPLICIT type; did you mean >.myadd.\\?" } >+ if (j /= 42) call abort >+ j = add_fourtytow(i, j) ! { dg-error "no IMPLICIT type; did you mean >.add_fourtytwo.\\?" } >+ myval = myadd(i, j) ! { dg-error "no IMPLICIT type; did you mean >.myvar.\\?" } >+ if (j /= 42 * 2) call abort >+ >+end program spellchekc >diff --git a/gcc/testsuite/gfortran.dg/spellcheck-procedure_2.f90 >b/gcc/testsuite/gfortran.dg/spellcheck-procedure_2.f90 >new file mode 100644 >index 0000000..a6ea5f9 >--- /dev/null >+++ b/gcc/testsuite/gfortran.dg/spellcheck-procedure_2.f90 >@@ -0,0 +1,35 @@ >+! { dg-do compile } >+! test levenshtein based spelling suggestions >+ >+ >+program spellchekc >+ implicit none (external) ! { dg-warning "GNU Extension: IMPORT NONE >with spec list" } >+ >+ interface >+ subroutine bark_unless_zero(iarg) >+ implicit none >+ integer, intent(in) :: iarg >+ end subroutine bark_unless_zero >+ end interface >+ >+ integer :: i >+ i = 0 >+ >+ if (i /= 1) call abort >+ call bark_unless_0(i) ! { dg-error "not explicitly declared; did you >mean .bark_unless_zero.\\?" } >+! call complain_about_0(i) ! { -dg-error "not explicitly declared; >did you mean .complain_about_zero.\\?" } >+ >+contains >+! We cannot reliably see this ATM, would need an unambiguous bit >somewhere >+ subroutine complain_about_zero(iarg) >+ integer, intent(in) :: iarg >+ if (iarg /= 0) call abort >+ end subroutine complain_about_zero >+ >+end program spellchekc >+ >+subroutine bark_unless_zero(iarg) >+ implicit none >+ integer, intent(in) :: iarg >+ if (iarg /= 0) call abort >+end subroutine bark_unless_zero >diff --git a/gcc/testsuite/gfortran.dg/spellcheck-structure.f90 >b/gcc/testsuite/gfortran.dg/spellcheck-structure.f90 >new file mode 100644 >index 0000000..929e05f >--- /dev/null >+++ b/gcc/testsuite/gfortran.dg/spellcheck-structure.f90 >@@ -0,0 +1,35 @@ >+! { dg-do compile } >+! test levenshtein based spelling suggestions >+implicit none >+ >+!!!!!!!!!!!!!! structure tests !!!!!!!!!!!!!! >+type type1 >+ real :: radius >+ integer :: i >+end type type1 >+ >+type type2 >+ integer :: myint >+ type(type1) :: mytype >+end type type2 >+ >+type type3 >+ type(type2) :: type_2 >+end type type3 >+type type4 >+ type(type3) :: type_3 >+end type type4 >+ >+type(type1) :: t1 >+t1%radiuz = .0 ! { dg-error ".radiuz. at .1. is not a member of the >.type1. structure; did you mean .radius.\\?" } >+t1%x = .0 ! { dg-error ".x. at .1. is not a member of the .type1. >structure" } >+type(type2) :: t2 >+t2%mytape%radius = .0 ! { dg-error ".mytape. at .1. is not a member of >the .type2. structure; did you mean .mytype.\\?" } >+t2%mytype%radious = .0 ! { dg-error ".radious. at .1. is not a member >of the .type1. structure; did you mean .radius.\\?" } >+type(type4) :: t4 >+t4%type_3%type_2%mytype%radium = 88.0 ! { dg-error ".radium. at .1. is >not a member of the .type1. structure; did you mean .radius.\\?" } >+ >+!!!!!!!!!!!!!! symbol tests !!!!!!!!!!!!!! >+integer :: iarg1 >+iarg2 = 1 ! { dg-error "Symbol .iarg2. at .1. has no IMPLICIT type; >did you mean .iarg1.\\?" } >+end