From patchwork Wed Nov 10 18:13:57 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathan Froyd X-Patchwork-Id: 70673 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 45C34B70EF for ; Thu, 11 Nov 2010 05:14:19 +1100 (EST) Received: (qmail 2475 invoked by alias); 10 Nov 2010 18:14:15 -0000 Received: (qmail 2465 invoked by uid 22791); 10 Nov 2010 18:14:12 -0000 X-SWARE-Spam-Status: No, hits=-1.8 required=5.0 tests=AWL, BAYES_00, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 10 Nov 2010 18:14:03 +0000 Received: (qmail 23896 invoked from network); 10 Nov 2010 18:14:00 -0000 Received: from unknown (HELO codesourcery.com) (froydnj@127.0.0.2) by mail.codesourcery.com with ESMTPA; 10 Nov 2010 18:14:00 -0000 Date: Wed, 10 Nov 2010 13:13:57 -0500 From: Nathan Froyd To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com Subject: [PATCH, c++] fix PR 45330, suggest alternatives for failed name lookups Message-ID: <20101110181356.GM7991@nightcrawler> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org The testcase in PR 45330 is: namespace N { int foo; } int bar() { return foo; } and the perceived problem is that GCC should suggest `N::foo' for the unqualified `foo' in `bar'. The below patch does just that: when name lookup fails, we grovel through all known namespaces for potential matches and suggest them to the user. I put the declaration for the function in cp-tree.h rather than name-lookup.h because it seemed like a better place than including name-lookup.h in lex.c. I'm happy to move the declaration to name-lookup.h and add the include. (Or, more radically, move unqualified_name_lookup_error and unqualified_fn_lookup_error to name-lookup.c and adjust includes appropriately.) The error.c change is to avoid dump_expr complaining (as occurs in g++.dg/lookup/koenig5.C). I didn't include a testcase, as the testcase tweaks indicated there are a number of testcases testing similar things. (g++.old-deja/g++.mike/ns5.C is almost identical to the testcase in the PR, for instance). Tested on x86_64-unknown-linux-gnu. OK to commit? -Nathan gcc/cp/ PR c++/45330 * cp-tree.h (suggest_alternatives_for): Declare. * error.c (dump_expr): Handle TYPE_DECL. * lex.c (unqualified_name_lookup_error): Call it. * name-lookup.c (suggest_alternatives_for_1): New function. (suggest_alternatives_for): New function. gcc/testsuite/ PR c++/45330 * g++.dg/ext/builtin3.C: Adjust. * g++.dg/lookup/error1.C: Adjust. * g++.dg/lookup/koenig5.C: Adjust. * g++.dg/overload/koenig1.C: Adjust. * g++.dg/parse/decl-specifier-1.C: Adjust. * g++.dg/template/static10.C: Adjust. * g++.old-deja/g++.mike/ns5.C: Adjust. * g++.old-deja/g++.mike/ns7.C: Adjust. * g++.old-deja/g++.ns/koenig5.C: Adjust. * g++.old-deja/g++.ns/koenig9.C: Adjust. * g++.old-deja/g++.other/lineno5.C: Adjust. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 67f4f93..a323501 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5639,6 +5639,9 @@ extern tree cxx_omp_clause_dtor (tree, tree); extern void cxx_omp_finish_clause (tree); extern bool cxx_omp_privatize_by_reference (const_tree); +/* in name-lookup.c */ +extern void suggest_alternatives_for (tree); + /* -- end of C++ */ #endif /* ! GCC_CP_TREE_H */ diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c index c583d7d..9c6be41 100644 --- a/gcc/cp/lex.c +++ b/gcc/cp/lex.c @@ -450,7 +450,10 @@ unqualified_name_lookup_error (tree name) else { if (!objc_diagnose_private_ivar (name)) - error ("%qD was not declared in this scope", name); + { + error ("%qD was not declared in this scope", name); + suggest_alternatives_for (name); + } /* Prevent repeated error messages by creating a VAR_DECL with this NAME in the innermost block scope. */ if (current_function_decl) diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 1560fc6..de45efe 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -1700,6 +1700,7 @@ dump_expr (tree t, int flags) case NAMESPACE_DECL: case LABEL_DECL: case OVERLOAD: + case TYPE_DECL: case IDENTIFIER_NODE: dump_decl (t, (flags & ~TFF_DECL_SPECIFIERS) | TFF_NO_FUNCTION_ARGUMENTS); break; diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 0a93da8..1f2f4e5 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "timevar.h" #include "toplev.h" #include "diagnostic-core.h" +#include "intl.h" #include "debug.h" #include "c-family/c-pragma.h" @@ -3919,6 +3920,62 @@ remove_hidden_names (tree fns) return fns; } +/* Helper function for suggest_alternatives_for: lookup NAME in + namespace SCOPE and record any matches in CANDIDATES. */ + +static VEC(tree,heap) * +suggest_alternatives_for_1 (tree name, tree scope, + VEC(tree,heap) *candidates) +{ + struct scope_binding binding = EMPTY_SCOPE_BINDING; + struct cp_binding_level *level = NAMESPACE_LEVEL (scope); + tree t; + + /* Look in this namespace. */ + qualified_lookup_using_namespace (name, scope, &binding, 0); + + if (binding.value) + VEC_safe_push (tree, heap, candidates, binding.value); + + /* Lookup in child namespaces. */ + for (t = level->namespaces; t; t = DECL_CHAIN (t)) + candidates = suggest_alternatives_for_1 (name, t, candidates); + + return candidates; +} + +/* Suggest alternatives for NAME, an IDENTIFIER_NODE for which name + lookup failed. Search through all available namespaces and print out + possible candidates. */ + +void +suggest_alternatives_for (tree name) +{ + VEC(tree,heap) *candidates + = suggest_alternatives_for_1 (name, global_namespace, NULL); + const char *str; + char *spaces; + tree t; + unsigned ix; + + /* Nothing useful to report. */ + if (VEC_empty (tree, candidates)) + return; + + str = (VEC_length(tree, candidates) > 1 + ? _("suggested alternatives:") + : _("suggested alternative:")); + spaces = NULL; + + FOR_EACH_VEC_ELT (tree, candidates, ix, t) + { + inform (input_location, "%s %qE", (spaces ? spaces : str), t); + spaces = spaces ? spaces : get_spaces (str); + } + + VEC_free (tree, heap, candidates); +} + /* Unscoped lookup of a global: iterate over current namespaces, considering using-directives. */ diff --git a/gcc/testsuite/g++.dg/ext/builtin3.C b/gcc/testsuite/g++.dg/ext/builtin3.C index 3d06dd7..a9ebce0 100644 --- a/gcc/testsuite/g++.dg/ext/builtin3.C +++ b/gcc/testsuite/g++.dg/ext/builtin3.C @@ -10,4 +10,5 @@ extern "C" int printf(char*, ...); void foo() { printf("abc"); // { dg-error "not declared" } + // { dg-message "note" "suggested alternative" { target *-*-* } 12 } } diff --git a/gcc/testsuite/g++.dg/lookup/error1.C b/gcc/testsuite/g++.dg/lookup/error1.C index 2264b23..3b34ee3 100644 --- a/gcc/testsuite/g++.dg/lookup/error1.C +++ b/gcc/testsuite/g++.dg/lookup/error1.C @@ -4,6 +4,7 @@ namespace N { int i; } void foo() { i; } // { dg-error "not declared" } + // { dg-message "note" "suggested alternative" { target *-*-* } 6 } using namespace N; void bar() { i; } diff --git a/gcc/testsuite/g++.dg/lookup/koenig5.C b/gcc/testsuite/g++.dg/lookup/koenig5.C index 6ecc25d..bc1dc8c 100644 --- a/gcc/testsuite/g++.dg/lookup/koenig5.C +++ b/gcc/testsuite/g++.dg/lookup/koenig5.C @@ -32,10 +32,12 @@ void g (N::A *a, M::B *b, O::C *c) One (a); // ok One (a, b); // ok One (b); // { dg-error "not declared" } + // { dg-message "note" "suggested alternative" { target *-*-* } 34 } Two (c); // ok Two (a, c); // ok Two (a); // { dg-error "not declared" } + // { dg-message "note" "suggested alternative" { target *-*-* } 39 } Two (a, a); // error masked by earlier error Two (b); // error masked by earlier error Two (a, b); // error masked by earlier error @@ -43,4 +45,5 @@ void g (N::A *a, M::B *b, O::C *c) Three (b); // ok Three (a, b); // ok Three (a); // { dg-error "not declared" } + // { dg-message "note" "suggested alternative" { target *-*-* } 47 } } diff --git a/gcc/testsuite/g++.dg/overload/koenig1.C b/gcc/testsuite/g++.dg/overload/koenig1.C index 1ed7bce..be0be69 100644 --- a/gcc/testsuite/g++.dg/overload/koenig1.C +++ b/gcc/testsuite/g++.dg/overload/koenig1.C @@ -14,5 +14,6 @@ void g () B *bp; N::A *ap; f (bp); // { dg-error "not declared" } + // { dg-message "note" "suggested alternative" { target *-*-* } 16 } f (ap); } diff --git a/gcc/testsuite/g++.dg/parse/decl-specifier-1.C b/gcc/testsuite/g++.dg/parse/decl-specifier-1.C index e81fbab..48fc7fa 100644 --- a/gcc/testsuite/g++.dg/parse/decl-specifier-1.C +++ b/gcc/testsuite/g++.dg/parse/decl-specifier-1.C @@ -13,4 +13,5 @@ N::X X; // { dg-error "" "" } int main() { return sizeof(X); // { dg-error "" "" } + // { dg-message "note" "suggested alternative" { target *-*-* } 15 } } diff --git a/gcc/testsuite/g++.dg/template/static10.C b/gcc/testsuite/g++.dg/template/static10.C index ab857bd..2f60f0c 100644 --- a/gcc/testsuite/g++.dg/template/static10.C +++ b/gcc/testsuite/g++.dg/template/static10.C @@ -20,4 +20,5 @@ namespace std { template<> void vector >::swap(vector >&) { } // { dg-error "" } + // { dg-message "note" "suggested alternative" { target *-*-* } 22 } } diff --git a/gcc/testsuite/g++.old-deja/g++.mike/ns5.C b/gcc/testsuite/g++.old-deja/g++.mike/ns5.C index 9d806ca..f13da04 100644 --- a/gcc/testsuite/g++.old-deja/g++.mike/ns5.C +++ b/gcc/testsuite/g++.old-deja/g++.mike/ns5.C @@ -4,3 +4,4 @@ namespace A { } int j = i; // { dg-error "" } + // { dg-message "note" "suggested alternative" { target *-*-* } 6 } diff --git a/gcc/testsuite/g++.old-deja/g++.mike/ns7.C b/gcc/testsuite/g++.old-deja/g++.mike/ns7.C index 57008db..31a71dd 100644 --- a/gcc/testsuite/g++.old-deja/g++.mike/ns7.C +++ b/gcc/testsuite/g++.old-deja/g++.mike/ns7.C @@ -6,4 +6,5 @@ namespace A { namespace B { int j = i; // { dg-error "" } + // { dg-message "note" "suggested alternative" { target *-*-* } 8 } } diff --git a/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C b/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C index 33061ad..67b781d 100644 --- a/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C +++ b/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C @@ -15,4 +15,5 @@ void g() // foo variable first, and therefore do not // perform argument-dependent lookup. bar(new X); // { dg-error "not declared" } + // { dg-message "note" "suggested alternative" { target *-*-* } 17 } } diff --git a/gcc/testsuite/g++.old-deja/g++.ns/koenig9.C b/gcc/testsuite/g++.old-deja/g++.ns/koenig9.C index 78b0e8b..f246639 100644 --- a/gcc/testsuite/g++.old-deja/g++.ns/koenig9.C +++ b/gcc/testsuite/g++.old-deja/g++.ns/koenig9.C @@ -10,4 +10,5 @@ void foo(const char*,...); inline void bar() { foo("",count); // { dg-error "" } multiple overloaded count functions + // { dg-message "note" "suggested alternative" { target *-*-* } 12 } } diff --git a/gcc/testsuite/g++.old-deja/g++.other/lineno5.C b/gcc/testsuite/g++.old-deja/g++.other/lineno5.C index d14bd90..20e49bc 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/lineno5.C +++ b/gcc/testsuite/g++.old-deja/g++.other/lineno5.C @@ -16,4 +16,5 @@ namespace tmp { class A { public: int kaka(tmp::B = b); // { dg-error "" } no b in scope + // { dg-message "note" "suggested alternative" { target *-*-* } 18 } };