/* Entry in the cooked index Copyright (C) 2022-2025 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "dwarf2/cooked-index-entry.h" #include "dwarf2/tag.h" #include "gdbsupport/selftest.h" /* See cooked-index-entry.h. */ std::string to_string (cooked_index_flag flags) { static constexpr cooked_index_flag::string_mapping mapping[] = { MAP_ENUM_FLAG (IS_MAIN), MAP_ENUM_FLAG (IS_STATIC), MAP_ENUM_FLAG (IS_LINKAGE), MAP_ENUM_FLAG (IS_TYPE_DECLARATION), MAP_ENUM_FLAG (IS_PARENT_DEFERRED), MAP_ENUM_FLAG (IS_SYNTHESIZED), }; return flags.to_string (mapping); } /* See cooked-index-entry.h. */ int cooked_index_entry::compare (const char *stra, const char *strb, comparison_mode mode) { #if defined (__GNUC__) && !defined (__clang__) && __GNUC__ <= 7 /* Work around error with gcc 7.5.0. */ auto munge = [] (char c) -> unsigned char #else auto munge = [] (char c) constexpr -> unsigned char #endif { /* Treat '<' as if it ended the string. This lets something like "func" match "func>". See the "Breakpoints in template functions" section in the manual. */ if (c == '<') return '\0'; return c_tolower (c); }; unsigned char a = munge (*stra); unsigned char b = munge (*strb); while (a != '\0' && b != '\0' && a == b) { a = munge (*++stra); b = munge (*++strb); } if (a == b) return 0; /* When completing, if STRB ends earlier than STRA, consider them as equal. */ if (mode == COMPLETE && b == '\0') return 0; return a < b ? -1 : 1; } #if GDB_SELF_TEST namespace { void test_compare () { /* Convenience aliases. */ const auto mode_compare = cooked_index_entry::MATCH; const auto mode_sort = cooked_index_entry::SORT; const auto mode_complete = cooked_index_entry::COMPLETE; SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", mode_complete) == 0); SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", mode_compare) < 0); SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", mode_compare) > 0); SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", mode_complete) < 0); SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", mode_complete) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name<>", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name<>", "name", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name<>", mode_complete) == 0); SELF_CHECK (cooked_index_entry::compare ("name<>", "name", mode_complete) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name", mode_complete) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name", mode_complete) == 0); SELF_CHECK (cooked_index_entry::compare ("name>", "name>", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name>", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name", "name>", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name>", "name", mode_compare) == 0); SELF_CHECK (cooked_index_entry::compare ("name>", "name>", "name 0); SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0); SELF_CHECK (cooked_index_entry::compare ("func", "func", mode_sort) == 0); SELF_CHECK (cooked_index_entry::compare ("func", "func1", mode_sort) < 0); } } /* anonymous namespace */ #endif /* GDB_SELF_TEST */ /* See cooked-index-entry.h. */ bool cooked_index_entry::matches (domain_search_flags kind) const { /* Just reject type declarations. */ if ((flags & IS_TYPE_DECLARATION) != 0) return false; return tag_matches_domain (tag, kind, lang); } /* See cooked-index-entry.h. */ const char * cooked_index_entry::full_name (struct obstack *storage, cooked_index_full_name_flag name_flags, const char *default_sep) const { const char *local_name = ((name_flags & FOR_MAIN) != 0) ? name : canonical; if ((flags & IS_LINKAGE) != 0 || get_parent () == nullptr) return local_name; const char *sep = default_sep; switch (lang) { case language_cplus: case language_rust: case language_fortran: sep = "::"; break; case language_ada: /* If GNAT emits hierarchical names (patches not in at the time of writing), then we need to compute the linkage name here. However for traditional GNAT, the linkage name will be in 'name'. Detect this by looking for "__"; see also cooked_index_shard::finalize. */ if ((name_flags & FOR_ADA_LINKAGE_NAME) != 0) { if (strstr (name, "__") != nullptr) return name; sep = "__"; break; } [[fallthrough]]; case language_go: case language_d: sep = "."; break; default: if (sep == nullptr) return local_name; break; } /* The FOR_ADA_LINKAGE_NAME flag should only affect Ada entries, so disable it here if we don't need it. */ if (lang != language_ada) name_flags &= ~FOR_ADA_LINKAGE_NAME; get_parent ()->write_scope (storage, sep, name_flags); obstack_grow0 (storage, local_name, strlen (local_name)); return (const char *) obstack_finish (storage); } /* See cooked-index-entry.h. */ void cooked_index_entry::write_scope (struct obstack *storage, const char *sep, cooked_index_full_name_flag flags) const { if (get_parent () != nullptr) get_parent ()->write_scope (storage, sep, flags); /* When computing the Ada linkage name, the entry might not have been canonicalized yet, so use the 'name'. */ const char *local_name = ((flags & (FOR_MAIN | FOR_ADA_LINKAGE_NAME)) != 0 ? name : canonical); obstack_grow (storage, local_name, strlen (local_name)); obstack_grow (storage, sep, strlen (sep)); } INIT_GDB_FILE (dwarf2_entry) { #if GDB_SELF_TEST selftests::register_test ("cooked_index_entry::compare", test_compare); #endif }