Home | History | Annotate | Line # | Download | only in include
rich-location.h revision 1.1
      1 /* Bundles of location information used when printing diagnostics.
      2    Copyright (C) 2015-2024 Free Software Foundation, Inc.
      3 
      4 This program is free software; you can redistribute it and/or modify it
      5 under the terms of the GNU General Public License as published by the
      6 Free Software Foundation; either version 3, or (at your option) any
      7 later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 GNU General Public License for more details.
     13 
     14 You should have received a copy of the GNU General Public License
     15 along with this program; see the file COPYING3.  If not see
     16 <http://www.gnu.org/licenses/>.
     17 
     18  In other words, you are welcome to use, share and improve this program.
     19  You are forbidden to forbid anyone else to use, share and improve
     20  what you give them.   Help stamp out software-hoarding!  */
     21 
     22 #ifndef LIBCPP_RICH_LOCATION_H
     23 #define LIBCPP_RICH_LOCATION_H
     24 
     25 class range_label;
     26 
     27 /* A hint to diagnostic_show_locus on how to print a source range within a
     28    rich_location.
     29 
     30    Typically this is SHOW_RANGE_WITH_CARET for the 0th range, and
     31    SHOW_RANGE_WITHOUT_CARET for subsequent ranges,
     32    but the Fortran frontend uses SHOW_RANGE_WITH_CARET repeatedly for
     33    printing things like:
     34 
     35        x = x + y
     36            1   2
     37        Error: Shapes for operands at (1) and (2) are not conformable
     38 
     39    where "1" and "2" are notionally carets.  */
     40 
     41 enum range_display_kind
     42 {
     43   /* Show the pertinent source line(s), the caret, and underline(s).  */
     44   SHOW_RANGE_WITH_CARET,
     45 
     46   /* Show the pertinent source line(s) and underline(s), but don't
     47      show the caret (just an underline).  */
     48   SHOW_RANGE_WITHOUT_CARET,
     49 
     50   /* Just show the source lines; don't show the range itself.
     51      This is for use when displaying some line-insertion fix-it hints (for
     52      showing the user context on the change, for when it doesn't make sense
     53      to highlight the first column on the next line).  */
     54   SHOW_LINES_WITHOUT_RANGE
     55 };
     56 
     57 /* A location within a rich_location: a caret&range, with
     58    the caret potentially flagged for display, and an optional
     59    label.  */
     60 
     61 struct location_range
     62 {
     63   location_t m_loc;
     64 
     65   enum range_display_kind m_range_display_kind;
     66 
     67   /* If non-NULL, the label for this range.  */
     68   const range_label *m_label;
     69 };
     70 
     71 /* A partially-embedded vec for use within rich_location for storing
     72    ranges and fix-it hints.
     73 
     74    Elements [0..NUM_EMBEDDED) are allocated within m_embed, after
     75    that they are within the dynamically-allocated m_extra.
     76 
     77    This allows for static allocation in the common case, whilst
     78    supporting the rarer case of an arbitrary number of elements.
     79 
     80    Dynamic allocation is not performed unless it's needed.  */
     81 
     82 template <typename T, int NUM_EMBEDDED>
     83 class semi_embedded_vec
     84 {
     85  public:
     86   semi_embedded_vec ();
     87   ~semi_embedded_vec ();
     88 
     89   unsigned int count () const { return m_num; }
     90   T& operator[] (int idx);
     91   const T& operator[] (int idx) const;
     92 
     93   void push (const T&);
     94   void truncate (int len);
     95 
     96  private:
     97   int m_num;
     98   T m_embedded[NUM_EMBEDDED];
     99   int m_alloc;
    100   T *m_extra;
    101 };
    102 
    103 /* Constructor for semi_embedded_vec.  In particular, no dynamic allocation
    104    is done.  */
    105 
    106 template <typename T, int NUM_EMBEDDED>
    107 semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec ()
    108 : m_num (0), m_alloc (0), m_extra (NULL)
    109 {
    110 }
    111 
    112 /* semi_embedded_vec's dtor.  Release any dynamically-allocated memory.  */
    113 
    114 template <typename T, int NUM_EMBEDDED>
    115 semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec ()
    116 {
    117   XDELETEVEC (m_extra);
    118 }
    119 
    120 /* Look up element IDX, mutably.  */
    121 
    122 template <typename T, int NUM_EMBEDDED>
    123 T&
    124 semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx)
    125 {
    126   linemap_assert (idx < m_num);
    127   if (idx < NUM_EMBEDDED)
    128     return m_embedded[idx];
    129   else
    130     {
    131       linemap_assert (m_extra != NULL);
    132       return m_extra[idx - NUM_EMBEDDED];
    133     }
    134 }
    135 
    136 /* Look up element IDX (const).  */
    137 
    138 template <typename T, int NUM_EMBEDDED>
    139 const T&
    140 semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) const
    141 {
    142   linemap_assert (idx < m_num);
    143   if (idx < NUM_EMBEDDED)
    144     return m_embedded[idx];
    145   else
    146     {
    147       linemap_assert (m_extra != NULL);
    148       return m_extra[idx - NUM_EMBEDDED];
    149     }
    150 }
    151 
    152 /* Append VALUE to the end of the semi_embedded_vec.  */
    153 
    154 template <typename T, int NUM_EMBEDDED>
    155 void
    156 semi_embedded_vec<T, NUM_EMBEDDED>::push (const T& value)
    157 {
    158   int idx = m_num++;
    159   if (idx < NUM_EMBEDDED)
    160     m_embedded[idx] = value;
    161   else
    162     {
    163       /* Offset "idx" to be an index within m_extra.  */
    164       idx -= NUM_EMBEDDED;
    165       if (NULL == m_extra)
    166 	{
    167 	  linemap_assert (m_alloc == 0);
    168 	  m_alloc = 16;
    169 	  m_extra = XNEWVEC (T, m_alloc);
    170 	}
    171       else if (idx >= m_alloc)
    172 	{
    173 	  linemap_assert (m_alloc > 0);
    174 	  m_alloc *= 2;
    175 	  m_extra = XRESIZEVEC (T, m_extra, m_alloc);
    176 	}
    177       linemap_assert (m_extra);
    178       linemap_assert (idx < m_alloc);
    179       m_extra[idx] = value;
    180     }
    181 }
    182 
    183 /* Truncate to length LEN.  No deallocation is performed.  */
    184 
    185 template <typename T, int NUM_EMBEDDED>
    186 void
    187 semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len)
    188 {
    189   linemap_assert (len <= m_num);
    190   m_num = len;
    191 }
    192 
    193 class fixit_hint;
    194 class diagnostic_path;
    195 
    196 /* A "rich" source code location, for use when printing diagnostics.
    197    A rich_location has one or more carets&ranges, where the carets
    198    are optional.  These are referred to as "ranges" from here.
    199    Typically the zeroth range has a caret; other ranges sometimes
    200    have carets.
    201 
    202    The "primary" location of a rich_location is the caret of range 0,
    203    used for determining the line/column when printing diagnostic
    204    text, such as:
    205 
    206       some-file.c:3:1: error: ...etc...
    207 
    208    Additional ranges may be added to help the user identify other
    209    pertinent clauses in a diagnostic.
    210 
    211    Ranges can (optionally) be given labels via class range_label.
    212 
    213    rich_location instances are intended to be allocated on the stack
    214    when generating diagnostics, and to be short-lived.
    215 
    216    Examples of rich locations
    217    --------------------------
    218 
    219    Example A
    220    *********
    221       int i = "foo";
    222               ^
    223    This "rich" location is simply a single range (range 0), with
    224    caret = start = finish at the given point.
    225 
    226    Example B
    227    *********
    228       a = (foo && bar)
    229           ~~~~~^~~~~~~
    230    This rich location has a single range (range 0), with the caret
    231    at the first "&", and the start/finish at the parentheses.
    232    Compare with example C below.
    233 
    234    Example C
    235    *********
    236       a = (foo && bar)
    237            ~~~ ^~ ~~~
    238    This rich location has three ranges:
    239    - Range 0 has its caret and start location at the first "&" and
    240      end at the second "&.
    241    - Range 1 has its start and finish at the "f" and "o" of "foo";
    242      the caret is not flagged for display, but is perhaps at the "f"
    243      of "foo".
    244    - Similarly, range 2 has its start and finish at the "b" and "r" of
    245      "bar"; the caret is not flagged for display, but is perhaps at the
    246      "b" of "bar".
    247    Compare with example B above.
    248 
    249    Example D (Fortran frontend)
    250    ****************************
    251        x = x + y
    252            1   2
    253    This rich location has range 0 at "1", and range 1 at "2".
    254    Both are flagged for caret display.  Both ranges have start/finish
    255    equal to their caret point.  The frontend overrides the diagnostic
    256    context's default caret character for these ranges.
    257 
    258    Example E (range labels)
    259    ************************
    260       printf ("arg0: %i  arg1: %s arg2: %i",
    261                                ^~
    262                                |
    263                                const char *
    264               100, 101, 102);
    265                    ~~~
    266                    |
    267                    int
    268    This rich location has two ranges:
    269    - range 0 is at the "%s" with start = caret = "%" and finish at
    270      the "s".  It has a range_label ("const char *").
    271    - range 1 has start/finish covering the "101" and is not flagged for
    272      caret printing.  The caret is at the start of "101", where its
    273      range_label is printed ("int").
    274 
    275    Fix-it hints
    276    ------------
    277 
    278    Rich locations can also contain "fix-it hints", giving suggestions
    279    for the user on how to edit their code to fix a problem.  These
    280    can be expressed as insertions, replacements, and removals of text.
    281    The edits by default are relative to the zeroth range within the
    282    rich_location, but optionally they can be expressed relative to
    283    other locations (using various overloaded methods of the form
    284    rich_location::add_fixit_*).
    285 
    286    For example:
    287 
    288    Example F: fix-it hint: insert_before
    289    *************************************
    290       ptr = arr[0];
    291 	    ^~~~~~
    292 	    &
    293    This rich location has a single range (range 0) covering "arr[0]",
    294    with the caret at the start.  The rich location has a single
    295    insertion fix-it hint, inserted before range 0, added via
    296      richloc.add_fixit_insert_before ("&");
    297 
    298    Example G: multiple fix-it hints: insert_before and insert_after
    299    ****************************************************************
    300       #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2)
    301 				      ^~~~  ^~~~  ^~~~
    302 				      (   ) (   ) (   )
    303    This rich location has three ranges, covering "arg0", "arg1",
    304    and "arg2", all with caret-printing enabled.
    305    The rich location has 6 insertion fix-it hints: each arg
    306    has a pair of insertion fix-it hints, suggesting wrapping
    307    them with parentheses: one a '(' inserted before,
    308    the other a ')' inserted after, added via
    309      richloc.add_fixit_insert_before (LOC, "(");
    310    and
    311      richloc.add_fixit_insert_after (LOC, ")");
    312 
    313    Example H: fix-it hint: removal
    314    *******************************
    315      struct s {int i};;
    316 		      ^
    317 		      -
    318    This rich location has a single range at the stray trailing
    319    semicolon, along with a single removal fix-it hint, covering
    320    the same range, added via:
    321      richloc.add_fixit_remove ();
    322 
    323    Example I: fix-it hint: replace
    324    *******************************
    325       c = s.colour;
    326 	    ^~~~~~
    327 	    color
    328    This rich location has a single range (range 0) covering "colour",
    329    and a single "replace" fix-it hint, covering the same range,
    330    added via
    331      richloc.add_fixit_replace ("color");
    332 
    333    Example J: fix-it hint: line insertion
    334    **************************************
    335 
    336      3 | #include <stddef.h>
    337      + |+#include <stdio.h>
    338      4 | int the_next_line;
    339 
    340    This rich location has a single range at line 4 column 1, marked
    341    with SHOW_LINES_WITHOUT_RANGE (to avoid printing a meaningless caret
    342    on the "i" of int).  It has a insertion fix-it hint of the string
    343    "#include <stdio.h>\n".
    344 
    345    Adding a fix-it hint can fail: for example, attempts to insert content
    346    at the transition between two line maps may fail due to there being no
    347    location_t value to express the new location.
    348 
    349    Attempts to add a fix-it hint within a macro expansion will fail.
    350 
    351    There is only limited support for newline characters in fix-it hints:
    352    only hints with newlines which insert an entire new line are permitted,
    353    inserting at the start of a line, and finishing with a newline
    354    (with no interior newline characters).  Other attempts to add
    355    fix-it hints containing newline characters will fail.
    356    Similarly, attempts to delete or replace a range *affecting* multiple
    357    lines will fail.
    358 
    359    The rich_location API handles these failures gracefully, so that
    360    diagnostics can attempt to add fix-it hints without each needing
    361    extensive checking.
    362 
    363    Fix-it hints within a rich_location are "atomic": if any hints can't
    364    be applied, none of them will be (tracked by the m_seen_impossible_fixit
    365    flag), and no fix-its hints will be displayed for that rich_location.
    366    This implies that diagnostic messages need to be worded in such a way
    367    that they make sense whether or not the fix-it hints are displayed,
    368    or that richloc.seen_impossible_fixit_p () should be checked before
    369    issuing the diagnostics.  */
    370 
    371 class rich_location
    372 {
    373  public:
    374   /* Constructors.  */
    375 
    376   /* Constructing from a location.  */
    377   rich_location (line_maps *set, location_t loc,
    378 		 const range_label *label = NULL);
    379 
    380   /* Destructor.  */
    381   ~rich_location ();
    382 
    383   /* The class manages the memory pointed to by the elements of
    384      the M_FIXIT_HINTS vector and is not meant to be copied or
    385      assigned.  */
    386   rich_location (const rich_location &) = delete;
    387   void operator= (const rich_location &) = delete;
    388 
    389   /* Accessors.  */
    390   location_t get_loc () const { return get_loc (0); }
    391   location_t get_loc (unsigned int idx) const;
    392 
    393   void
    394   add_range (location_t loc,
    395 	     enum range_display_kind range_display_kind
    396 	       = SHOW_RANGE_WITHOUT_CARET,
    397 	     const range_label *label = NULL);
    398 
    399   void
    400   set_range (unsigned int idx, location_t loc,
    401 	     enum range_display_kind range_display_kind);
    402 
    403   unsigned int get_num_locations () const { return m_ranges.count (); }
    404 
    405   const location_range *get_range (unsigned int idx) const;
    406   location_range *get_range (unsigned int idx);
    407 
    408   expanded_location get_expanded_location (unsigned int idx) const;
    409 
    410   void
    411   override_column (int column);
    412 
    413   /* Fix-it hints.  */
    414 
    415   /* Methods for adding insertion fix-it hints.  */
    416 
    417   /* Suggest inserting NEW_CONTENT immediately before the primary
    418      range's start.  */
    419   void
    420   add_fixit_insert_before (const char *new_content);
    421 
    422   /* Suggest inserting NEW_CONTENT immediately before the start of WHERE.  */
    423   void
    424   add_fixit_insert_before (location_t where,
    425 			   const char *new_content);
    426 
    427   /* Suggest inserting NEW_CONTENT immediately after the end of the primary
    428      range.  */
    429   void
    430   add_fixit_insert_after (const char *new_content);
    431 
    432   /* Suggest inserting NEW_CONTENT immediately after the end of WHERE.  */
    433   void
    434   add_fixit_insert_after (location_t where,
    435 			  const char *new_content);
    436 
    437   /* Methods for adding removal fix-it hints.  */
    438 
    439   /* Suggest removing the content covered by range 0.  */
    440   void
    441   add_fixit_remove ();
    442 
    443   /* Suggest removing the content covered between the start and finish
    444      of WHERE.  */
    445   void
    446   add_fixit_remove (location_t where);
    447 
    448   /* Suggest removing the content covered by SRC_RANGE.  */
    449   void
    450   add_fixit_remove (source_range src_range);
    451 
    452   /* Methods for adding "replace" fix-it hints.  */
    453 
    454   /* Suggest replacing the content covered by range 0 with NEW_CONTENT.  */
    455   void
    456   add_fixit_replace (const char *new_content);
    457 
    458   /* Suggest replacing the content between the start and finish of
    459      WHERE with NEW_CONTENT.  */
    460   void
    461   add_fixit_replace (location_t where,
    462 		     const char *new_content);
    463 
    464   /* Suggest replacing the content covered by SRC_RANGE with
    465      NEW_CONTENT.  */
    466   void
    467   add_fixit_replace (source_range src_range,
    468 		     const char *new_content);
    469 
    470   unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); }
    471   fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; }
    472   fixit_hint *get_last_fixit_hint () const;
    473   bool seen_impossible_fixit_p () const { return m_seen_impossible_fixit; }
    474 
    475   /* Set this if the fix-it hints are not suitable to be
    476      automatically applied.
    477 
    478      For example, if you are suggesting more than one
    479      mutually exclusive solution to a problem, then
    480      it doesn't make sense to apply all of the solutions;
    481      manual intervention is required.
    482 
    483      If set, then the fix-it hints in the rich_location will
    484      be printed, but will not be added to generated patches,
    485      or affect the modified version of the file.  */
    486   void fixits_cannot_be_auto_applied ()
    487   {
    488     m_fixits_cannot_be_auto_applied = true;
    489   }
    490 
    491   bool fixits_can_be_auto_applied_p () const
    492   {
    493     return !m_fixits_cannot_be_auto_applied;
    494   }
    495 
    496   /* An optional path through the code.  */
    497   const diagnostic_path *get_path () const { return m_path; }
    498   void set_path (const diagnostic_path *path) { m_path = path; }
    499 
    500   /* A flag for hinting that the diagnostic involves character encoding
    501      issues, and thus that it will be helpful to the user if we show some
    502      representation of how the characters in the pertinent source lines
    503      are encoded.
    504      The default is false (i.e. do not escape).
    505      When set to true, non-ASCII bytes in the pertinent source lines will
    506      be escaped in a manner controlled by the user-supplied option
    507      -fdiagnostics-escape-format=, so that the user can better understand
    508      what's going on with the encoding in their source file.  */
    509   bool escape_on_output_p () const { return m_escape_on_output; }
    510   void set_escape_on_output (bool flag) { m_escape_on_output = flag; }
    511 
    512   const line_maps *get_line_table () const { return m_line_table; }
    513 
    514   int get_column_override () const { return m_column_override; }
    515 
    516 private:
    517   bool reject_impossible_fixit (location_t where);
    518   void stop_supporting_fixits ();
    519   void maybe_add_fixit (location_t start,
    520 			location_t next_loc,
    521 			const char *new_content);
    522 
    523 public:
    524   static const int STATICALLY_ALLOCATED_RANGES = 3;
    525 
    526 protected:
    527   line_maps * const m_line_table;
    528   semi_embedded_vec <location_range, STATICALLY_ALLOCATED_RANGES> m_ranges;
    529 
    530   int m_column_override;
    531 
    532   mutable bool m_have_expanded_location;
    533   bool m_seen_impossible_fixit;
    534   bool m_fixits_cannot_be_auto_applied;
    535   bool m_escape_on_output;
    536 
    537   mutable expanded_location m_expanded_location;
    538 
    539   static const int MAX_STATIC_FIXIT_HINTS = 2;
    540   semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints;
    541 
    542   const diagnostic_path *m_path;
    543 };
    544 
    545 /* A struct for the result of range_label::get_text: a NUL-terminated buffer
    546    of localized text, and a flag to determine if the caller should "free" the
    547    buffer.  */
    548 
    549 class label_text
    550 {
    551 public:
    552   label_text ()
    553   : m_buffer (NULL), m_owned (false)
    554   {}
    555 
    556   ~label_text ()
    557   {
    558     if (m_owned)
    559       free (m_buffer);
    560   }
    561 
    562   /* Move ctor.  */
    563   label_text (label_text &&other)
    564   : m_buffer (other.m_buffer), m_owned (other.m_owned)
    565   {
    566     other.release ();
    567   }
    568 
    569   /* Move assignment.  */
    570   label_text & operator= (label_text &&other)
    571   {
    572     if (m_owned)
    573       free (m_buffer);
    574     m_buffer = other.m_buffer;
    575     m_owned = other.m_owned;
    576     other.release ();
    577     return *this;
    578   }
    579 
    580   /* Delete the copy ctor and copy-assignment operator.  */
    581   label_text (const label_text &) = delete;
    582   label_text & operator= (const label_text &) = delete;
    583 
    584   /* Create a label_text instance that borrows BUFFER from a
    585      longer-lived owner.  */
    586   static label_text borrow (const char *buffer)
    587   {
    588     return label_text (const_cast <char *> (buffer), false);
    589   }
    590 
    591   /* Create a label_text instance that takes ownership of BUFFER.  */
    592   static label_text take (char *buffer)
    593   {
    594     return label_text (buffer, true);
    595   }
    596 
    597   void release ()
    598   {
    599     m_buffer = NULL;
    600     m_owned = false;
    601   }
    602 
    603   const char *get () const
    604   {
    605     return m_buffer;
    606   }
    607 
    608   bool is_owner () const
    609   {
    610     return m_owned;
    611   }
    612 
    613 private:
    614   char *m_buffer;
    615   bool m_owned;
    616 
    617   label_text (char *buffer, bool owned)
    618   : m_buffer (buffer), m_owned (owned)
    619   {}
    620 };
    621 
    622 /* Abstract base class for labelling a range within a rich_location
    623    (e.g. for labelling expressions with their type).
    624 
    625    Generating the text could require non-trivial work, so this work
    626    is delayed (via the "get_text" virtual function) until the diagnostic
    627    printing code "knows" it needs it, thus avoiding doing it e.g. for
    628    warnings that are filtered by command-line flags.  This virtual
    629    function also isolates libcpp and the diagnostics subsystem from
    630    the front-end and middle-end-specific code for generating the text
    631    for the labels.
    632 
    633    Like the rich_location instances they annotate, range_label instances
    634    are intended to be allocated on the stack when generating diagnostics,
    635    and to be short-lived.  */
    636 
    637 class range_label
    638 {
    639  public:
    640   virtual ~range_label () {}
    641 
    642   /* Get localized text for the label.
    643      The RANGE_IDX is provided, allowing for range_label instances to be
    644      shared by multiple ranges if need be (the "flyweight" design pattern).  */
    645   virtual label_text get_text (unsigned range_idx) const = 0;
    646 };
    647 
    648 /* A fix-it hint: a suggested insertion, replacement, or deletion of text.
    649    We handle these three types of edit with one class, by representing
    650    them as replacement of a half-open range:
    651        [start, next_loc)
    652    Insertions have start == next_loc: "replace" the empty string at the
    653    start location with the new string.
    654    Deletions are replacement with the empty string.
    655 
    656    There is only limited support for newline characters in fix-it hints
    657    as noted above in the comment for class rich_location.
    658    A fixit_hint instance can have at most one newline character; if
    659    present, the newline character must be the final character of
    660    the content (preventing e.g. fix-its that split a pre-existing line).  */
    661 
    662 class fixit_hint
    663 {
    664  public:
    665   fixit_hint (location_t start,
    666 	      location_t next_loc,
    667 	      const char *new_content);
    668   ~fixit_hint () { free (m_bytes); }
    669 
    670   bool affects_line_p (const line_maps *set,
    671 		       const char *file,
    672 		       int line) const;
    673   location_t get_start_loc () const { return m_start; }
    674   location_t get_next_loc () const { return m_next_loc; }
    675   bool maybe_append (location_t start,
    676 		     location_t next_loc,
    677 		     const char *new_content);
    678 
    679   const char *get_string () const { return m_bytes; }
    680   size_t get_length () const { return m_len; }
    681 
    682   bool insertion_p () const { return m_start == m_next_loc; }
    683 
    684   bool ends_with_newline_p () const;
    685 
    686  private:
    687   /* We don't use source_range here since, unlike most places,
    688      this is a half-open/half-closed range:
    689        [start, next_loc)
    690      so that we can support insertion via start == next_loc.  */
    691   location_t m_start;
    692   location_t m_next_loc;
    693   char *m_bytes;
    694   size_t m_len;
    695 };
    696 
    697 #endif /* !LIBCPP_RICH_LOCATION_H  */
    698