OK.
On Fri, May 5, 2017 at 6:50 PM, David Malcolm <dmalc...@redhat.com> wrote: > On Mon, 2017-05-01 at 14:43 -0400, Jason Merrill wrote: >> On Thu, Apr 27, 2017 at 7:23 AM, Nathan Sidwell <nat...@acm.org> >> wrote: >> > On 04/26/2017 12:34 PM, David Malcolm wrote: >> > >> > > Thanks - yes; that gives information on the const vs non-const of >> > > the >> > > "this" parameter, but doesn't say whether the argument was const >> > > vs non >> > > -const. >> > >> > >> > > However, within: >> > > >> > > int test_const_ptr (const t1 *ptr) >> > > { >> > > return ptr->m_color; >> > > } >> > > from which we can see the const-ness of the t1: >> > >> > >> > correct. >> > >> > > but the call to lookup_member from within >> > > finish_class_member_access_expr discards this information, giving >> > > just >> > > "access_path": a BINFO that wraps the RECORD_TYPE for t1 >> > > directly. >> > >> > >> > Correct. >> > >> > lookup_member just looks for a matching name. the BINFO represents >> > the >> > class hierarchy - it's not modified depending on the cvquals of >> > where you >> > came from. >> > >> > > A somewhat invasive solution would be for lookup_member to grow >> > > an extra: >> > > tree object >> > > parameter, and to pass this information down through the access >> > > -enforcement code, so that locate_field_accessor can look at the >> > > const >> > > -ness of the lookup, and avoid suggesting const methods when the >> > > object >> > > is const. The code would probably need to support the new param >> > > being >> > > NULL_TREE for cases where we're looking up a static member. Or >> > > maybe >> > > an enum of access style for const vs non-const vs static. >> > > Maybe name the param "access_hint" to signify that it's merely >> > > there >> > > for the purpose of hints for the user, and not to affect the >> > > parsing >> > > itself? >> > >> > Hm, that does seem rather unfortunate. >> > > >> > > Another solution would be to not bother offering non-const >> > > methods as >> > > accessors. >> > >> > >> > I think that would be very unfortunate. >> > >> > How about adding a tsubst_flag value? >> > >> > tf_const_obj = 1 << 11, /* For alternative accessor suggestion >> > help. */ >> > >> > and pass that in? the tsubst flags have grown in meaning somewhat >> > since >> > they first appeared -- their name is no longer so appropriate. >> > >> > (of course we have the same problem with volatile, but that's >> > probably >> > overkill for first attempt.) >> > >> > Jason, WDYT? >> >> I'd suggest handling this diagnostic in >> finish_class_member_access_expr, rather than try to push down context >> information into lookup_member. Perhaps by adding another parameter >> to lookup_member for passing back the inaccessible or ambiguous >> lookup >> result? >> >> Jason > > Thanks. > > Here's an updated version of the patch which adds an optional struct > ptr, for writing back the info, which then gets emitted (if set) > in finish_class_member_access_expr (and thus has access to the constness > of the object). > > Successfully bootstrapped®rtested on x86_64-pc-linux-gnu. > > OK for trunk? > > gcc/cp/ChangeLog: > * call.c (enforce_access): Add access_failure_info * param and use > it to record access failures. > * cp-tree.h (class access_failure_info): New class. > (enforce_access): Add access_failure_info * param, defaulting to > NULL. > (lookup_member): Likewise. > (locate_field_accessor): New function decl. > (perform_or_defer_access_check): Add access_failure_info * param, > defaulting to NULL. > * search.c (lookup_member): Add access_failure_info * param and > pass it on to call to perform_or_defer_access_check. > (matches_code_and_type_p): New function. > (field_access_p): New function. > (direct_accessor_p): New function. > (reference_accessor_p): New function. > (field_accessor_p): New function. > (struct locate_field_data): New struct. > (dfs_locate_field_accessor_pre): New function. > (locate_field_accessor): New function. > * semantics.c (perform_or_defer_access_check): Add > access_failure_info * param, and pass it on to call to > enforce_access. > * typeck.c (access_failure_info::record_access_failure): New method. > (access_failure_info::maybe_suggest_accessor): New method. > (finish_class_member_access_expr): Pass an access_failure_info > instance to the lookup_member call, and call its > maybe_suggest_accessor method afterwards. > > gcc/testsuite/ChangeLog: > * g++.dg/other/accessor-fixits-1.C: New test case. > * g++.dg/other/accessor-fixits-2.C: New test case. > * g++.dg/other/accessor-fixits-3.C: New test case. > * g++.dg/other/accessor-fixits-4.C: New test case. > --- > gcc/cp/call.c | 8 +- > gcc/cp/cp-tree.h | 31 +++- > gcc/cp/search.c | 240 > ++++++++++++++++++++++++- > gcc/cp/semantics.c | 8 +- > gcc/cp/typeck.c | 45 ++++- > gcc/testsuite/g++.dg/other/accessor-fixits-1.C | 178 ++++++++++++++++++ > gcc/testsuite/g++.dg/other/accessor-fixits-2.C | 104 +++++++++++ > gcc/testsuite/g++.dg/other/accessor-fixits-3.C | 15 ++ > gcc/testsuite/g++.dg/other/accessor-fixits-4.C | 48 +++++ > 9 files changed, 666 insertions(+), 11 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/other/accessor-fixits-1.C > create mode 100644 gcc/testsuite/g++.dg/other/accessor-fixits-2.C > create mode 100644 gcc/testsuite/g++.dg/other/accessor-fixits-3.C > create mode 100644 gcc/testsuite/g++.dg/other/accessor-fixits-4.C > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index c15b8e4..9ed4ad0 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -6415,7 +6415,7 @@ build_op_delete_call (enum tree_code code, tree addr, > tree size, > > bool > enforce_access (tree basetype_path, tree decl, tree diag_decl, > - tsubst_flags_t complain) > + tsubst_flags_t complain, access_failure_info *afi) > { > gcc_assert (TREE_CODE (basetype_path) == TREE_BINFO); > > @@ -6441,17 +6441,23 @@ enforce_access (tree basetype_path, tree decl, tree > diag_decl, > error ("%q#D is private within this context", diag_decl); > inform (DECL_SOURCE_LOCATION (diag_decl), > "declared private here"); > + if (afi) > + afi->record_access_failure (basetype_path, diag_decl); > } > else if (TREE_PROTECTED (decl)) > { > error ("%q#D is protected within this context", diag_decl); > inform (DECL_SOURCE_LOCATION (diag_decl), > "declared protected here"); > + if (afi) > + afi->record_access_failure (basetype_path, diag_decl); > } > else > { > error ("%q#D is inaccessible within this context", diag_decl); > inform (DECL_SOURCE_LOCATION (diag_decl), "declared here"); > + if (afi) > + afi->record_access_failure (basetype_path, diag_decl); > } > } > return false; > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 8721ed4..c2b9df8 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -5659,8 +5659,30 @@ extern bool can_convert_arg > (tree, tree, tree, int, > tsubst_flags_t); > extern bool can_convert_arg_bad (tree, tree, tree, > int, > tsubst_flags_t); > + > +/* A class for recording information about access failures (e.g. private > + fields), so that we can potentially supply a fix-it hint about > + an accessor (from a context in which the constness of the object > + is known). */ > + > +class access_failure_info > +{ > + public: > + access_failure_info () : m_was_inaccessible (false), m_basetype_path > (NULL_TREE), > + m_field_decl (NULL_TREE) {} > + > + void record_access_failure (tree basetype_path, tree field_decl); > + void maybe_suggest_accessor (bool const_p) const; > + > + private: > + bool m_was_inaccessible; > + tree m_basetype_path; > + tree m_field_decl; > +}; > + > extern bool enforce_access (tree, tree, tree, > - tsubst_flags_t); > + tsubst_flags_t, > + access_failure_info *afi = > NULL); > extern void push_defarg_context (tree); > extern void pop_defarg_context (void); > extern tree convert_default_arg (tree, tree, tree, > int, > @@ -6322,8 +6344,10 @@ extern tree lookup_fnfields_slot_nolazy > (tree, tree); > extern int class_method_index_for_fn (tree, tree); > extern tree lookup_fnfields (tree, tree, int); > extern tree lookup_member (tree, tree, int, bool, > - tsubst_flags_t); > + tsubst_flags_t, > + access_failure_info *afi = > NULL); > extern tree lookup_member_fuzzy (tree, tree, bool); > +extern tree locate_field_accessor (tree, tree, bool); > extern int look_for_overrides (tree, tree); > extern void get_pure_virtuals (tree); > extern void maybe_suppress_debug_info (tree); > @@ -6378,7 +6402,8 @@ extern bool perform_access_checks > (vec<deferred_access_check, va_gc> *, > tsubst_flags_t); > extern bool perform_deferred_access_checks (tsubst_flags_t); > extern bool perform_or_defer_access_check (tree, tree, tree, > - tsubst_flags_t); > + tsubst_flags_t, > + access_failure_info *afi = > NULL); > > /* RAII sentinel to ensures that deferred access checks are popped before > a function returns. */ > diff --git a/gcc/cp/search.c b/gcc/cp/search.c > index 09c1b4e..2cd6ea5 100644 > --- a/gcc/cp/search.c > +++ b/gcc/cp/search.c > @@ -1232,11 +1232,13 @@ build_baselink (tree binfo, tree access_binfo, tree > functions, tree optype) > > WANT_TYPE is 1 when we should only return TYPE_DECLs. > > - If nothing can be found return NULL_TREE and do not issue an error. */ > + If nothing can be found return NULL_TREE and do not issue an error. > + > + If non-NULL, failure information is written back to AFI. */ > > tree > lookup_member (tree xbasetype, tree name, int protect, bool want_type, > - tsubst_flags_t complain) > + tsubst_flags_t complain, access_failure_info *afi) > { > tree rval, rval_binfo = NULL_TREE; > tree type = NULL_TREE, basetype_path = NULL_TREE; > @@ -1337,7 +1339,7 @@ lookup_member (tree xbasetype, tree name, int protect, > bool want_type, > tree decl = is_overloaded_fn (rval) ? get_first_fn (rval) : rval; > if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (decl) > && !perform_or_defer_access_check (basetype_path, decl, decl, > - complain)) > + complain, afi)) > rval = error_mark_node; > } > > @@ -1993,6 +1995,238 @@ dfs_walk_once_accessible (tree binfo, bool friends_p, > return rval; > } > > +/* Return true iff the code of T is CODE, and it has compatible > + type with TYPE. */ > + > +static bool > +matches_code_and_type_p (tree t, enum tree_code code, tree type) > +{ > + if (TREE_CODE (t) != code) > + return false; > + if (!cxx_types_compatible_p (TREE_TYPE (t), type)) > + return false; > + return true; > +} > + > +/* Subroutine of direct_accessor_p and reference_accessor_p. > + Determine if COMPONENT_REF is a simple field lookup of this->FIELD_DECL. > + We expect a tree of the form: > + <component_ref: > + <indirect_ref:S> > + <nop_expr:P* > + <parm_decl (this)> > + <field_decl (FIELD_DECL)>>>. */ > + > +static bool > +field_access_p (tree component_ref, tree field_decl, tree field_type) > +{ > + if (!matches_code_and_type_p (component_ref, COMPONENT_REF, field_type)) > + return false; > + > + tree indirect_ref = TREE_OPERAND (component_ref, 0); > + if (TREE_CODE (indirect_ref) != INDIRECT_REF) > + return false; > + > + tree ptr = STRIP_NOPS (TREE_OPERAND (indirect_ref, 0)); > + if (!is_this_parameter (ptr)) > + return false; > + > + /* Must access the correct field. */ > + if (TREE_OPERAND (component_ref, 1) != field_decl) > + return false; > + return true; > +} > + > +/* Subroutine of field_accessor_p. > + > + Assuming that INIT_EXPR has already had its code and type checked, > + determine if it is a simple accessor for FIELD_DECL > + (of type FIELD_TYPE). > + > + Specifically, a simple accessor within struct S of the form: > + T get_field () { return m_field; } > + should have a DECL_SAVED_TREE of the form: > + <return_expr > + <init_expr:T > + <result_decl:T > + <nop_expr:T > + <component_ref: > + <indirect_ref:S> > + <nop_expr:P* > + <parm_decl (this)> > + <field_decl (FIELD_DECL)>>>. */ > + > +static bool > +direct_accessor_p (tree init_expr, tree field_decl, tree field_type) > +{ > + tree result_decl = TREE_OPERAND (init_expr, 0); > + if (!matches_code_and_type_p (result_decl, RESULT_DECL, field_type)) > + return false; > + > + tree component_ref = STRIP_NOPS (TREE_OPERAND (init_expr, 1)); > + if (!field_access_p (component_ref, field_decl, field_type)) > + return false; > + > + return true; > +} > + > +/* Subroutine of field_accessor_p. > + > + Assuming that INIT_EXPR has already had its code and type checked, > + determine if it is a "reference" accessor for FIELD_DECL > + (of type FIELD_REFERENCE_TYPE). > + > + Specifically, a simple accessor within struct S of the form: > + T& get_field () { return m_field; } > + should have a DECL_SAVED_TREE of the form: > + <return_expr > + <init_expr:T& > + <result_decl:T& > + <nop_expr: T& > + <addr_expr: T* > + <component_ref:T > + <indirect_ref:S > + <nop_expr > + <parm_decl (this)>> > + <field (FIELD_DECL)>>>>>>. */ > +static bool > +reference_accessor_p (tree init_expr, tree field_decl, tree field_type, > + tree field_reference_type) > +{ > + tree result_decl = TREE_OPERAND (init_expr, 0); > + if (!matches_code_and_type_p (result_decl, RESULT_DECL, > field_reference_type)) > + return false; > + > + tree field_pointer_type = build_pointer_type (field_type); > + tree addr_expr = STRIP_NOPS (TREE_OPERAND (init_expr, 1)); > + if (!matches_code_and_type_p (addr_expr, ADDR_EXPR, field_pointer_type)) > + return false; > + > + tree component_ref = STRIP_NOPS (TREE_OPERAND (addr_expr, 0)); > + > + if (!field_access_p (component_ref, field_decl, field_type)) > + return false; > + > + return true; > +} > + > +/* Return true if FN is an accessor method for FIELD_DECL. > + i.e. a method of the form { return FIELD; }, with no > + conversions. > + > + If CONST_P, then additionally require that FN be a const > + method. */ > + > +static bool > +field_accessor_p (tree fn, tree field_decl, bool const_p) > +{ > + if (TREE_CODE (fn) != FUNCTION_DECL) > + return false; > + > + /* We don't yet support looking up static data, just fields. */ > + if (TREE_CODE (field_decl) != FIELD_DECL) > + return false; > + > + tree fntype = TREE_TYPE (fn); > + if (TREE_CODE (fntype) != METHOD_TYPE) > + return false; > + > + /* If the field is accessed via a const "this" argument, verify > + that the "this" parameter is const. */ > + if (const_p) > + { > + tree this_type = type_of_this_parm (fntype); > + if (!TYPE_READONLY (this_type)) > + return false; > + } > + > + tree saved_tree = DECL_SAVED_TREE (fn); > + > + if (saved_tree == NULL_TREE) > + return false; > + > + if (TREE_CODE (saved_tree) != RETURN_EXPR) > + return false; > + > + tree init_expr = TREE_OPERAND (saved_tree, 0); > + if (TREE_CODE (init_expr) != INIT_EXPR) > + return false; > + > + /* Determine if this is a simple accessor within struct S of the form: > + T get_field () { return m_field; }. */ > + tree field_type = TREE_TYPE (field_decl); > + if (cxx_types_compatible_p (TREE_TYPE (init_expr), field_type)) > + return direct_accessor_p (init_expr, field_decl, field_type); > + > + /* Failing that, determine if it is an accessor of the form: > + T& get_field () { return m_field; }. */ > + tree field_reference_type = cp_build_reference_type (field_type, false); > + if (cxx_types_compatible_p (TREE_TYPE (init_expr), field_reference_type)) > + return reference_accessor_p (init_expr, field_decl, field_type, > + field_reference_type); > + > + return false; > +} > + > +/* Callback data for dfs_locate_field_accessor_pre. */ > + > +struct locate_field_data > +{ > + locate_field_data (tree field_decl_, bool const_p_) > + : field_decl (field_decl_), const_p (const_p_) {} > + > + tree field_decl; > + bool const_p; > +}; > + > +/* Return a FUNCTION_DECL that is an "accessor" method for DATA, a > FIELD_DECL, > + callable via binfo, if one exists, otherwise return NULL_TREE. > + > + Callback for dfs_walk_once_accessible for use within > + locate_field_accessor. */ > + > +static tree > +dfs_locate_field_accessor_pre (tree binfo, void *data) > +{ > + locate_field_data *lfd = (locate_field_data *)data; > + tree type = BINFO_TYPE (binfo); > + > + vec<tree, va_gc> *method_vec; > + tree fn; > + size_t i; > + > + if (!CLASS_TYPE_P (type)) > + return NULL_TREE; > + > + method_vec = CLASSTYPE_METHOD_VEC (type); > + if (!method_vec) > + return NULL_TREE; > + > + for (i = 0; vec_safe_iterate (method_vec, i, &fn); ++i) > + if (fn) > + if (field_accessor_p (fn, lfd->field_decl, lfd->const_p)) > + return fn; > + > + return NULL_TREE; > +} > + > +/* Return a FUNCTION_DECL that is an "accessor" method for FIELD_DECL, > + callable via BASETYPE_PATH, if one exists, otherwise return NULL_TREE. */ > + > +tree > +locate_field_accessor (tree basetype_path, tree field_decl, bool const_p) > +{ > + if (TREE_CODE (basetype_path) != TREE_BINFO) > + return NULL_TREE; > + > + /* Walk the hierarchy, looking for a method of some base class that allows > + access to the field. */ > + locate_field_data lfd (field_decl, const_p); > + return dfs_walk_once_accessible (basetype_path, /*friends=*/true, > + dfs_locate_field_accessor_pre, > + NULL, &lfd); > +} > + > /* Check that virtual overrider OVERRIDER is acceptable for base function > BASEFN. Issue diagnostic, and return zero, if unacceptable. */ > > diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c > index 4db2462..e6c6bd9 100644 > --- a/gcc/cp/semantics.c > +++ b/gcc/cp/semantics.c > @@ -305,11 +305,13 @@ perform_deferred_access_checks (tsubst_flags_t complain) > > /* Defer checking the accessibility of DECL, when looked up in > BINFO. DIAG_DECL is the declaration to use to print diagnostics. > - Return value like perform_access_checks above. */ > + Return value like perform_access_checks above. > + If non-NULL, report failures to AFI. */ > > bool > perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl, > - tsubst_flags_t complain) > + tsubst_flags_t complain, > + access_failure_info *afi) > { > int i; > deferred_access *ptr; > @@ -328,7 +330,7 @@ perform_or_defer_access_check (tree binfo, tree decl, > tree diag_decl, > /* If we are not supposed to defer access checks, just check now. */ > if (ptr->deferring_access_checks_kind == dk_no_deferred) > { > - bool ok = enforce_access (binfo, decl, diag_decl, complain); > + bool ok = enforce_access (binfo, decl, diag_decl, complain, afi); > return (complain & tf_error) ? true : ok; > } > > diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c > index 7aee0d6..66fb7ba 100644 > --- a/gcc/cp/typeck.c > +++ b/gcc/cp/typeck.c > @@ -2650,6 +2650,46 @@ check_template_keyword (tree decl) > } > } > > +/* Record that an access failure occurred on BASETYPE_PATH attempting > + to access FIELD_DECL. */ > + > +void > +access_failure_info::record_access_failure (tree basetype_path, > + tree field_decl) > +{ > + m_was_inaccessible = true; > + m_basetype_path = basetype_path; > + m_field_decl = field_decl; > +} > + > +/* If an access failure was recorded, then attempt to locate an > + accessor function for the pertinent field, and if one is > + available, add a note and fix-it hint suggesting using it. */ > + > +void > +access_failure_info::maybe_suggest_accessor (bool const_p) const > +{ > + if (!m_was_inaccessible) > + return; > + > + tree accessor > + = locate_field_accessor (m_basetype_path, m_field_decl, const_p); > + if (!accessor) > + return; > + > + /* The accessor must itself be accessible for it to be a reasonable > + suggestion. */ > + if (!accessible_p (m_basetype_path, accessor, true)) > + return; > + > + rich_location richloc (line_table, input_location); > + pretty_printer pp; > + pp_printf (&pp, "%s()", IDENTIFIER_POINTER (DECL_NAME (accessor))); > + richloc.add_fixit_replace (pp_formatted_text (&pp)); > + inform_at_rich_loc (&richloc, "field %q#D can be accessed via %q#D", > + m_field_decl, accessor); > +} > + > /* This function is called by the parser to process a class member > access expression of the form OBJECT.NAME. NAME is a node used by > the parser to represent a name; it is not yet a DECL. It may, > @@ -2834,8 +2874,11 @@ finish_class_member_access_expr (cp_expr object, tree > name, bool template_p, > else > { > /* Look up the member. */ > + access_failure_info afi; > member = lookup_member (access_path, name, /*protect=*/1, > - /*want_type=*/false, complain); > + /*want_type=*/false, complain, > + &afi); > + afi.maybe_suggest_accessor (TYPE_READONLY (object_type)); > if (member == NULL_TREE) > { > if (dependent_type_p (object_type)) > diff --git a/gcc/testsuite/g++.dg/other/accessor-fixits-1.C > b/gcc/testsuite/g++.dg/other/accessor-fixits-1.C > new file mode 100644 > index 0000000..cc96b87 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/other/accessor-fixits-1.C > @@ -0,0 +1,178 @@ > +// { dg-options "-fdiagnostics-show-caret" } > + > +class t1 > +{ > +public: > + int get_color () const { return m_color; } > + int get_shape () const { return m_shape; } > + > +private: > + int m_color; > + > +protected: > + int m_shape; > +}; > + > +int test_access_t1_color (t1 &ref) > +{ > + return ref.m_color; // { dg-error ".int t1::m_color. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ref.m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "declared private here" "" { target *-*-* } 10 } > + /* { dg-begin-multiline-output "" } > + int m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_color. can be accessed via .int > t1::get_color\\(\\) const." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ref.m_color; > + ^~~~~~~ > + get_color() > + { dg-end-multiline-output "" } */ > +} > + > +int test_access_t1_shape (t1 &ref) > +{ > + return ref.m_shape; // { dg-error ".int t1::m_shape. is protected within > this context" } > + /* { dg-begin-multiline-output "" } > + return ref.m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "declared protected here" "" { target *-*-* } 13 } > + /* { dg-begin-multiline-output "" } > + int m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_shape. can be accessed via .int > t1::get_shape\\(\\) const." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ref.m_shape; > + ^~~~~~~ > + get_shape() > + { dg-end-multiline-output "" } */ > +} > + > +int test_deref_t1_color (t1 *ptr) > +{ > + return ptr->m_color; // { dg-error ".int t1::m_color. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + > + /* { dg-begin-multiline-output "" } > + int m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_color. can be accessed via .int > t1::get_color\\(\\) const." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + get_color() > + { dg-end-multiline-output "" } */ > +} > + > +int test_deref_t1_shape (t1 *ptr) > +{ > + return ptr->m_shape; // { dg-error ".int t1::m_shape. is protected within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + > + /* { dg-begin-multiline-output "" } > + int m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_shape. can be accessed via .int > t1::get_shape\\(\\) const." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ptr->m_shape; > + ^~~~~~~ > + get_shape() > + { dg-end-multiline-output "" } */ > +} > + > +/* Example of public inheritance. */ > + > +class t2 : public t1 > +{ > +}; > + > +int test_deref_t2_color (t2 *ptr) > +{ > + return ptr->m_color; // { dg-error ".int t1::m_color. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + > + /* { dg-begin-multiline-output "" } > + int m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_color. can be accessed via .int > t1::get_color\\(\\) const." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + get_color() > + { dg-end-multiline-output "" } */ > +} > + > +/* Example of private inheritance. */ > + > +class t3 : private t1 > +{ > +}; > + > +int test_deref_t3_color (t3 *ptr) > +{ > + return ptr->m_color; // { dg-error ".int t1::m_color. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + /* { dg-begin-multiline-output "" } > + int m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + /* We shouldn't provide a fix-it hint for this case due to the > + private inheritance. */ > +} > + > +/* Example of non-public "accessor". */ > + > +class t4 > +{ > + int m_field; > + int get_field () { return m_field; } > +}; > + > +int test_deref_t4_field (t4 *ptr) > +{ > + return ptr->m_field; // { dg-error ".int t4::m_field. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_field; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + /* { dg-begin-multiline-output "" } > + int m_field; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + /* We shouldn't provide a fix-it hint for this case, as the accessor is > + itself private. */ > +} > diff --git a/gcc/testsuite/g++.dg/other/accessor-fixits-2.C > b/gcc/testsuite/g++.dg/other/accessor-fixits-2.C > new file mode 100644 > index 0000000..e1a2b78 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/other/accessor-fixits-2.C > @@ -0,0 +1,104 @@ > +// { dg-options "-fdiagnostics-show-caret" } > + > +/* Test of accessors that return references. */ > + > +class t1 > +{ > +public: > + int& get_color () { return m_color; } > + int& get_shape () { return m_shape; } > + > +private: > + int m_color; > + > +protected: > + int m_shape; > +}; > + > +int test_access_t1_color (t1 &ref) > +{ > + return ref.m_color; // { dg-error ".int t1::m_color. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ref.m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "declared private here" "" { target *-*-* } 12 } > + /* { dg-begin-multiline-output "" } > + int m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_color. can be accessed via .int& > t1::get_color\\(\\)." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ref.m_color; > + ^~~~~~~ > + get_color() > + { dg-end-multiline-output "" } */ > +} > + > +int test_access_t1_shape (t1 &ref) > +{ > + return ref.m_shape; // { dg-error ".int t1::m_shape. is protected within > this context" } > + /* { dg-begin-multiline-output "" } > + return ref.m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "declared protected here" "" { target *-*-* } 15 } > + /* { dg-begin-multiline-output "" } > + int m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_shape. can be accessed via .int& > t1::get_shape\\(\\)." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ref.m_shape; > + ^~~~~~~ > + get_shape() > + { dg-end-multiline-output "" } */ > +} > + > +int test_deref_t1_color (t1 *ptr) > +{ > + return ptr->m_color; // { dg-error ".int t1::m_color. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + > + /* { dg-begin-multiline-output "" } > + int m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_color. can be accessed via .int& > t1::get_color\\(\\)." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + get_color() > + { dg-end-multiline-output "" } */ > +} > + > +int test_deref_t1_shape (t1 *ptr) > +{ > + return ptr->m_shape; // { dg-error ".int t1::m_shape. is protected within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + > + /* { dg-begin-multiline-output "" } > + int m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "field .int t1::m_shape. can be accessed via .int& > t1::get_shape\\(\\)." "" { target *-*-* } .-12 } > + /* { dg-begin-multiline-output "" } > + return ptr->m_shape; > + ^~~~~~~ > + get_shape() > + { dg-end-multiline-output "" } */ > +} > diff --git a/gcc/testsuite/g++.dg/other/accessor-fixits-3.C > b/gcc/testsuite/g++.dg/other/accessor-fixits-3.C > new file mode 100644 > index 0000000..27d2eb4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/other/accessor-fixits-3.C > @@ -0,0 +1,15 @@ > +class foo > +{ > +public: > + static foo& get_singleton () { return s_singleton; } > + > +private: > + static foo s_singleton; > +}; > + > +foo & test_access_singleton () > +{ > + return foo::s_singleton; // { dg-error ".foo foo::s_singleton. is private > within this context" } > + // { dg-message "declared private here" "" { target *-*-* } 7 } > + // We don't yet support generating a fix-it hint for this case. > +} > diff --git a/gcc/testsuite/g++.dg/other/accessor-fixits-4.C > b/gcc/testsuite/g++.dg/other/accessor-fixits-4.C > new file mode 100644 > index 0000000..c03dd4e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/other/accessor-fixits-4.C > @@ -0,0 +1,48 @@ > +// { dg-options "-fdiagnostics-show-caret" } > + > +class t1 > +{ > +public: > + int& get_color () { return m_color; } > + int& get_shape () { return m_shape; } > + > +private: > + int m_color; // { dg-line color_decl } > + int m_shape; // { dg-line shape_decl } > +}; > + > +int test_const_ptr (const t1 *ptr) > +{ > + return ptr->m_color; // { dg-error ".int t1::m_color. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ptr->m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "declared private here" "" { target *-*-* } color_decl } > + /* { dg-begin-multiline-output "" } > + int m_color; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + /* We shouldn't issue a suggestion: the accessor is non-const, and we > + only have a const ptr. */ > +} > + > +int test_const_reference (const t1 &ref) > +{ > + return ref.m_shape; // { dg-error ".int t1::m_shape. is private within > this context" } > + /* { dg-begin-multiline-output "" } > + return ref.m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + // { dg-message "declared private here" "" { target *-*-* } shape_decl } > + /* { dg-begin-multiline-output "" } > + int m_shape; > + ^~~~~~~ > + { dg-end-multiline-output "" } */ > + > + /* We shouldn't issue a suggestion: the accessor is non-const, and we > + only have a const ptr. */ > +} > -- > 1.8.5.3 >