From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by sourceware.org (Postfix) with ESMTPS id 41B19385802E for ; Wed, 31 Mar 2021 14:24:22 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 41B19385802E Received: by mail-wm1-x32e.google.com with SMTP id g20so10254135wmk.3 for ; Wed, 31 Mar 2021 07:24:22 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:content-transfer-encoding :in-reply-to; bh=4Qn9hH04FTLPG6sVSny19gE4y40DVvUMwxE0sPc4QGo=; b=Soq+4a2fNjcU4DWDcm23WDG0rKau1B6cdq5FuDbBGbC6f7GUjkCoMQQ+57W8U/gnVk de8Ge4xepQYiLIk4EjrRU4/Rw9c14NmbBEPdmo6H0cV4ucf3/YvT9sm3ZGZuCLCLpPXv T/rLe0rnJ7RiYPtgZE+UVE/Iyr+Bugi9olDpxaE4dDcj39cG3uuQf6lEhf82lxkIyJE5 OCGYUVfPWyrKdoYHR5m+4dOLnFhviZtgX9HaWfB6vYu5Shyev1RuapjdH01QZGdld2l6 /yLBYEB+KNIT1EF3eJ4y9+r6NxDcvJ/wRfuHiAsfCYGStJz8iAb85coSq+2ao5TJZUho s5eA== X-Gm-Message-State: AOAM532nXISPrjvNV+x9R2sOLQ/o6zwVSlOysqTG1uLKYuqnIPRqb0dT o9eocdmvlwe582UbBNzC4fjUzQ== X-Google-Smtp-Source: ABdhPJwqTLTftg2Bd4LNd62wUQHnUW5/zVShxXMn7LnRMOPFH0EKmbF+HVNTY1nHCVuNhwHHdUx5iQ== X-Received: by 2002:a1c:b783:: with SMTP id h125mr3452985wmf.106.1617200660827; Wed, 31 Mar 2021 07:24:20 -0700 (PDT) Received: from google.com ([2a00:79e0:d:210:4c16:3f7c:8607:d758]) by smtp.gmail.com with ESMTPSA id t1sm6147842wry.90.2021.03.31.07.24.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 31 Mar 2021 07:24:19 -0700 (PDT) Date: Wed, 31 Mar 2021 15:24:19 +0100 From: Matthias Maennich To: Giuliano Procida Cc: libabigail@sourceware.org, dodji@seketeli.org, kernel-team@android.com Subject: Re: [PATCH] XML reader: improve XML node traversal Message-ID: References: <20210331092352.619148-1-gprocida@google.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline Content-Transfer-Encoding: quoted-printable In-Reply-To: <20210331092352.619148-1-gprocida@google.com> X-Spam-Status: No, score=-30.0 required=5.0 tests=BAYES_00, DKIMWL_WL_MED, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, ENV_AND_HDR_SPF_MATCH, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, USER_IN_DEF_DKIM_WL, USER_IN_DEF_SPF_WL autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libabigail@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list of the Libabigail project List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 31 Mar 2021 14:24:26 -0000 On Wed, Mar 31, 2021 at 10:23:51AM +0100, Giuliano Procida wrote: >Bug 27616: libabigail treats empty and nearly empty XML elements inconsist= ently > >The XML reader uses libxml's Reader API for parsing XML and traverses >high-level XML nodes using reader cursor advancement. Low-level nodes >are read into complete node trees and are traversed following >children, next and (in one instance) parent pointers. Some >intermediate-level nodes can be parsed both ways. > >A lot of code cares about node types, typically skipping over >non-element nodes. Unfortunately, in a few places, the tracked "corpus >node" needs to land on text (whitespace) within elements for the XML >reader to work; stripping text nodes out causes some elements to be >skipped. Almost all node tree traversals are very straightforward and >can be expressed using simpler primitves than at present. > >This commit changes all the node tree traversals to use two node >iteration primitives consistently. These are "first_child" and >"next_child", both of which automatically skip text nodes. They >replace the existing "advance_to_next_sibling_element" and *all* >direct references to "children" and "next" nodes. The unwanted >dependency on text within elements has been fixed. > >For-loops constructed with the new primitives present only element >nodes to the loop body. This allows a lot of redundant checking for >null / non-element nodes to be removed. > >These changes have been tested by using abidiff to comparing all >current test XML files with copies where all text has been removed. >One such test case is included in this commit. > > * include/abg-libxml-utils.h > (advance_to_next_sibling_element): Remove this function. > (first_child): Add new function. (next_child): Likewise. This > is simpler than but functionally identical to > advance_to_next_sibling_element. > * src/abg-libxml-utils.cc (skip_non_elements): New function to > skip non-element nodes in a sibling node list. (first_child): > New function to find the first child element of a node. > (next_child): New function to find the next sibling element of > a node. > * src/abg-reader.cc (walk_xml_node_to_map_type_ids): Remove > redundant node checks. Iterate over children with first_child > and next_child helpers. (read_translation_unit): Likewise. > (read_translation_unit_from_input): Remove a no-longer-needed > ->next and redundant node checks. Advance "corpus node" after > reading the translation unit. (read_symbol_db_from_input): > Remove a no-longer-needed ->next and advance "corpus node" as > part of for-loop. Remove redundant node checks. > (build_needed): Remove redundant node checks. Iterate over > children with first_child and next_child. > (read_elf_needed_from_input): Remove redundant node checks and > simplify logic. Move to next element node after reading > elf-needed node. (read_corpus_from_input): Remove no-children > early return (which was inhibited by text nodes anyway). Set > initial "corpus node" using first_child instead of children > pointer. Remove redundant "corpus node" updates at end of > function as the caller takes care of this and the node can > become null with the new primitives. > (read_corpus_group_from_input): Iterate over children using > first_child and next child. Set "corpus node" for each corpus, > not just the first. Incidentally keep going even if one corpus > parse fails. (build_namespace_decl): Remove redundant node > checks. Iterate over children using first_child and > next_child. (build_elf_symbol): Remove redundant node checks. > (build_elf_symbol_db): Iterate over children using first_child > and next_child. (build_function_decl): Remove redundant node > checks. Iterate over children using first_child and > next_child. (build_function_type): Likewise. > (build_array_type_def): Likewise. (build_enum_type_decl): > Likewise. (build_class_decl): Likewise. (build_union_decl): > Likewise. (build_function_tdecl): Likewise. > (build_type_composition): Likewise. > (build_template_tparameter): Likewise. > (tests/data/Makefile.am): Add new test files. > (tests/data/test-abidiff-exit/test-squished-report.txt): Add > empty diff report. > (tests/data/test-abidiff-exit/test-squished-v0.abi): Add test > ABI file, containing all ELF elements. > (tests/data/test-abidiff-exit/test-squished-v1.abi): Likewise > and identical, except that all text has been stripped. > (tests/test-abidiff-exit.cc): Add new test case. > >Signed-off-by: Giuliano Procida That is a very nice simplification that comes with this fix! Thanks! Reviewed-by: Matthias Maennich Cheers, Matthias >--- > include/abg-libxml-utils.h | 5 +- > src/abg-libxml-utils.cc | 47 ++-- > src/abg-reader.cc | 208 +++++------------- > tests/data/Makefile.am | 3 + > .../test-squished-report.txt | 0 > .../test-abidiff-exit/test-squished-v0.abi | 43 ++++ > .../test-abidiff-exit/test-squished-v1.abi | 1 + > tests/test-abidiff-exit.cc | 11 + > 8 files changed, 136 insertions(+), 182 deletions(-) > create mode 100644 tests/data/test-abidiff-exit/test-squished-report.txt > create mode 100644 tests/data/test-abidiff-exit/test-squished-v0.abi > create mode 100644 tests/data/test-abidiff-exit/test-squished-v1.abi > >diff --git a/include/abg-libxml-utils.h b/include/abg-libxml-utils.h >index 69e940f6..f87ee5a0 100644 >--- a/include/abg-libxml-utils.h >+++ b/include/abg-libxml-utils.h >@@ -82,7 +82,10 @@ int get_xml_node_depth(xmlNodePtr); > reinterpret_cast(xml_char_str.get()) > > xmlNodePtr >-advance_to_next_sibling_element(xmlNodePtr node); >+first_child(xmlNodePtr node); >+ >+xmlNodePtr >+next_child(xmlNodePtr node); > > void > escape_xml_string(const std::string& str, >diff --git a/src/abg-libxml-utils.cc b/src/abg-libxml-utils.cc >index a1acff1f..baea25ac 100644 >--- a/src/abg-libxml-utils.cc >+++ b/src/abg-libxml-utils.cc >@@ -413,40 +413,39 @@ unescape_xml_comment(const std::string& str) > return result; > } > >-/// Maybe get the next sibling element node of an XML node, or stay to th= e sam >+/// Skip until we reach an XML element node or run out of (child) nodes. > /// >-/// If there is no next sibling xml element node, the function returns >-/// the initial node. >+/// @param a pointer to a node. > /// >-/// @param node the initial node to consider. >-/// >-/// @return the next sibling node or the initial node @p node. >-static xmlNodePtr >-go_to_next_sibling_element_or_stay(xmlNodePtr node) >+/// @return a pointer to an XML element node or nil. >+xmlNodePtr >+skip_non_elements(xmlNodePtr node) > { >- xmlNodePtr n; >- for (n =3D node; n; n =3D n->next) >- { >- if (n->type =3D=3D XML_ELEMENT_NODE) >- break; >- } >- return n ? n : node; >+ while (node && node->type !=3D XML_ELEMENT_NODE) >+ node =3D node->next; >+ return node; > } > >-/// Get the next sibling element node of an XML node. >+/// Get the first child element of an XML node. > /// >-/// If there is no next sibling xml element node, the function returns ni= l. >+/// @param a pointer to the node whose children are to be perused. > /// >-/// @param node the XML node to consider. >+/// @return a pointer to the first XML element child of @p node or nil. >+xmlNodePtr >+first_child(xmlNodePtr node) >+{ >+ return skip_non_elements(node->children); >+} >+ >+/// Get the next sibling element of an XML node. > /// >-/// @return the next sibling element node or nil. >+/// @param a pointer to the XML node whose following siblings are to be p= erused. >+/// >+/// @return a pointer to the next sibling element of @p node or nil. > xmlNodePtr >-advance_to_next_sibling_element(xmlNodePtr node) >+next_child(xmlNodePtr node) > { >- xmlNodePtr n =3D go_to_next_sibling_element_or_stay(node->next); >- if (n =3D=3D 0 || n->type !=3D XML_ELEMENT_NODE) >- return 0; >- return n; >+ return skip_non_elements(node->next); > } > > }//end namespace xml >diff --git a/src/abg-reader.cc b/src/abg-reader.cc >index 3e552864..a143eb58 100644 >--- a/src/abg-reader.cc >+++ b/src/abg-reader.cc >@@ -1343,18 +1343,13 @@ static void > walk_xml_node_to_map_type_ids(read_context& ctxt, > xmlNodePtr node) > { >- xmlNodePtr n =3D node; >- >- if (!n || n->type !=3D XML_ELEMENT_NODE) >- return; >- >- if (xml_char_sptr s =3D XML_NODE_GET_ATTRIBUTE(n, "id")) >+ if (xml_char_sptr s =3D XML_NODE_GET_ATTRIBUTE(node, "id")) > { > string id =3D CHAR_STR(s); >- ctxt.map_id_and_node(id, n); >+ ctxt.map_id_and_node(id, node); > } > >- for (n =3D n->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > walk_xml_node_to_map_type_ids(ctxt, n); > } > >@@ -1396,12 +1391,8 @@ read_translation_unit(read_context& ctxt, translati= on_unit& tu, xmlNodePtr node) > || !ctxt.get_corpus()) > walk_xml_node_to_map_type_ids(ctxt, node); > >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >- { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- handle_element_node(ctxt, n, /*add_decl_to_scope=3D*/true); >- } >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) >+ handle_element_node(ctxt, n, /*add_decl_to_scope=3D*/true); > > ctxt.pop_scope_or_abort(tu.get_global_scope()); > >@@ -1495,16 +1486,9 @@ read_translation_unit_from_input(read_context& ctxt) > else > { > node =3D 0; >- for (xmlNodePtr n =3D ctxt.get_corpus_node()->next; n; n =3D n->nex= t) >- { >- if (!n >- || n->type !=3D XML_ELEMENT_NODE) >- continue; >- if (!xmlStrEqual(n->name, BAD_CAST("abi-instr"))) >- return nil; >- node =3D n; >- break; >- } >+ xmlNodePtr n =3D ctxt.get_corpus_node(); >+ if (n && xmlStrEqual(n->name, BAD_CAST("abi-instr"))) >+ node =3D n; > } > > if (node =3D=3D 0) >@@ -1517,9 +1501,9 @@ read_translation_unit_from_input(read_context& ctxt) > // case, after that unexpected call to read_translation_unit(), the > // current corpus node of the context is going to point to that > // translation unit that has been read under the hood. Let's set >- // the corpus node to the one we initially called >+ // the corpus node to just after the one we initially called > // read_translation_unit() on here. >- ctxt.set_corpus_node(node); >+ ctxt.set_corpus_node(xml::next_child(node)); > return tu; > } > >@@ -1593,11 +1577,8 @@ read_symbol_db_from_input(read_context& ctxt, > xmlTextReaderNext(reader.get()); > } > else >- for (xmlNodePtr n =3D ctxt.get_corpus_node()->next; n; n =3D n->next) >+ for (xmlNodePtr n =3D ctxt.get_corpus_node(); n; n =3D xml::next_chil= d(n), ctxt.set_corpus_node(n)) That line looks a little long. > { >- if (!n || n->type !=3D XML_ELEMENT_NODE) >- continue; >- > bool has_fn_syms =3D false, has_var_syms =3D false; > if (xmlStrEqual(n->name, BAD_CAST("elf-function-symbols"))) > has_fn_syms =3D true; >@@ -1605,7 +1586,6 @@ read_symbol_db_from_input(read_context& ctxt, > has_var_syms =3D true; > else > break; >- ctxt.set_corpus_node(n); > if (has_fn_syms) > { > fn_symdb =3D build_elf_symbol_db(ctxt, n, true); >@@ -1636,16 +1616,12 @@ read_symbol_db_from_input(read_context& ctxt, > static bool > build_needed(xmlNode* node, vector& needed) > { >- if (!node) >- return false; >- >- if (!node || !xmlStrEqual(node->name,BAD_CAST("elf-needed"))) >+ if (!xmlStrEqual(node->name,BAD_CAST("elf-needed"))) > return false; > >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE >- || !xmlStrEqual(n->name, BAD_CAST("dependency"))) >+ if (!xmlStrEqual(n->name, BAD_CAST("dependency"))) > continue; > > string name; >@@ -1693,27 +1669,19 @@ read_elf_needed_from_input(read_context& ctxt, > return false; > > node =3D xmlTextReaderExpand(reader.get()); >- if (!node) >- return false; > } > else > { >- for (xmlNodePtr n =3D ctxt.get_corpus_node()->next; n; n =3D n->nex= t) >- { >- if (!n || n->type !=3D XML_ELEMENT_NODE) >- continue; >- if (!xmlStrEqual(n->name, BAD_CAST("elf-needed"))) >- return false; >- node =3D n; >- break; >- } >+ xmlNodePtr n =3D ctxt.get_corpus_node(); >+ if (n && xmlStrEqual(n->name, BAD_CAST("elf-needed"))) >+ node =3D n; > } > > bool result =3D false; > if (node) > { > result =3D build_needed(node, needed); >- ctxt.set_corpus_node(node); >+ ctxt.set_corpus_node(xml::next_child(node)); > } > > return result; >@@ -1917,10 +1885,7 @@ read_corpus_from_input(read_context& ctxt) > corp.set_soname(reinterpret_cast(soname_str.get())); > } > >- if (!node->children) >- return nil; >- >- ctxt.set_corpus_node(node->children); >+ ctxt.set_corpus_node(xml::first_child(node)); > > corpus& corp =3D *ctxt.get_corpus(); > >@@ -1986,17 +1951,6 @@ read_corpus_from_input(read_context& ctxt) > // call at the beginning of the function. > xmlTextReaderNext(reader.get()); > } >- else >- { >- node =3D ctxt.get_corpus_node(); >- node =3D xml::advance_to_next_sibling_element(node); >- if (!node) >- { >- node =3D ctxt.get_corpus_node(); >- node =3D xml::advance_to_next_sibling_element(node->parent); >- } >- ctxt.set_corpus_node(node); >- } > > return ctxt.get_corpus(); > } >@@ -2046,13 +2000,13 @@ read_corpus_group_from_input(read_context& ctxt) > if (!node) > return nil; > >- //node =3D xml::get_first_element_sibling_if_text(node->children); >- node =3D xml::advance_to_next_sibling_element(node->children); >- ctxt.set_corpus_node(node); >- >- corpus_sptr corp; >- while ((corp =3D read_corpus_from_input(ctxt))) >- ctxt.get_corpus_group()->add_corpus(corp); >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) >+ { >+ ctxt.set_corpus_node(n); >+ corpus_sptr corp =3D read_corpus_from_input(ctxt); >+ if (corp) >+ ctxt.get_corpus_group()->add_corpus(corp); >+ } > > xmlTextReaderNext(reader.get()); > >@@ -2738,12 +2692,8 @@ build_namespace_decl(read_context& ctxt, > ctxt.push_decl_to_current_scope(decl, add_to_current_scope); > ctxt.map_xml_node_to_decl(node, decl); > >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >- { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- handle_element_node(ctxt, n, /*add_to_current_scope=3D*/true); >- } >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) >+ handle_element_node(ctxt, n, /*add_to_current_scope=3D*/true); > > ctxt.pop_scope_or_abort(decl); > >@@ -2767,9 +2717,7 @@ build_elf_symbol(read_context& ctxt, const xmlNodePt= r node, > { > elf_symbol_sptr nil; > >- if (!node >- || node->type !=3D XML_ELEMENT_NODE >- || !xmlStrEqual(node->name, BAD_CAST("elf-symbol"))) >+ if (!xmlStrEqual(node->name, BAD_CAST("elf-symbol"))) > return nil; > > string name; >@@ -2928,7 +2876,7 @@ build_elf_symbol_db(read_context& ctxt, > xml_node_ptr_elf_symbol_sptr_map_type xml_node_ptr_elf_symbol_map; > > elf_symbol_sptr sym; >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { > if ((sym =3D build_elf_symbol(ctxt, n, /*drop_if_suppress=3D*/true)= )) > { >@@ -3094,12 +3042,9 @@ build_function_decl(read_context& ctxt, > std::vector parms; > type_base_sptr return_type =3D env->get_void_type(); > >- for (xmlNodePtr n =3D node->children; n ; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- >- else if (xmlStrEqual(n->name, BAD_CAST("parameter"))) >+ if (xmlStrEqual(n->name, BAD_CAST("parameter"))) > { > if (function_decl::parameter_sptr p =3D > build_function_parameter(ctxt, n)) >@@ -3789,12 +3734,9 @@ build_function_type(read_context& ctxt, > ctxt.get_translation_unit()->bind_function_type_life_time(fn_type); > ctxt.key_type_decl(fn_type, id); > >- for (xmlNodePtr n =3D node->children; n ; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- >- else if (xmlStrEqual(n->name, BAD_CAST("parameter"))) >+ if (xmlStrEqual(n->name, BAD_CAST("parameter"))) > { > if (function_decl::parameter_sptr p =3D > build_function_parameter(ctxt, n)) >@@ -4025,12 +3967,9 @@ build_array_type_def(read_context& ctxt, > read_location(ctxt, node, loc); > array_type_def::subranges_type subranges; > >- for (xmlNodePtr n =3D node->children; n ; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- >- else if (xmlStrEqual(n->name, BAD_CAST("subrange"))) >+ if (xmlStrEqual(n->name, BAD_CAST("subrange"))) > { > if (array_type_def::subrange_sptr s =3D > build_subrange_type(ctxt, n)) >@@ -4183,11 +4122,8 @@ build_enum_type_decl(read_context& ctxt, > > string base_type_id; > enum_type_decl::enumerators enums; >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (xmlStrEqual(n->name, BAD_CAST("underlying-type"))) > { > xml_char_sptr a =3D xml::build_sptr(xmlGetProp(n, BAD_CAST("type-id"))= ); >@@ -4554,11 +4490,8 @@ build_class_decl(read_context& ctxt, > decl->set_naming_typedef(naming_typedef); > } > >- for (xmlNodePtr n =3D node->children; !is_decl_only && n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); !is_decl_only && n; n =3D= xml::next_child(n)) Likewise. > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (xmlStrEqual(n->name, BAD_CAST("base-class"))) > { > access_specifier access =3D >@@ -4605,11 +4538,8 @@ build_class_decl(read_context& ctxt, > > ctxt.map_xml_node_to_decl(n, decl); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (type_base_sptr t =3D > build_type(ctxt, p, /*add_to_current_scope=3D*/true)) > { >@@ -4643,11 +4573,8 @@ build_class_decl(read_context& ctxt, > bool is_static =3D false; > read_static(n, is_static); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (var_decl_sptr v =3D > build_var_decl(ctxt, p, /*add_to_cur_scope=3D*/false)) > { >@@ -4701,11 +4628,8 @@ build_class_decl(read_context& ctxt, > bool is_ctor =3D false, is_dtor =3D false, is_const =3D false; > read_cdtor_const(n, is_ctor, is_dtor, is_const); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (function_decl_sptr f =3D > build_function_decl_if_not_suppressed(ctxt, p, decl, > /*add_to_cur_sc=3D*/true)) >@@ -4740,11 +4664,8 @@ build_class_decl(read_context& ctxt, > bool is_ctor =3D false, is_dtor =3D false, is_const =3D false; > read_cdtor_const(n, is_ctor, is_dtor, is_const); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (shared_ptr f =3D > build_function_tdecl(ctxt, p, > /*add_to_current_scope=3D*/true)) >@@ -4954,11 +4875,8 @@ build_union_decl(read_context& ctxt, > ctxt.map_xml_node_to_decl(node, decl); > ctxt.key_type_decl(decl, id); > >- for (xmlNodePtr n =3D node->children; !is_decl_only && n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); !is_decl_only && n; n =3D= xml::next_child(n)) Likewise. > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (xmlStrEqual(n->name, BAD_CAST("member-type"))) > { > access_specifier access =3D private_access; >@@ -4966,11 +4884,8 @@ build_union_decl(read_context& ctxt, > > ctxt.map_xml_node_to_decl(n, decl); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (type_base_sptr t =3D > build_type(ctxt, p, /*add_to_current_scope=3D*/true)) > { >@@ -4998,11 +4913,8 @@ build_union_decl(read_context& ctxt, > bool is_static =3D false; > read_static(n, is_static); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (var_decl_sptr v =3D > build_var_decl(ctxt, p, /*add_to_cur_scope=3D*/false)) > { >@@ -5040,11 +4952,8 @@ build_union_decl(read_context& ctxt, > bool is_ctor =3D false, is_dtor =3D false, is_const =3D false; > read_cdtor_const(n, is_ctor, is_dtor, is_const); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (function_decl_sptr f =3D > build_function_decl_if_not_suppressed(ctxt, p, decl, > /*add_to_cur_sc=3D*/true)) >@@ -5073,11 +4982,8 @@ build_union_decl(read_context& ctxt, > bool is_ctor =3D false, is_dtor =3D false, is_const =3D false; > read_cdtor_const(n, is_ctor, is_dtor, is_const); > >- for (xmlNodePtr p =3D n->children; p; p =3D p->next) >+ for (xmlNodePtr p =3D xml::first_child(n); p; p =3D xml::next_child(p)) > { >- if (p->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (function_tdecl_sptr f =3D > build_function_tdecl(ctxt, p, > /*add_to_current_scope=3D*/true)) >@@ -5152,11 +5058,8 @@ build_function_tdecl(read_context& ctxt, > ctxt.push_decl_to_current_scope(fn_tmpl_decl, add_to_current_scope); > > unsigned parm_index =3D 0; >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (template_parameter_sptr parm =3D > build_template_parameter(ctxt, n, parm_index, fn_tmpl_decl)) > { >@@ -5216,11 +5119,8 @@ build_class_tdecl(read_context& ctxt, > ctxt.push_decl_to_current_scope(class_tmpl, add_to_current_scope); > > unsigned parm_index =3D 0; >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (template_parameter_sptr parm=3D > build_template_parameter(ctxt, n, parm_index, class_tmpl)) > { >@@ -5335,11 +5235,8 @@ build_type_composition(read_context& ctxt, > ctxt.push_decl_to_current_scope(dynamic_pointer_cast(result), > /*add_to_current_scope=3D*/true); > >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- > if ((composed_type =3D > build_pointer_type_def(ctxt, n, > /*add_to_current_scope=3D*/true)) >@@ -5465,11 +5362,8 @@ build_template_tparameter(read_context& ctxt, > > // Go parse template parameters that are children nodes > int parm_index =3D 0; >- for (xmlNodePtr n =3D node->children; n; n =3D n->next) >+ for (xmlNodePtr n =3D xml::first_child(node); n; n =3D xml::next_child(= n)) > { >- if (n->type !=3D XML_ELEMENT_NODE) >- continue; >- > if (shared_ptr p =3D > build_template_parameter(ctxt, n, parm_index, result)) > { >diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am >index 4fb2d6a4..7bdae7bf 100644 >--- a/tests/data/Makefile.am >+++ b/tests/data/Makefile.am >@@ -197,6 +197,9 @@ test-abidiff-exit/test-non-leaf-array-v0.o \ > test-abidiff-exit/test-non-leaf-array-v1.c \ > test-abidiff-exit/test-non-leaf-array-v1.o \ > test-abidiff-exit/test-non-leaf-array-report.txt \ >+test-abidiff-exit/test-squished-v0.abi \ >+test-abidiff-exit/test-squished-v1.abi \ >+test-abidiff-exit/test-squished-report.txt \ > \ > test-diff-dwarf/test0-v0.cc \ > test-diff-dwarf/test0-v0.o \ >diff --git a/tests/data/test-abidiff-exit/test-squished-report.txt b/tests= /data/test-abidiff-exit/test-squished-report.txt >new file mode 100644 >index 00000000..e69de29b >diff --git a/tests/data/test-abidiff-exit/test-squished-v0.abi b/tests/dat= a/test-abidiff-exit/test-squished-v0.abi >new file mode 100644 >index 00000000..6b3d0460 >--- /dev/null >+++ b/tests/data/test-abidiff-exit/test-squished-v0.abi >@@ -0,0 +1,43 @@ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >+ >diff --git a/tests/data/test-abidiff-exit/test-squished-v1.abi b/tests/dat= a/test-abidiff-exit/test-squished-v1.abi >new file mode 100644 >index 00000000..3ffa272b >--- /dev/null >+++ b/tests/data/test-abidiff-exit/test-squished-v1.abi >@@ -0,0 +1 @@ >+= <= /elf-function-symbols>= >\ No newline at end of file >diff --git a/tests/test-abidiff-exit.cc b/tests/test-abidiff-exit.cc >index 4283e145..f1526b9e 100644 >--- a/tests/test-abidiff-exit.cc >+++ b/tests/test-abidiff-exit.cc >@@ -392,6 +392,17 @@ InOutSpec in_out_specs[] =3D > "data/test-abidiff-exit/test-non-leaf-array-report.txt", > "output/test-abidiff-exit/test-non-leaf-array-report.txt" > }, >+ { >+ "data/test-abidiff-exit/test-squished-v0.abi", >+ "data/test-abidiff-exit/test-squished-v1.abi", >+ "", >+ "", >+ "", >+ "--harmless", >+ abigail::tools_utils::ABIDIFF_OK, >+ "data/test-abidiff-exit/test-squished-report.txt", >+ "output/test-abidiff-exit/test-squished-report.txt" >+ }, > {0, 0, 0 ,0, 0, 0, abigail::tools_utils::ABIDIFF_OK, 0, 0} > }; > >--=20 >2.31.0.291.g576ba9dcdaf-goog >