C++: fix-it hints suggesting accessors for private fields

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.

From-SVN: r248128
This commit is contained in:
David Malcolm 2017-05-16 19:52:26 +00:00 committed by David Malcolm
parent 727577c230
commit 10791753c1
11 changed files with 703 additions and 11 deletions

View file

@ -1,3 +1,33 @@
2017-05-16 David Malcolm <dmalcolm@redhat.com>
* 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.
2017-05-16 Marek Polacek <polacek@redhat.com>
PR sanitizer/80536

View file

@ -6396,7 +6396,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);
@ -6422,17 +6422,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;

View file

@ -5758,8 +5758,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,
@ -6412,8 +6434,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);
@ -6468,7 +6492,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. */

View file

@ -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. */

View file

@ -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;
}

View file

@ -2647,6 +2647,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,
@ -2829,8 +2869,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))

View file

@ -1,3 +1,10 @@
2017-05-16 David Malcolm <dmalcolm@redhat.com>
* 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.
2017-05-16 Carl Love <cel@us.ibm.com>
* gcc.target/powerpc/builtins-3.c: New vec_mule, vec_mulo test cases.

View file

@ -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. */
}

View file

@ -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 "" } */
}

View file

@ -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.
}

View file

@ -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. */
}