From 722ec359d1b00bc48d306cc8261358cfa395fdd0 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 18 Mar 2026 18:16:39 -0400
Subject: [PATCH 46/98] FIXME: not-yet-working experiments with mandatory names
 for fix-it hints (PR diagnostics/121986)

---
 gcc/c-family/c-warn.cc         |  25 +++--
 gcc/c/c-parser.cc              |   5 +-
 gcc/c/c-typeck.cc              |  10 +-
 gcc/cp/lex.cc                  |   7 +-
 gcc/cp/typeck2.cc              |   8 +-
 gcc/substring-locations.cc     |  17 ++-
 libcpp/include/rich-location.h | 198 +++++++++++++++++++--------------
 7 files changed, 167 insertions(+), 103 deletions(-)

diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc
index 6de072582dfc..a9b0b2e88954 100644
--- a/gcc/c-family/c-warn.cc
+++ b/gcc/c-family/c-warn.cc
@@ -575,8 +575,11 @@ warn_logical_not_parentheses (location_t location, enum tree_code code,
     {
       location_t lhs_loc = EXPR_LOCATION (lhs);
       rich_location richloc (line_table, lhs_loc);
-      richloc.add_fixit_insert_before (lhs_loc, "(");
-      richloc.add_fixit_insert_after (lhs_loc, ")");
+      auto &fix = richloc.add_proposed_fix
+	("Add parentheses around left hand side "
+	 "expression to silence this warning");
+      fix.insert_before (lhs_loc, "(");
+      fix.insert_after (lhs_loc, ")");
       inform (&richloc, "add parentheses around left hand side "
 	      "expression to silence this warning");
     }
@@ -3934,8 +3937,10 @@ check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val,
       if (rhs_uhwi < (INT_TYPE_SIZE - 1))
 	{
 	  unsigned HOST_WIDE_INT suggested_result = 1 << rhs_uhwi;
-	  loc.add_fixit_replace (lhs_loc, "1");
-	  loc.add_fixit_replace (operator_loc, "<<");
+	  auto &fix = loc.add_proposed_fix ("Use %qs rather than %qs",
+					    "1 <<", "2^");
+	  fix.replace (lhs_loc, "1");
+	  fix.replace (operator_loc, "<<");
 	  warned = warning_at (&loc, OPT_Wxor_used_as_pow,
 			       "result of %<%wu^%wu%> is %wu;"
 			       " did you mean %<1 << %wu%> (%wu)?",
@@ -3944,8 +3949,10 @@ check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val,
 	}
       else if (rhs_uhwi < (LONG_LONG_TYPE_SIZE - 1))
 	{
-	  loc.add_fixit_replace (lhs_loc, "1LL");
-	  loc.add_fixit_replace (operator_loc, "<<");
+	  auto &fix = loc.add_proposed_fix ("Use %qs rather than %qs",
+					    "1LL <<", "2^");
+	  fix.replace (lhs_loc, "1LL");
+	  fix.replace (operator_loc, "<<");
 	  warned = warning_at (&loc, OPT_Wxor_used_as_pow,
 			       "result of %<%wu^%wu%> is %wu;"
 			       " did you mean %<1LL << %wu%>?",
@@ -3962,8 +3969,10 @@ check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val,
   else
     {
       gcc_assert (lhs_uhwi == 10);
-      loc.add_fixit_replace (lhs_loc, "1");
-      loc.add_fixit_replace (operator_loc, "e");
+      auto &fix = loc.add_proposed_fix ("Use %qs rather than %qs",
+					"1e", "10^");
+      fix.replace (lhs_loc, "1");
+      fix.replace (operator_loc, "e");
       warned = warning_at (&loc, OPT_Wxor_used_as_pow,
 			   "result of %<%wu^%wu%> is %wu;"
 			   " did you mean %<1e%wu%>?",
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 0b8b23871090..12503bb10dfb 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -30560,7 +30560,10 @@ c_parser_omp_metadirective (c_parser *parser, bool *if_p)
       if (strcmp (p, "default") == 0)
 	{
 	  gcc_rich_location richloc (pragma_loc);
-	  richloc.add_fixit_replace (match_loc, "otherwise");
+	  auto &fix = richloc.add_proposed_fix ("Replace %qs with %qs",
+						"default",
+						"otherwise");
+	  fix.replace (match_loc, "otherwise");
 	  warning_at (&richloc, OPT_Wdeprecated_openmp,
 		      "%<default%> clause on metadirectives deprecated since "
 		      "OpenMP 5.2, use %<otherwise%>");
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 051773a0cd4d..7914346ad73f 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -3437,8 +3437,10 @@ build_component_ref (location_t loc, tree datum, tree component,
       rich_location richloc (line_table, loc);
       if (INDIRECT_REF_P (datum) && arrow_loc != UNKNOWN_LOCATION)
 	{
-	  richloc.add_fixit_insert_before (arrow_loc, "(*");
-	  richloc.add_fixit_insert_after (arrow_loc, ")");
+	  auto fix& = richloc.add_proposed_fix
+	    ("Add %qs around pointer before dereferencing it", "(* )");
+	  fix.insert_before (arrow_loc, "(*");
+	  fix.insert_after (arrow_loc, ")");
 	  error_at (&richloc,
 		    "%qE is a pointer to pointer; did you mean to dereference "
 		    "it before applying %<->%> to it?",
@@ -3447,7 +3449,9 @@ build_component_ref (location_t loc, tree datum, tree component,
       else
 	{
 	  /* "loc" should be the "." token.  */
-	  richloc.add_fixit_replace ("->");
+	  auto fix& = richloc.add_proposed_fix
+	    ("Replace %qs with %qs", ".", "->");
+	  fix.replace ("->");
 	  error_at (&richloc,
 		    "%qE is a pointer; did you mean to use %<->%>?",
 		    datum);
diff --git a/gcc/cp/lex.cc b/gcc/cp/lex.cc
index 88b0b24e097c..47390e6ac21c 100644
--- a/gcc/cp/lex.cc
+++ b/gcc/cp/lex.cc
@@ -819,7 +819,12 @@ unqualified_name_lookup_error (tree name, location_t loc)
 	  if (const char *suggestion = hint.suggestion ())
 	    {
 	      gcc_rich_location richloc (loc);
-	      richloc.add_fixit_replace (suggestion);
+	      auto &fix = richloc.add_proposed_fix
+		("Replace %qD with %qs",
+		 // FIXME how to decode %qD?
+		 // Do we have access to global_dc at all?
+		 name, suggestion);
+	      fix.replace (suggestion);
 	      error_at (&richloc,
 			"%qD was not declared in this scope; did you mean %qs?",
 			name, suggestion);
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 664dd0990bf8..d30cb80da4cf 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -448,7 +448,13 @@ cxx_incomplete_type_diagnostic (location_t loc, const_tree value,
 	    if (TREE_CODE (member) == FUNCTION_DECL
 		&& DECL_OBJECT_MEMBER_FUNCTION_P (member)
 		&& type_num_arguments (TREE_TYPE (member)) == 1)
-	      richloc.add_fixit_insert_after ("()");
+	      {
+		auto &fix
+		  = richloc.add_proposed_fix ("Invoke %qD by adding"
+					      " trailing parentheses",
+					      member);
+		fix.insert_after ("()");
+	      }
 	    complained = emit_diagnostic (diag_kind, &richloc, 0,
 			     "invalid use of member function %qD "
 			     "(did you forget the %<()%> ?)", member);
diff --git a/gcc/substring-locations.cc b/gcc/substring-locations.cc
index 64871f437dd0..d7c698e78e04 100644
--- a/gcc/substring-locations.cc
+++ b/gcc/substring-locations.cc
@@ -201,7 +201,13 @@ format_string_diagnostic_t::emit_warning_n_va (diagnostics::option_id option_id,
 		       highlight_color_param);
 
   if (!err && m_corrected_substring && substring_within_range)
-    richloc.add_fixit_replace (fmt_substring_range, m_corrected_substring);
+    {
+      auto &fix
+	= richloc.add_proposed_fix ("Replace %qs with %qs",
+				    "FIXME",
+				    m_corrected_substring);
+      fix.replace (fmt_substring_range, m_corrected_substring);
+    }
 
   diagnostics::diagnostic_info diagnostic;
   if (singular_gmsgid != plural_gmsgid)
@@ -236,8 +242,13 @@ format_string_diagnostic_t::emit_warning_n_va (diagnostics::option_id option_id,
 	   m_fmt_label,
 	   highlight_color_format_string);
 	if (m_corrected_substring)
-	  substring_richloc.add_fixit_replace (fmt_substring_range,
-					       m_corrected_substring);
+	  {
+	    auto &fix
+	      = richloc.add_proposed_fix ("Replace %qs with %qs",
+					  "FIXME",
+					  m_corrected_substring);
+	    fix.replace (fmt_substring_range, m_corrected_substring);
+	  }
 	inform (&substring_richloc,
 		"format string is defined here");
       }
diff --git a/libcpp/include/rich-location.h b/libcpp/include/rich-location.h
index 1334b3abb80d..c78837b90e77 100644
--- a/libcpp/include/rich-location.h
+++ b/libcpp/include/rich-location.h
@@ -212,8 +212,10 @@ semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len)
   m_num = len;
 }
 
-class fixit_hint;
-namespace diagnostics { namespace paths { class path; }}
+namespace diagnostics {
+  namespace changes { class proposed_fix; }
+  namespace paths { class path; }
+}
 
 /* A "rich" source code location, for use when printing diagnostics.
    A rich_location has one or more carets&ranges, where the carets
@@ -437,65 +439,13 @@ class rich_location
   override_column (int column);
 
   /* Fix-it hints.  */
+  diagnostics::changes::proposed_fix &
+  add_proposed_fix (const char *fmt, ...);
 
-  /* Methods for adding insertion fix-it hints.  */
-
-  /* Suggest inserting NEW_CONTENT immediately before the primary
-     range's start.  */
-  void
-  add_fixit_insert_before (const char *new_content);
-
-  /* Suggest inserting NEW_CONTENT immediately before the start of WHERE.  */
-  void
-  add_fixit_insert_before (location_t where,
-			   const char *new_content);
-
-  /* Suggest inserting NEW_CONTENT immediately after the end of the primary
-     range.  */
-  void
-  add_fixit_insert_after (const char *new_content);
-
-  /* Suggest inserting NEW_CONTENT immediately after the end of WHERE.  */
-  void
-  add_fixit_insert_after (location_t where,
-			  const char *new_content);
-
-  /* Methods for adding removal fix-it hints.  */
-
-  /* Suggest removing the content covered by range 0.  */
-  void
-  add_fixit_remove ();
-
-  /* Suggest removing the content covered between the start and finish
-     of WHERE.  */
-  void
-  add_fixit_remove (location_t where);
-
-  /* Suggest removing the content covered by SRC_RANGE.  */
-  void
-  add_fixit_remove (source_range src_range);
-
-  /* Methods for adding "replace" fix-it hints.  */
-
-  /* Suggest replacing the content covered by range 0 with NEW_CONTENT.  */
-  void
-  add_fixit_replace (const char *new_content);
-
-  /* Suggest replacing the content between the start and finish of
-     WHERE with NEW_CONTENT.  */
-  void
-  add_fixit_replace (location_t where,
-		     const char *new_content);
-
-  /* Suggest replacing the content covered by SRC_RANGE with
-     NEW_CONTENT.  */
-  void
-  add_fixit_replace (source_range src_range,
-		     const char *new_content);
-
-  unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); }
-  fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; }
-  fixit_hint *get_last_fixit_hint () const;
+#if 0
+  unsigned int get_num_proposed_fixs () const { return m_proposed_fixs.count (); }
+  proposed_fix *get_proposed_fix (int idx) const { return m_proposed_fixs[idx]; }
+  proposed_fix *get_last_proposed_fix () const;
   bool seen_impossible_fixit_p () const { return m_seen_impossible_fixit; }
 
   /* Set this if the fix-it hints are not suitable to be
@@ -518,6 +468,7 @@ class rich_location
   {
     return !m_fixits_cannot_be_auto_applied;
   }
+#endif
 
   /* An optional path through the code.  */
   const diagnostics::paths::path *get_path () const { return m_path; }
@@ -540,11 +491,13 @@ class rich_location
   int get_column_override () const { return m_column_override; }
 
 private:
+#if 0
   bool reject_impossible_fixit (location_t where);
   void stop_supporting_fixits ();
   void maybe_add_fixit (location_t start,
 			location_t next_loc,
 			const char *new_content);
+#endif
 
 public:
   static const int STATICALLY_ALLOCATED_RANGES = 3;
@@ -563,9 +516,10 @@ protected:
   mutable expanded_location m_expanded_location;
 
   /* The class manages the memory pointed to by the elements of
-     the m_fixit_hints vector.  */
-  static const int MAX_STATIC_FIXIT_HINTS = 2;
-  semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints;
+     the m_proposed_fixs vector.  */
+  static const int MAX_STATIC_PROPOSED_FIXES = 2;
+  semi_embedded_vec <diagnostics::changes::proposed_fix *,
+		     MAX_STATIC_PROPOSED_FIXES> m_proposed_fixs;
 
   const diagnostics::paths::path *m_path;
 };
@@ -603,31 +557,22 @@ class range_label
   }
 };
 
-/* A fix-it hint: a suggested insertion, replacement, or deletion of text.
-   We handle these three types of edit with one class, by representing
-   them as replacement of a half-open range:
-       [start, next_loc)
-   Insertions have start == next_loc: "replace" the empty string at the
-   start location with the new string.
-   Deletions are replacement with the empty string.
+namespace diagnostics {
+namespace changes {
 
-   There is only limited support for newline characters in fix-it hints
-   as noted above in the comment for class rich_location.
-   A fixit_hint instance can have at most one newline character; if
-   present, the newline character must be the final character of
-   the content (preventing e.g. fix-its that split a pre-existing line).  */
+// FIXME:
 
-class fixit_hint
+class modification
 {
- public:
-  fixit_hint (location_t start,
-	      location_t next_loc,
-	      const char *new_content);
-  fixit_hint (const fixit_hint &other);
-  fixit_hint (fixit_hint &&other) = delete;
-  ~fixit_hint () { free (m_bytes); }
-  fixit_hint &operator= (const fixit_hint &) = delete;
-  fixit_hint &operator= (fixit_hint &&) = delete;
+public:
+  modification (location_t start,
+		location_t next_loc,
+		const char *new_content);
+  modification (const modification &other);
+  modification (modification &&other) = delete;
+  ~modification () { free (m_bytes); }
+  modification &operator= (const modification &) = delete;
+  modification &operator= (modification &&) = delete;
 
   bool affects_line_p (const line_maps *set,
 		       const char *file,
@@ -647,7 +592,7 @@ class fixit_hint
 
   bool ends_with_newline_p () const;
 
- private:
+private:
   /* We don't use source_range here since, unlike most places,
      this is a half-open/half-closed range:
        [start, next_loc)
@@ -658,4 +603,85 @@ class fixit_hint
   size_t m_len;
 };
 
+// FIXME:
+/* A fix-it hint: a suggested insertion, replacement, or deletion of text.
+   We handle these three types of edit with one class, by representing
+   them as replacement of a half-open range:
+       [start, next_loc)
+   Insertions have start == next_loc: "replace" the empty string at the
+   start location with the new string.
+   Deletions are replacement with the empty string.
+
+   There is only limited support for newline characters in fix-it hints
+   as noted above in the comment for class rich_location.
+   A fixit_hint instance can have at most one newline character; if
+   present, the newline character must be the final character of
+   the content (preventing e.g. fix-its that split a pre-existing line).  */
+
+class proposed_fix
+{
+ public:
+  /* Methods for adding modifications.  */
+
+  /* Suggest inserting NEW_CONTENT immediately before the primary
+     range's start.  */
+  void
+  insert_before (const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately before the start of WHERE.  */
+  void
+  insert_before (location_t where,
+		 const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately after the end of the primary
+     range.  */
+  void
+  insert_after (const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately after the end of WHERE.  */
+  void
+  insert_after (location_t where,
+		const char *new_content);
+
+  /* Methods for adding removal fix-it hints.  */
+
+  /* Suggest removing the content covered by range 0.  */
+  void
+  remove ();
+
+  /* Suggest removing the content covered between the start and finish
+     of WHERE.  */
+  void
+  remove (location_t where);
+
+  /* Suggest removing the content covered by SRC_RANGE.  */
+  void
+  remove (source_range src_range);
+
+  /* Methods for adding "replace" fix-it hints.  */
+
+  /* Suggest replacing the content covered by range 0 with NEW_CONTENT.  */
+  void
+  replace (const char *new_content);
+
+  /* Suggest replacing the content between the start and finish of
+     WHERE with NEW_CONTENT.  */
+  void
+  replace (location_t where,
+	   const char *new_content);
+
+  /* Suggest replacing the content covered by SRC_RANGE with
+     NEW_CONTENT.  */
+  void
+  replace (source_range src_range,
+	   const char *new_content);
+
+private:
+  static const int MAX_STATIC_MODIFICATIONS = 2;
+  semi_embedded_vec <modification, MAX_STATIC_MODIFICATIONS> m_modifications;
+};
+
+} // namespace diagnostics::changes
+} // namespace diagnostics
+
 #endif /* !LIBCPP_RICH_LOCATION_H  */
-- 
2.49.0

