• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2023 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 /// @file
9 ///
10 /// This file contains definitions of diff objects filtering
11 /// facilities.
12 
13 #include "abg-internal.h"
14 #include <memory>
15 // <headers defining libabigail's API go under here>
16 ABG_BEGIN_EXPORT_DECLARATIONS
17 
18 #include "abg-comp-filter.h"
19 #include "abg-tools-utils.h"
20 
21 ABG_END_EXPORT_DECLARATIONS
22 // </headers defining libabigail's API>
23 
24 namespace abigail
25 {
26 namespace comparison
27 {
28 namespace filtering
29 {
30 
31 using std::dynamic_pointer_cast;
32 
33 /// Walk the diff sub-trees of a a @ref corpus_diff and apply a filter
34 /// to the nodes visted.  The filter categorizes each node, assigning
35 /// it into one or several categories.
36 ///
37 /// @param filter the filter to apply to the diff nodes
38 ///
39 /// @param d the corpus diff to apply the filter to.
40 void
apply_filter(filter_base & filter,corpus_diff_sptr d)41 apply_filter(filter_base& filter, corpus_diff_sptr d)
42 {
43   bool s = d->context()->visiting_a_node_twice_is_forbidden();
44   d->context()->forbid_visiting_a_node_twice(true);
45   d->traverse(filter);
46   d->context()->forbid_visiting_a_node_twice(s);
47 }
48 
49 /// Walk a diff sub-tree and apply a filter to the nodes visted.  The
50 /// filter categorizes each node, assigning it into one or several
51 /// categories.
52 ///
53 /// Note that this function makes sure to avoid visiting a node (or
54 /// any other node equivalent to it) more than once.  This helps avoid
55 /// infinite loops for diff trees that involve type changes that
56 /// reference themselves.
57 ///
58 /// @param filter the filter to apply to the nodes of the sub-tree.
59 ///
60 /// @param d the diff sub-tree to walk and apply the filter to.
61 void
apply_filter(filter_base & filter,diff_sptr d)62 apply_filter(filter_base& filter, diff_sptr d)
63 {
64   bool s = d->context()->visiting_a_node_twice_is_forbidden();
65   d->context()->forbid_visiting_a_node_twice(true);
66   d->context()->forget_visited_diffs();
67   d->traverse(filter);
68   d->context()->forbid_visiting_a_node_twice(s);
69 }
70 
71 /// Walk a diff sub-tree and apply a filter to the nodes visted.  The
72 /// filter categorizes each node, assigning it into one or several
73 /// categories.
74 ///
75 /// Note that this function makes sure to avoid visiting a node (or
76 /// any other node equivalent to it) more than once.  This helps avoid
77 /// infinite loops for diff trees that involve type changes that
78 /// reference themselves.
79 ///
80 /// @param filter the filter to apply to the nodes of the sub-tree.
81 ///
82 /// @param d the diff sub-tree to walk and apply the filter to.
83 void
apply_filter(filter_base_sptr filter,diff_sptr d)84 apply_filter(filter_base_sptr filter, diff_sptr d)
85 {apply_filter(*filter, d);}
86 
87 /// Test if there is a class that is declaration-only among the two
88 /// classes in parameter.
89 ///
90 /// @param class1 the first class to consider.
91 ///
92 /// @param class2 the second class to consider.
93 ///
94 /// @return true if either classes are declaration-only, false
95 /// otherwise.
96 static bool
there_is_a_decl_only_class(const class_decl_sptr & class1,const class_decl_sptr & class2)97 there_is_a_decl_only_class(const class_decl_sptr& class1,
98 			   const class_decl_sptr& class2)
99 {
100   if ((class1 && class1->get_is_declaration_only())
101       || (class2 && class2->get_is_declaration_only()))
102     return true;
103   return false;
104 }
105 
106 /// Test if there is a enum that is declaration-only among the two
107 /// enums in parameter.
108 ///
109 /// @param enum1 the first enum to consider.
110 ///
111 /// @param enum2 the second enum to consider.
112 ///
113 /// @return true if either enums are declaration-only, false
114 /// otherwise.
115 static bool
there_is_a_decl_only_enum(const enum_type_decl_sptr & enum1,const enum_type_decl_sptr & enum2)116 there_is_a_decl_only_enum(const enum_type_decl_sptr& enum1,
117 			  const enum_type_decl_sptr& enum2)
118 {
119   if ((enum1 && enum1->get_is_declaration_only())
120       || (enum2 && enum2->get_is_declaration_only()))
121     return true;
122   return false;
123 }
124 
125 /// Test if the diff involves a declaration-only class.
126 ///
127 /// @param diff the class diff to consider.
128 ///
129 /// @return true iff the diff involves a declaration-only class.
130 static bool
diff_involves_decl_only_class(const class_diff * diff)131 diff_involves_decl_only_class(const class_diff* diff)
132 {
133   if (diff && there_is_a_decl_only_class(diff->first_class_decl(),
134 					 diff->second_class_decl()))
135     return true;
136   return false;
137 }
138 
139 /// Tests if the size of a given type changed.
140 ///
141 /// @param f the first version of the type to consider.
142 ///
143 /// @param s the second version of the type to consider.
144 ///
145 /// @return true if the type size changed, false otherwise.
146 static bool
type_size_changed(const type_base_sptr f,const type_base_sptr s)147 type_size_changed(const type_base_sptr f, const type_base_sptr s)
148 {
149   if (!f || !s
150       || f->get_size_in_bits() == 0
151       || s->get_size_in_bits() == 0
152       || there_is_a_decl_only_class(is_compatible_with_class_type(f),
153 				    is_compatible_with_class_type(s))
154       || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
155 				   is_compatible_with_enum_type(s)))
156     return false;
157 
158   return f->get_size_in_bits() != s->get_size_in_bits();
159 }
160 
161 /// Tests if the size of a given type changed.
162 ///
163 /// @param f the declaration of the first version of the type to
164 /// consider.
165 ///
166 /// @param s the declaration of the second version of the type to
167 /// consider.
168 ///
169 /// @return true if the type size changed, false otherwise.
170 static bool
type_size_changed(const decl_base_sptr f,const decl_base_sptr s)171 type_size_changed(const decl_base_sptr f, const decl_base_sptr s)
172 {return type_size_changed(is_type(f), is_type(s));}
173 
174 /// Test if a given type diff node carries a type size change.
175 ///
176 /// @param diff the diff tree node to test.
177 ///
178 /// @return true if @p diff carries a type size change.
179 static bool
has_type_size_change(const diff * diff)180 has_type_size_change(const diff* diff)
181 {
182   if (!diff)
183     return false;
184 
185   if (const fn_parm_diff* fn_parm_d = is_fn_parm_diff(diff))
186     diff = fn_parm_d->type_diff().get();
187 
188   type_base_sptr f = is_type(diff->first_subject()),
189     s = is_type(diff->second_subject());
190 
191   if (!f || !s)
192     return false;
193 
194   return type_size_changed(f, s);
195 }
196 /// Tests if the access specifiers for a member declaration changed.
197 ///
198 /// @param f the declaration for the first version of the member
199 /// declaration to consider.
200 ///
201 /// @param s the declaration for the second version of the member
202 /// delcaration to consider.
203 ///
204 /// @return true iff the access specifier changed.
205 static bool
access_changed(const decl_base_sptr & f,const decl_base_sptr & s)206 access_changed(const decl_base_sptr& f, const decl_base_sptr& s)
207 {
208   if (!is_member_decl(f)
209       || !is_member_decl(s))
210     return false;
211 
212   access_specifier fa = get_member_access_specifier(f),
213     sa = get_member_access_specifier(s);
214 
215   if (sa != fa)
216     return true;
217 
218   return false;
219 }
220 
221 /// Test if there was a function or variable CRC change.
222 ///
223 /// @param f the first function or variable to consider.
224 ///
225 /// @param s the second function or variable to consider.
226 ///
227 /// @return true if the test is positive, false otherwise.
228 template <typename function_or_var_decl_sptr>
229 static bool
crc_changed(const function_or_var_decl_sptr & f,const function_or_var_decl_sptr & s)230 crc_changed(const function_or_var_decl_sptr& f,
231 	    const function_or_var_decl_sptr& s)
232 {
233   const auto& symbol_f = f->get_symbol();
234   const auto& symbol_s = s->get_symbol();
235   if (!symbol_f || !symbol_s)
236     return false;
237   return symbol_f->get_crc() != symbol_s->get_crc();
238 }
239 
240 /// Test if the current diff tree node carries a CRC change in either a
241 /// function or a variable.
242 ///
243 /// @param diff the diff tree node to consider.
244 ///
245 /// @return true if the test is positive, false otherwise.
246 static bool
crc_changed(const diff * diff)247 crc_changed(const diff* diff)
248 {
249   if (const function_decl_diff* d =
250 	dynamic_cast<const function_decl_diff*>(diff))
251     return crc_changed(d->first_function_decl(), d->second_function_decl());
252   if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
253     return crc_changed(d->first_var(), d->second_var());
254   return false;
255 }
256 
257 /// Test if there was a function or variable namespace change.
258 ///
259 /// @param f the first function or variable to consider.
260 ///
261 /// @param s the second function or variable to consider.
262 ///
263 /// @return true if the test is positive, false otherwise.
264 template <typename function_or_var_decl_sptr>
265 static bool
namespace_changed(const function_or_var_decl_sptr & f,const function_or_var_decl_sptr & s)266 namespace_changed(const function_or_var_decl_sptr& f,
267 		  const function_or_var_decl_sptr& s)
268 {
269   const auto& symbol_f = f->get_symbol();
270   const auto& symbol_s = s->get_symbol();
271   if (!symbol_f || !symbol_s)
272     return false;
273   return symbol_f->get_namespace() != symbol_s->get_namespace();
274 }
275 
276 /// Test if the current diff tree node carries a namespace change in
277 /// either a function or a variable.
278 ///
279 /// @param diff the diff tree node to consider.
280 ///
281 /// @return true if the test is positive, false otherwise.
282 static bool
namespace_changed(const diff * diff)283 namespace_changed(const diff* diff)
284 {
285   if (const function_decl_diff* d =
286 	dynamic_cast<const function_decl_diff*>(diff))
287     return namespace_changed(d->first_function_decl(),
288 			     d->second_function_decl());
289   if (const var_diff* d = dynamic_cast<const var_diff*>(diff))
290     return namespace_changed(d->first_var(), d->second_var());
291   return false;
292 }
293 
294 /// Test if there was a function name change, but there there was no
295 /// change in name of the underlying symbol.  IOW, if the name of a
296 /// function changed, but the symbol of the new function is equal to
297 /// the symbol of the old one, or is equal to an alians of the symbol
298 /// of the old function.
299 ///
300 /// @param f the first function to consider.
301 ///
302 /// @param s the second function to consider.
303 ///
304 /// @return true if the test is positive, false otherwise.
305 static bool
function_name_changed_but_not_symbol(const function_decl_sptr & f,const function_decl_sptr & s)306 function_name_changed_but_not_symbol(const function_decl_sptr& f,
307 				     const function_decl_sptr& s)
308 {
309   if (!f || !s)
310     return false;
311   string fn = f->get_qualified_name(),
312     sn = s->get_qualified_name();
313 
314   if (fn != sn)
315     {
316       elf_symbol_sptr fs = f->get_symbol(), ss = s->get_symbol();
317       if (fs == ss)
318 	return true;
319       if (!!fs != !!ss)
320 	return false;
321       for (elf_symbol_sptr s = fs->get_next_alias();
322 	   s && !s->is_main_symbol();
323 	   s = s->get_next_alias())
324 	if (*s == *ss)
325 	  return true;
326     }
327   return false;
328 }
329 
330 /// Test if the current diff tree node carries a function name change,
331 /// in which there there was no change in the name of the underlying
332 /// symbol.  IOW, if the name of a function changed, but the symbol of
333 /// the new function is equal to the symbol of the old one, or is
334 /// equal to an alians of the symbol of the old function.
335 ///
336 /// @param diff the diff tree node to consider.
337 ///
338 /// @return true if the test is positive, false otherwise.
339 static bool
function_name_changed_but_not_symbol(const diff * diff)340 function_name_changed_but_not_symbol(const diff* diff)
341 {
342   if (const function_decl_diff* d =
343       dynamic_cast<const function_decl_diff*>(diff))
344     return function_name_changed_but_not_symbol(d->first_function_decl(),
345 						d->second_function_decl());
346   return false;
347 }
348 
349 /// Tests if the offset of a given data member changed.
350 ///
351 /// @param f the declaration for the first version of the data member to
352 /// consider.
353 ///
354 /// @param s the declaration for the second version of the data member
355 /// to consider.
356 ///
357 /// @return true iff the offset of the data member changed.
358 static bool
data_member_offset_changed(decl_base_sptr f,decl_base_sptr s)359 data_member_offset_changed(decl_base_sptr f, decl_base_sptr s)
360 {
361   if (!is_member_decl(f)
362       || !is_member_decl(s))
363     return false;
364 
365   var_decl_sptr v0 = dynamic_pointer_cast<var_decl>(f),
366     v1 = dynamic_pointer_cast<var_decl>(s);
367   if (!v0 || !v1)
368     return false;
369 
370   if (get_data_member_offset(v0) != get_data_member_offset(v1))
371     return true;
372 
373   return false;
374 }
375 
376 /// Test if the size of a non-static data member changed accross two
377 /// versions.
378 ///
379 /// @param f the first version of the non-static data member.
380 ///
381 /// @param s the second version of the non-static data member.
382 static bool
non_static_data_member_type_size_changed(const decl_base_sptr & f,const decl_base_sptr & s)383 non_static_data_member_type_size_changed(const decl_base_sptr& f,
384 					 const decl_base_sptr& s)
385 {
386   if (!is_member_decl(f)
387       || !is_member_decl(s))
388     return false;
389 
390   var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
391     sv = dynamic_pointer_cast<var_decl>(s);
392   if (!fv
393       || !sv
394       || get_member_is_static(fv)
395       || get_member_is_static(sv))
396     return false;
397 
398   return type_size_changed(fv->get_type(), sv->get_type());
399 }
400 
401 /// Test if the size of a static data member changed accross two
402 /// versions.
403 ///
404 /// @param f the first version of the static data member.
405 ///
406 /// @param s the second version of the static data member.
407 static bool
static_data_member_type_size_changed(const decl_base_sptr & f,const decl_base_sptr & s)408 static_data_member_type_size_changed(const decl_base_sptr& f,
409 				     const decl_base_sptr& s)
410 {
411   if (!is_member_decl(f)
412       || !is_member_decl(s))
413     return false;
414 
415   var_decl_sptr fv = dynamic_pointer_cast<var_decl>(f),
416     sv = dynamic_pointer_cast<var_decl>(s);
417   if (!fv
418       || !sv
419       || !get_member_is_static(fv)
420       || !get_member_is_static(sv))
421     return false;
422 
423   return type_size_changed(fv->get_type(), sv->get_type());
424 }
425 
426 /// Test if two types are different but compatible.
427 ///
428 /// @param d1 the declaration of the first type to consider.
429 ///
430 /// @param d2 the declaration of the second type to consider.
431 ///
432 /// @return true if d1 and d2 are different but compatible.
433 static bool
is_compatible_change(const decl_base_sptr & d1,const decl_base_sptr & d2)434 is_compatible_change(const decl_base_sptr& d1, const decl_base_sptr& d2)
435 {
436   if ((d1 && d2)
437       && (d1 != d2)
438       && types_are_compatible(d1, d2))
439     return true;
440   return false;
441 }
442 
443 /// Test if two decls have different names.
444 ///
445 /// @param d1 the first declaration to consider.
446 ///
447 /// @param d2 the second declaration to consider.
448 ///
449 /// @return true if d1 and d2 have different names.
450 static bool
decl_name_changed(const type_or_decl_base * a1,const type_or_decl_base * a2)451 decl_name_changed(const type_or_decl_base* a1, const type_or_decl_base *a2)
452 {
453   string d1_name, d2_name;
454 
455   const decl_base *d1 = dynamic_cast<const decl_base*>(a1);
456   if (d1 == 0)
457     return false;
458 
459   const decl_base *d2 = dynamic_cast<const decl_base*>(a2);
460   if (d2 == 0)
461     return false;
462 
463   if (d1)
464     d1_name = d1->get_qualified_name();
465   if (d2)
466     d2_name = d2->get_qualified_name();
467 
468   return d1_name != d2_name;
469 }
470 
471 /// Test if two decls have different names.
472 ///
473 /// @param d1 the first declaration to consider.
474 ///
475 /// @param d2 the second declaration to consider.
476 ///
477 /// @return true if d1 and d2 have different names.
478 static bool
decl_name_changed(const type_or_decl_base_sptr & d1,const type_or_decl_base_sptr & d2)479 decl_name_changed(const type_or_decl_base_sptr& d1,
480 		  const type_or_decl_base_sptr& d2)
481 {return decl_name_changed(d1.get(), d2.get());}
482 
483 /// Test if a diff nodes carries a changes in which two decls have
484 /// different names.
485 ///
486 /// @param d the diff node to consider.
487 ///
488 /// @return true iff d carries a changes in which two decls have
489 /// different names.
490 static bool
decl_name_changed(const diff * d)491 decl_name_changed(const diff *d)
492 {return decl_name_changed(d->first_subject(), d->second_subject());}
493 
494 /// Test if two decls represents a harmless name change.
495 ///
496 /// For now, a harmless name change is considered only for a typedef,
497 /// enum or a data member.
498 ///
499 /// @param f the first decl to consider in the comparison.
500 ///
501 /// @param s the second decl to consider in the comparison.
502 ///
503 /// @return true iff decl @p s represents a harmless change over @p f.
504 bool
has_harmless_name_change(const decl_base_sptr & f,const decl_base_sptr & s)505 has_harmless_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
506 {
507   // So, a harmless name change is either ...
508   return (decl_name_changed(f, s)
509 	  && (// ... an anonymous decl name changed into another
510 	      // anonymous decl name ...
511 	      (f->get_is_anonymous() && s->get_is_anonymous())
512 	      ||
513 	      // ... an anonymous decl name changed harmlessly into
514 	      // another anonymous decl name ...
515 	      ((f->get_is_anonymous_or_has_anonymous_parent()
516 		&& s->get_is_anonymous_or_has_anonymous_parent())
517 	       && tools_utils::decl_names_equal(f->get_qualified_name(),
518 						s->get_qualified_name()))
519 	      // ... a typedef name change, without having the
520 	      // underlying type changed ...
521 	      || (is_typedef(f)
522 		  && is_typedef(s)
523 		  && (is_typedef(f)->get_underlying_type()
524 		   == is_typedef(s)->get_underlying_type()))
525 	      // .. or a data member name change, without having its
526 	      // type changed ...
527 	      || (is_data_member(f)
528 		  && is_data_member(s)
529 		  && (is_var_decl(f)->get_type()
530 		      == is_var_decl(s)->get_type()))
531 	      // .. an enum name change without having any other part
532 	      // of the enum to change.
533 	      || (is_enum_type(f)
534 		  && is_enum_type(s)
535 		  && !enum_has_non_name_change(*is_enum_type(f),
536 					       *is_enum_type(s),
537 					       0))));
538 }
539 
540 /// Test if two decls represents a harmful name change.
541 ///
542 /// A harmful name change is a name change that is not harmless, so
543 /// this function uses the function has_harmless_name_change.
544 ///
545 /// @param f the first decl to consider in the comparison.
546 ///
547 /// @param s the second decl to consider in the comparison.
548 ///
549 /// @return true iff decl @p s represents a harmful name change over
550 /// @p f.
551 bool
has_harmful_name_change(const decl_base_sptr & f,const decl_base_sptr & s)552 has_harmful_name_change(const decl_base_sptr& f, const decl_base_sptr& s)
553 {return decl_name_changed(f, s) && ! has_harmless_name_change(f, s);}
554 
555 /// Test if a diff node represents a harmful name change.
556 ///
557 /// A harmful name change is a name change that is not harmless, so
558 /// this function uses the function has_harmless_name_change.
559 ///
560 /// @param f the first decl to consider in the comparison.
561 ///
562 /// @param s the second decl to consider in the comparison.
563 ///
564 /// @return true iff decl @p s represents a harmful name change over
565 /// @p f.
566 bool
has_harmful_name_change(const diff * dif)567 has_harmful_name_change(const diff* dif)
568 {
569   decl_base_sptr f = is_decl(dif->first_subject()),
570     s = is_decl(dif->second_subject());
571 
572   return has_harmful_name_change(f, s);
573 }
574 
575 /// Test if a class_diff node has non-static members added or
576 /// removed.
577 ///
578 /// @param diff the diff node to consider.
579 ///
580 /// @return true iff the class_diff node has non-static members added
581 /// or removed.
582 static bool
non_static_data_member_added_or_removed(const class_diff * diff)583 non_static_data_member_added_or_removed(const class_diff* diff)
584 {
585   if (diff && !diff_involves_decl_only_class(diff))
586     {
587       for (string_decl_base_sptr_map::const_iterator i =
588 	     diff->inserted_data_members().begin();
589 	   i != diff->inserted_data_members().end();
590 	   ++i)
591 	if (!get_member_is_static(i->second))
592 	  return true;
593 
594       for (string_decl_base_sptr_map::const_iterator i =
595 	     diff->deleted_data_members().begin();
596 	   i != diff->deleted_data_members().end();
597 	   ++i)
598 	if (!get_member_is_static(i->second))
599 	  return true;
600     }
601 
602   return false;
603 }
604 
605 /// Test if a class_diff node has members added or removed.
606 ///
607 /// @param diff the diff node to consider.
608 ///
609 /// @return true iff the class_diff node has members added or removed.
610 static bool
non_static_data_member_added_or_removed(const diff * diff)611 non_static_data_member_added_or_removed(const diff* diff)
612 {
613   return non_static_data_member_added_or_removed
614     (dynamic_cast<const class_diff*>(diff));
615 }
616 
617 /// Test if a @ref class_or_union_diff has a data member replaced by
618 /// an anonymous data member in a harmless way.  That means, the new
619 /// anonymous data member somehow contains the replaced data member
620 /// and it doesn't break the layout of the containing class.
621 ///
622 /// @param diff the diff node to consider.
623 ///
624 /// @return true iff the @ref class_or_union_diff has a data member
625 /// harmlessly replaced by an anonymous data member.
626 bool
has_data_member_replaced_by_anon_dm(const diff * diff)627 has_data_member_replaced_by_anon_dm(const diff* diff)
628 {
629   const class_or_union_diff *c = is_class_or_union_diff(diff);
630 
631   if (!c)
632     return false;
633   return !c->data_members_replaced_by_adms().empty();
634 }
635 
636 /// Test if we are looking at two variables which types are both one
637 /// dimension array, with one of them being of unknow size and the two
638 /// variables having the same symbol size.
639 ///
640 /// This can happen in the case of these two declarations, for instance:
641 ///
642 ///     unsigned int array[];
643 ///
644 /// and:
645 ///
646 ///     unsigned int array[] ={0};
647 ///
648 /// In both cases, the size of the ELF symbol of the variable 'array'
649 /// is 32 bits, but, at least in the first case
650 bool
is_var_1_dim_unknown_size_array_change(const var_decl_sptr & var1,const var_decl_sptr & var2)651 is_var_1_dim_unknown_size_array_change(const var_decl_sptr& var1,
652 				       const var_decl_sptr& var2)
653 {
654   type_base_sptr /*first type*/ft =
655     peel_qualified_or_typedef_type(var1->get_type());
656   type_base_sptr /*second type*/st =
657     peel_qualified_or_typedef_type(var2->get_type());
658 
659   array_type_def_sptr /*first array type*/fat = is_array_type(ft);
660   array_type_def_sptr /*second array type*/sat = is_array_type(st);
661 
662   // The types of the variables must be arrays.
663   if (!fat || !sat)
664     return false;
665 
666   // The arrays must have one dimension and at least one of them must
667   // be of unknown size.
668   if (fat->get_subranges().size() != 1
669       || sat->get_subranges().size() != 1
670       || (!fat->is_infinite() && !sat->is_infinite()))
671     return false;
672 
673   // The variables must be equal modulo their type.
674   if (!var_equals_modulo_types(*var1, *var2, nullptr))
675     return false;
676 
677   // The symbols of the variables must be defined and of the same
678   // non-zero size.
679   if (!var1->get_symbol()
680       || !var2->get_symbol()
681       || var1->get_symbol()->get_size() != var2->get_symbol()->get_size())
682     return false;
683 
684   return true;
685 }
686 
687 /// Test if we are looking at a diff that carries a change of
688 /// variables which types are both one dimension array, with one of
689 /// them being of unknow size and the two variables having the same
690 /// symbol size.
691 ///
692 /// This can happen in the case of these two declarations, for instance:
693 ///
694 ///     unsigned int array[];
695 ///
696 /// and:
697 ///
698 ///     unsigned int array[] ={0};
699 ///
700 /// In both cases, the size of the ELF symbol of the variable 'array'
701 /// is 32 bits, but, at least in the first case
702 bool
is_var_1_dim_unknown_size_array_change(const diff * diff)703 is_var_1_dim_unknown_size_array_change(const diff* diff)
704 {
705   const var_diff* d = is_var_diff(diff);
706 
707   if (!d)
708     return false;
709 
710   var_decl_sptr f = d->first_var(), s = d->second_var();
711 
712   return is_var_1_dim_unknown_size_array_change(f, s);
713 }
714 
715 /// Test if a class_diff node has static members added or removed.
716 ///
717 /// @param diff the diff node to consider.
718 ///
719 /// @return true iff the class_diff node has static members added
720 /// or removed.
721 static bool
static_data_member_added_or_removed(const class_diff * diff)722 static_data_member_added_or_removed(const class_diff* diff)
723 {
724   if (diff && !diff_involves_decl_only_class(diff))
725     {
726       for (string_decl_base_sptr_map::const_iterator i =
727 	     diff->inserted_data_members().begin();
728 	   i != diff->inserted_data_members().end();
729 	   ++i)
730 	if (get_member_is_static(i->second))
731 	  return true;
732 
733       for (string_decl_base_sptr_map::const_iterator i =
734 	     diff->deleted_data_members().begin();
735 	   i != diff->deleted_data_members().end();
736 	   ++i)
737 	if (get_member_is_static(i->second))
738 	  return true;
739     }
740 
741   return false;
742 }
743 
744 /// Test if a class_diff node has a harmless "One Definition Rule"
745 /// violation that will cause a diagnostic rule.
746 ///
747 /// The conditions this function looks for are:
748 ///
749 ///  1/ The two subject of the diff must be canonically different
750 ///
751 ///  2/ The two subjects of the diff must be structurally equal
752 ///
753 ///  3/ The canonical types of the subjects of the diff must be
754 ///  structurally different.
755 ///
756 /// These conditions makes the diff node appears as it carries changes
757 /// (because of a ODR glitch present in the binary), but the glitch
758 /// has no effect on the structural equality of the subjects of the
759 /// diff.  If we do not detect these conditions, we'd end up with a
760 /// diagnostic glitch where the reporter thinks there is an ABI change
761 /// (because of the canonical difference), but then it fails to give
762 /// any detail about it, because there is no structural change.
763 ///
764 /// @param diff the diff node to consider.
765 ///
766 /// @return true iff the the diff node has a harmless "One Definition
767 /// Rule" violation that cause an empty false positive.
768 static bool
class_diff_has_harmless_odr_violation_change(const diff * dif)769 class_diff_has_harmless_odr_violation_change(const diff* dif)
770 {
771   class_diff* d =  dynamic_cast<class_diff*>(const_cast<diff*>(dif));
772   if (!d || !d->has_changes())
773     return false;
774 
775   class_decl_sptr first = d->first_class_decl();
776   class_decl_sptr second = d->second_class_decl();
777 
778   if (first->get_qualified_name() == second->get_qualified_name()
779       && first != second
780       && first->get_corpus() == second->get_corpus())
781     return true;
782 
783   return false;
784 }
785 
786 /// Test if a class_diff node has static members added or
787 /// removed.
788 ///
789 /// @param diff the diff node to consider.
790 ///
791 /// @return true iff the class_diff node has static members added
792 /// or removed.
793 static bool
static_data_member_added_or_removed(const diff * diff)794 static_data_member_added_or_removed(const diff* diff)
795 {
796   return static_data_member_added_or_removed
797     (dynamic_cast<const class_diff*>(diff));
798 }
799 
800 /// Test if the class_diff node has a change involving virtual member
801 /// functions.
802 ///
803 /// That means whether there is an added, removed or changed virtual
804 /// member function.
805 ///
806 /// @param diff the class_diff node to consider.
807 ///
808 /// @return true iff the class_diff node contains changes involving
809 /// virtual member functions.
810 static bool
has_virtual_mem_fn_change(const class_diff * diff)811 has_virtual_mem_fn_change(const class_diff* diff)
812 {
813   if (!diff || diff_involves_decl_only_class(diff))
814     return false;
815 
816   for (string_member_function_sptr_map::const_iterator i =
817 	 diff->deleted_member_fns().begin();
818        i != diff->deleted_member_fns().end();
819        ++i)
820     {
821       if (get_member_function_is_virtual(i->second))
822 	{
823 	  // Do not consider a virtual function that got deleted from
824 	  // an offset and re-inserted at the same offset as a
825 	  // "virtual member function change".
826 	  string_member_function_sptr_map::const_iterator j =
827 	    diff->inserted_member_fns().find(i->first);
828 	  if (j != diff->inserted_member_fns().end()
829 	      && (get_member_function_vtable_offset(i->second)
830 		  == get_member_function_vtable_offset(j->second)))
831 	    continue;
832 
833 	  return true;
834 	}
835     }
836 
837   for (string_member_function_sptr_map::const_iterator i =
838 	 diff->inserted_member_fns().begin();
839        i != diff->inserted_member_fns().end();
840        ++i)
841     {
842       if (get_member_function_is_virtual(i->second))
843 	{
844 	  // Do not consider a virtual function that got deleted from
845 	  // an offset and re-inserted at the same offset as a
846 	  // "virtual member function change".
847 	  string_member_function_sptr_map::const_iterator j =
848 	    diff->deleted_member_fns().find(i->first);
849 	  if (j != diff->deleted_member_fns().end()
850 	      && (get_member_function_vtable_offset(i->second)
851 		  == get_member_function_vtable_offset(j->second)))
852 	    continue;
853 
854 	  return true;
855 	}
856     }
857 
858   for (function_decl_diff_sptrs_type::const_iterator i =
859 	 diff->changed_member_fns().begin();
860        i != diff->changed_member_fns().end();
861        ++i)
862     if (get_member_function_is_virtual((*i)->first_function_decl())
863 	|| get_member_function_is_virtual((*i)->second_function_decl()))
864       {
865 	if (get_member_function_vtable_offset((*i)->first_function_decl())
866 	    == get_member_function_vtable_offset((*i)->second_function_decl()))
867 	  continue;
868 
869 	return true;
870       }
871 
872   return false;
873 }
874 
875 /// Test if the function_decl_diff node has a change involving virtual
876 /// member functions.
877 ///
878 /// That means whether there is an added, removed or changed virtual
879 /// member function.
880 ///
881 /// @param diff the function_decl_diff node to consider.
882 ///
883 /// @return true iff the function_decl_diff node contains changes
884 /// involving virtual member functions.
885 bool
has_virtual_mem_fn_change(const function_decl_diff * diff)886 has_virtual_mem_fn_change(const function_decl_diff* diff)
887 {
888   if (!diff)
889     return false;
890 
891   function_decl_sptr ff = diff->first_function_decl(),
892     sf = diff->second_function_decl();
893 
894   if (!is_member_function(ff)
895       || !is_member_function(sf))
896     return false;
897 
898   bool ff_is_virtual = get_member_function_is_virtual(ff),
899     sf_is_virtual = get_member_function_is_virtual(sf);
900 
901   if (ff_is_virtual != sf_is_virtual)
902     return true;
903 
904   size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
905     sf_vtable_offset = get_member_function_vtable_offset(sf);
906 
907   if (ff_vtable_offset != sf_vtable_offset)
908     return true;
909 
910   return false;
911 }
912 
913 /// Test if the class_diff node has a change involving virtual member
914 /// functions.
915 ///
916 /// That means whether there is an added, removed or changed virtual
917 /// member function.
918 ///
919 /// @param diff the class_diff node to consider.
920 ///
921 /// @return true iff the class_diff node contains changes involving
922 /// virtual member functions.
923 static bool
has_virtual_mem_fn_change(const diff * diff)924 has_virtual_mem_fn_change(const diff* diff)
925 {
926   return (has_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff))
927 	  || has_virtual_mem_fn_change(dynamic_cast<const function_decl_diff*>(diff)));
928 }
929 
930 /// Test if the class_diff has changes to non virtual member
931 /// functions.
932 ///
933 ///@param diff the class_diff nod e to consider.
934 ///
935 /// @retrurn iff the class_diff node has changes to non virtual member
936 /// functions.
937 static bool
has_non_virtual_mem_fn_change(const class_diff * diff)938 has_non_virtual_mem_fn_change(const class_diff* diff)
939 {
940   if (!diff || diff_involves_decl_only_class(diff))
941     return false;
942 
943   for (string_member_function_sptr_map::const_iterator i =
944 	 diff->deleted_member_fns().begin();
945        i != diff->deleted_member_fns().end();
946        ++i)
947     if (!get_member_function_is_virtual(i->second))
948       return true;
949 
950   for (string_member_function_sptr_map::const_iterator i =
951 	 diff->inserted_member_fns().begin();
952        i != diff->inserted_member_fns().end();
953        ++i)
954     if (!get_member_function_is_virtual(i->second))
955       return true;
956 
957   for (function_decl_diff_sptrs_type::const_iterator i =
958 	 diff->changed_member_fns().begin();
959        i != diff->changed_member_fns().end();
960        ++i)
961     if(!get_member_function_is_virtual((*i)->first_function_decl())
962        && !get_member_function_is_virtual((*i)->second_function_decl()))
963       return true;
964 
965   return false;
966 }
967 
968 /// Test if the class_diff has changes to non virtual member
969 /// functions.
970 ///
971 ///@param diff the class_diff nod e to consider.
972 ///
973 /// @retrurn iff the class_diff node has changes to non virtual member
974 /// functions.
975 static bool
has_non_virtual_mem_fn_change(const diff * diff)976 has_non_virtual_mem_fn_change(const diff* diff)
977 {return has_non_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff));}
978 
979 /// Test if a class_diff carries base classes adding or removals.
980 ///
981 /// @param diff the class_diff to consider.
982 ///
983 /// @return true iff @p diff carries base classes adding or removals.
984 static bool
base_classes_added_or_removed(const class_diff * diff)985 base_classes_added_or_removed(const class_diff* diff)
986 {
987   if (!diff)
988     return false;
989   return diff->deleted_bases().size() || diff->inserted_bases().size();
990 }
991 
992 /// Test if a class_diff carries base classes adding or removals.
993 ///
994 /// @param diff the class_diff to consider.
995 ///
996 /// @return true iff @p diff carries base classes adding or removals.
997 static bool
base_classes_added_or_removed(const diff * diff)998 base_classes_added_or_removed(const diff* diff)
999 {return base_classes_added_or_removed(dynamic_cast<const class_diff*>(diff));}
1000 
1001 /// Test if two classes that are decl-only (have the decl-only flag
1002 /// and carry no data members) but are different just by their size.
1003 ///
1004 /// In some weird DWARF representation, it happens that a decl-only
1005 /// class (with no data member) actually carries a non-zero size.
1006 /// That shouldn't happen, but hey, we need to deal with real life.
1007 /// So we need to detect that case first.
1008 ///
1009 /// @param first the first class or union to consider.
1010 ///
1011 /// @param seconf the second class or union to consider.
1012 ///
1013 /// @return true if the two classes are decl-only and differ in their
1014 /// size.
1015 bool
is_decl_only_class_with_size_change(const class_or_union & first,const class_or_union & second)1016 is_decl_only_class_with_size_change(const class_or_union& first,
1017 				    const class_or_union& second)
1018 {
1019   if (first.get_qualified_name() != second.get_qualified_name())
1020     return false;
1021 
1022   if (!first.get_is_declaration_only() || !second.get_is_declaration_only())
1023     return false;
1024 
1025   bool f_is_empty = first.get_data_members().empty();
1026   bool s_is_empty = second.get_data_members().empty();
1027 
1028   return f_is_empty && s_is_empty;
1029 }
1030 
1031 /// Test if two classes that are decl-only (have the decl-only flag
1032 /// and carry no data members) but are different just by their size.
1033 ///
1034 /// In some weird DWARF representation, it happens that a decl-only
1035 /// class (with no data member) actually carries a non-zero size.
1036 /// That shouldn't happen, but hey, we need to deal with real life.
1037 /// So we need to detect that case first.
1038 ///
1039 /// @param first the first class or union to consider.
1040 ///
1041 /// @param seconf the second class or union to consider.
1042 ///
1043 /// @return true if the two classes are decl-only and differ in their
1044 /// size.
1045 bool
is_decl_only_class_with_size_change(const class_or_union_sptr & first,const class_or_union_sptr & second)1046 is_decl_only_class_with_size_change(const class_or_union_sptr& first,
1047 				    const class_or_union_sptr& second)
1048 {
1049   if (!first || !second)
1050     return false;
1051 
1052   class_or_union_sptr f = look_through_decl_only_class(first);
1053   class_or_union_sptr s = look_through_decl_only_class(second);
1054 
1055   return is_decl_only_class_with_size_change(*f, *s);
1056 }
1057 
1058 /// Test if a diff node is for two classes that are decl-only (have
1059 /// the decl-only flag and carry no data members) but are different
1060 /// just by their size.
1061 ///
1062 /// In some weird DWARF representation, it happens that a decl-only
1063 /// class (with no data member) actually carries a non-zero size.
1064 /// That shouldn't happen, but hey, we need to deal with real life.
1065 /// So we need to detect that case first.
1066 ///
1067 /// @param diff the diff node to consider.
1068 ///
1069 /// @return true if the two classes are decl-only and differ in their
1070 /// size.
1071 bool
is_decl_only_class_with_size_change(const diff * diff)1072 is_decl_only_class_with_size_change(const diff *diff)
1073 {
1074   const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1075   if (!d)
1076     return false;
1077 
1078   class_or_union_sptr f =
1079     look_through_decl_only_class(d->first_class_or_union());
1080   class_or_union_sptr s =
1081     look_through_decl_only_class(d->second_class_or_union());
1082 
1083   return is_decl_only_class_with_size_change(f, s);
1084 }
1085 
1086 /// Test if two @ref decl_base_sptr are different just by the
1087 /// fact that one is decl-only and the other one is defined.
1088 ///
1089 /// @param first the first decl to consider.
1090 ///
1091 /// @param second the second decl to consider.
1092 ///
1093 /// @return true iff the two arguments are different just by the fact
1094 /// that one is decl-only and the other one is defined.
1095 bool
has_decl_only_def_change(const decl_base_sptr & first,const decl_base_sptr & second)1096 has_decl_only_def_change(const decl_base_sptr& first,
1097 			 const decl_base_sptr& second)
1098 {
1099   if (!first || !second)
1100     return false;
1101 
1102   decl_base_sptr f =
1103     look_through_decl_only(first);
1104   decl_base_sptr s =
1105     look_through_decl_only(second);
1106 
1107   if (f->get_qualified_name() != s->get_qualified_name())
1108     return false;
1109 
1110   return f->get_is_declaration_only() != s->get_is_declaration_only();
1111 }
1112 
1113 /// Test if a diff carries a change in which the two decls are
1114 /// different by the fact that one is a decl-only and the other one is
1115 /// defined.
1116 ///
1117 /// @param diff the diff node to consider.
1118 ///
1119 /// @return true if the diff carries a change in which the two decls
1120 /// are different by the fact that one is a decl-only and the other
1121 /// one is defined.
1122 bool
has_decl_only_def_change(const diff * d)1123 has_decl_only_def_change(const diff *d)
1124 {
1125   if (!d)
1126     return false;
1127 
1128   decl_base_sptr f =
1129     look_through_decl_only(is_decl(d->first_subject()));
1130   decl_base_sptr s =
1131     look_through_decl_only(is_decl(d->second_subject()));
1132 
1133   return has_decl_only_def_change(f, s);
1134 }
1135 
1136 
1137 /// Test if two @ref class_or_union_sptr are different just by the
1138 /// fact that one is decl-only and the other one is defined.
1139 ///
1140 /// @param first the first class or union to consider.
1141 ///
1142 /// @param second the second class or union to consider.
1143 ///
1144 /// @return true iff the two arguments are different just by the fact
1145 /// that one is decl-only and the other one is defined.
1146 bool
has_class_decl_only_def_change(const class_or_union_sptr & first,const class_or_union_sptr & second)1147 has_class_decl_only_def_change(const class_or_union_sptr& first,
1148 			       const class_or_union_sptr& second)
1149 {
1150   if (!first || !second)
1151     return false;
1152 
1153   class_or_union_sptr f =
1154     look_through_decl_only_class(first);
1155   class_or_union_sptr s =
1156     look_through_decl_only_class(second);
1157 
1158   if (f->get_qualified_name() != s->get_qualified_name())
1159     return false;
1160 
1161   return f->get_is_declaration_only() != s->get_is_declaration_only();
1162 }
1163 
1164 /// Test if two @ref enum_sptr are different just by the
1165 /// fact that one is decl-only and the other one is defined.
1166 ///
1167 /// @param first the first enum to consider.
1168 ///
1169 /// @param second the second enum to consider.
1170 ///
1171 /// @return true iff the two arguments are different just by the fact
1172 /// that one is decl-only and the other one is defined.
1173 bool
has_enum_decl_only_def_change(const enum_type_decl_sptr & first,const enum_type_decl_sptr & second)1174 has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
1175 			      const enum_type_decl_sptr& second)
1176 {
1177   if (!first || !second)
1178     return false;
1179 
1180   enum_type_decl_sptr f = look_through_decl_only_enum(first);
1181   enum_type_decl_sptr s = look_through_decl_only_enum(second);
1182 
1183   if (f->get_qualified_name() != s->get_qualified_name())
1184     return false;
1185 
1186   return f->get_is_declaration_only() != s->get_is_declaration_only();
1187 }
1188 
1189 /// Test if a class_or_union_diff carries a change in which the two
1190 /// classes are different by the fact that one is a decl-only and the
1191 /// other one is defined.
1192 ///
1193 /// @param diff the diff node to consider.
1194 ///
1195 /// @return true if the class_or_union_diff carries a change in which
1196 /// the two classes are different by the fact that one is a decl-only
1197 /// and the other one is defined.
1198 bool
has_class_decl_only_def_change(const diff * diff)1199 has_class_decl_only_def_change(const diff *diff)
1200 {
1201   const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1202   if (!d)
1203     return false;
1204 
1205   class_or_union_sptr f =
1206     look_through_decl_only_class(d->first_class_or_union());
1207   class_or_union_sptr s =
1208     look_through_decl_only_class(d->second_class_or_union());
1209 
1210   return has_class_decl_only_def_change(f, s);
1211 }
1212 
1213 /// Test if a enum_diff carries a change in which the two enums are
1214 /// different by the fact that one is a decl-only and the other one is
1215 /// defined.
1216 ///
1217 /// @param diff the diff node to consider.
1218 ///
1219 /// @return true if the enum_diff carries a change in which the two
1220 /// enums are different by the fact that one is a decl-only and the
1221 /// other one is defined.
1222 bool
has_enum_decl_only_def_change(const diff * diff)1223 has_enum_decl_only_def_change(const diff *diff)
1224 {
1225   const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
1226   if (!d)
1227     return false;
1228 
1229   enum_type_decl_sptr f = look_through_decl_only_enum(d->first_enum());
1230   enum_type_decl_sptr s = look_through_decl_only_enum(d->second_enum());
1231 
1232   return has_enum_decl_only_def_change(f, s);
1233 }
1234 
1235 /// Test if a diff node carries a basic type name change.
1236 ///
1237 /// @param d the diff node to consider.
1238 ///
1239 /// @return true iff the diff node carries a basic type name change.
1240 bool
has_basic_type_name_change(const diff * d)1241 has_basic_type_name_change(const diff *d)
1242 {
1243   if (const type_decl_diff *dif = is_diff_of_basic_type(d))
1244     if (decl_name_changed(dif))
1245       return true;
1246 
1247   return false;
1248 }
1249 
1250 /// Test if a diff node carries a class or union type name change.
1251 ///
1252 /// @param d the diff node to consider.
1253 ///
1254 /// @return true iff the diff node carries a class or union type name
1255 /// change.
1256 bool
has_class_or_union_type_name_change(const diff * d)1257 has_class_or_union_type_name_change(const diff *d)
1258 {
1259   if (const class_or_union_diff *dif = is_diff_of_class_or_union_type(d))
1260     if (decl_name_changed(dif))
1261       return true;
1262 
1263   return false;
1264 }
1265 
1266 /// Test if a diff node carries a basic or class type name change.
1267 ///
1268 /// @param d the diff node to consider.
1269 ///
1270 /// @return true iff the diff node carries a basic or class type name
1271 /// change.
1272 bool
has_basic_or_class_type_name_change(const diff * d)1273 has_basic_or_class_type_name_change(const diff *d)
1274 {
1275   return (has_basic_type_name_change(d)
1276 	  || has_class_or_union_type_name_change(d));
1277 }
1278 
1279 /// Test if a diff node carries a distinct type change or a
1280 /// pointer/reference/typedef to distinct type change.
1281 ///
1282 /// Note that a distinct type change is a change where the two
1283 /// subjects of the change are not of the same kind, e.g, a basic type
1284 /// that got changed into a qualified type.
1285 ///
1286 /// @param d the diff node to consider.
1287 ///
1288 /// @return true iff @p d is mostly a distinct diff.
1289 bool
is_mostly_distinct_diff(const diff * d)1290 is_mostly_distinct_diff(const diff *d)
1291 {
1292   if (is_distinct_diff(d))
1293     return true;
1294 
1295   // Let's consider that 'd' is a type diff ...
1296   diff * td = const_cast<type_diff_base*>(is_type_diff(d));
1297   if (!td)
1298     {
1299       // ... or a function parameter diff.  In which case, let's get
1300       // its child type diff ...
1301       fn_parm_diff *pd = const_cast<fn_parm_diff*>(is_fn_parm_diff(d));
1302       if (pd)
1303 	{
1304 	  td = const_cast<type_diff_base*>(is_type_diff(pd->type_diff().get()));
1305 	  if (!td)
1306 	    // if the diff of the fn_parm_diff is a a distinct diff
1307 	    // then handle it.
1308 	    td = const_cast<distinct_diff*>
1309 	      (is_distinct_diff(pd->type_diff().get()));
1310 	}
1311       else
1312 	return false;
1313     }
1314 
1315   // At this point, if we are not looking at a type diff we must have
1316   // bailed out already.
1317   ABG_ASSERT(td);
1318 
1319   type_base_sptr first = is_type(td->first_subject());
1320   type_base_sptr second = is_type(td->second_subject());
1321 
1322   first = peel_typedef_pointer_or_reference_type(first);
1323   second = peel_typedef_pointer_or_reference_type(second);
1324   ABG_ASSERT(first && second);
1325 
1326   return distinct_diff::entities_are_of_distinct_kinds(first, second);
1327 }
1328 
1329 /// Test if a diff node carries a non-anonymous data member to
1330 /// anonymous data member change, or vice-versa.
1331 ///
1332 /// @param d the diff node to consider.
1333 ///
1334 /// @return true iff @p d carries a non-anonymous to anonymous data
1335 /// member change, or vice-versa.
1336 bool
has_anonymous_data_member_change(const diff * d)1337 has_anonymous_data_member_change(const diff *d)
1338 {
1339   if (is_anonymous_data_member(d->first_subject())
1340       || is_anonymous_data_member(d->second_subject()))
1341     return true;
1342   return false;
1343 }
1344 
1345 /// Test if a diff node carries a non-anonymous data member to
1346 /// anonymous data member change, or vice-versa.
1347 ///
1348 /// @param d the diff node to consider.
1349 ///
1350 /// @return true iff @p d carries a non-anonymous to anonymous data
1351 /// member change, or vice-versa.
1352 bool
has_anonymous_data_member_change(const diff_sptr & d)1353 has_anonymous_data_member_change(const diff_sptr &d)
1354 {return has_anonymous_data_member_change(d.get());}
1355 
1356 /// Test if an enum_diff carries an enumerator insertion.
1357 ///
1358 /// @param diff the enum_diff to consider.
1359 ///
1360 /// @return true iff @p diff carries an enumerator insertion.
1361 static bool
has_enumerator_insertion(const diff * diff)1362 has_enumerator_insertion(const diff* diff)
1363 {
1364   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1365     return !d->inserted_enumerators().empty();
1366   return false;
1367 }
1368 
1369 /// Test if an enum_diff carries an enumerator removal.
1370 ///
1371 /// @param diff the enum_diff to consider.
1372 ///
1373 /// @return true iff @p diff carries an enumerator removal or change.
1374 static bool
has_enumerator_removal_or_change(const diff * diff)1375 has_enumerator_removal_or_change(const diff* diff)
1376 {
1377   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1378     return (!d->deleted_enumerators().empty()
1379 	    || !d->changed_enumerators().empty());
1380   return false;
1381 }
1382 
1383 /// Test if an enum_diff carries a harmful change.
1384 ///
1385 /// @param diff the enum_diff to consider.
1386 ///
1387 /// @return true iff @p diff carries a harmful change.
1388 static bool
has_harmful_enum_change(const diff * diff)1389 has_harmful_enum_change(const diff* diff)
1390 {
1391   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1392     return (has_enumerator_removal_or_change(d)
1393 	    || has_type_size_change(d));
1394   return false;
1395 }
1396 
1397 /// Test if a diff node carries a harmless change of an enum into an
1398 /// integer (or vice-versa).
1399 ///
1400 /// The test takes into account the fact change we care about might be
1401 /// wrapped into a typedef or qualified type diff.
1402 ///
1403 /// @param diff the diff node to consider.
1404 ///
1405 /// @return true if @p diff is a harmless enum to integer change.
1406 static bool
has_harmless_enum_to_int_change(const diff * diff)1407 has_harmless_enum_to_int_change(const diff* diff)
1408 {
1409   if (!diff)
1410     return false;
1411 
1412   diff = peel_typedef_or_qualified_type_diff(diff);
1413 
1414   if (const distinct_diff *d = is_distinct_diff(diff))
1415     {
1416       const enum_type_decl *enum_type = 0;
1417       const type_base *integer_type = 0;
1418 
1419       type_base *first_type =
1420 	peel_qualified_or_typedef_type(is_type(d->first().get()));
1421       type_base *second_type =
1422 	peel_qualified_or_typedef_type(is_type(d->second().get()));
1423 
1424       if (const enum_type_decl *e = is_enum_type(first_type))
1425 	enum_type = e;
1426       else if (const enum_type_decl *e = is_enum_type(second_type))
1427 	enum_type = e;
1428 
1429       if (const type_base * i = is_type_decl(first_type))
1430 	integer_type = i;
1431       else if (const type_base *i = is_type_decl(second_type))
1432 	integer_type = i;
1433 
1434       if (enum_type
1435 	  && integer_type
1436 	  && enum_type->get_size_in_bits() == integer_type->get_size_in_bits())
1437 	return true;
1438     }
1439 
1440   return false;
1441 }
1442 
1443 /// Test if an @ref fn_parm_diff node has a top cv qualifier change on
1444 /// the type of the function parameter.
1445 ///
1446 /// @param diff the diff node to consider.  It should be a @ref
1447 /// fn_parm_diff, otherwise the function returns 'false' directly.
1448 ///
1449 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1450 /// top cv qualifier change on the type of the function parameter.
1451 static bool
has_fn_parm_type_top_cv_qual_change(const diff * diff)1452 has_fn_parm_type_top_cv_qual_change(const diff* diff)
1453 {
1454   // is diff a "function parameter diff node?
1455   const fn_parm_diff* parm_diff = is_fn_parm_diff(diff);
1456 
1457   if (!parm_diff || !parm_diff->has_changes())
1458     // diff either carries no change or is not a function parameter
1459     // diff node.  So bail out.
1460     return false;
1461 
1462   function_decl::parameter_sptr first_parm = parm_diff->first_parameter();
1463   function_decl::parameter_sptr second_parm = parm_diff->second_parameter();
1464 
1465   type_base_sptr first_parm_type = first_parm->get_type();
1466   type_base_sptr second_parm_type = second_parm->get_type();
1467 
1468   if (!is_qualified_type(first_parm_type)
1469       && !is_qualified_type(second_parm_type))
1470     // None of the parameter types is qualified.
1471     return false;
1472 
1473   qualified_type_def::CV cv_quals_1 = qualified_type_def::CV_NONE;
1474   qualified_type_def::CV cv_quals_2 = qualified_type_def::CV_NONE;
1475   type_base_sptr peeled_type_1 = first_parm_type;
1476   type_base_sptr peeled_type_2 = second_parm_type;
1477 
1478   if (qualified_type_def_sptr qtype1 = is_qualified_type(first_parm_type))
1479     {
1480       cv_quals_1 = qtype1->get_cv_quals();
1481       peeled_type_1 = peel_qualified_type(qtype1);
1482     }
1483 
1484   if (qualified_type_def_sptr qtype2 = is_qualified_type(second_parm_type))
1485     {
1486       cv_quals_2 = qtype2->get_cv_quals();
1487       peeled_type_2 = peel_qualified_type(qtype2);
1488     }
1489 
1490   if (peeled_type_1
1491       && peeled_type_2
1492       && get_type_name(peeled_type_1) == get_type_name(peeled_type_2)
1493       && cv_quals_1 != cv_quals_2)
1494     // The top-level CV qualifiers of the function type are different
1495     // and the un-qualified variant (peeled) of said function types
1496     // are equal.  This means the only change the function types have
1497     // are about top-level CV qualifiers.
1498     return true;
1499 
1500   return false;
1501 }
1502 
1503 /// Test if a type diff only carries a CV qualifier-only change.
1504 ///
1505 /// @param type_dif the type dif to consider.
1506 ///
1507 /// @return true iff the type_diff carries a CV qualifier only change.
1508 static bool
type_diff_has_cv_qual_change_only(const diff * type_dif)1509 type_diff_has_cv_qual_change_only(const diff *type_dif)
1510 {
1511   if (!is_type_diff(type_dif))
1512     return false;
1513 
1514   if (is_pointer_diff(type_dif))
1515     type_dif = peel_pointer_diff(type_dif);
1516   else if (is_reference_diff(type_dif))
1517     type_dif = peel_reference_diff(type_dif);
1518 
1519   const type_base *f = 0;
1520   const type_base *s = 0;
1521   if (const distinct_diff *d = is_distinct_diff(type_dif))
1522     {
1523       if (is_qualified_type(d->first()) == is_qualified_type(d->second()))
1524 	return false;
1525       else
1526 	{
1527 	  f = is_type(d->first()).get();
1528 	  s = is_type(d->second()).get();
1529 	}
1530     }
1531   else if (const qualified_type_diff *d = is_qualified_type_diff(type_dif))
1532     {
1533       f = is_type(d->first_qualified_type()).get();
1534       s = is_type(d->second_qualified_type()).get();
1535     }
1536   else
1537     return false;
1538 
1539   f = peel_qualified_type(f);
1540   s = peel_qualified_type(s);
1541 
1542   // If f and s are arrays, note that they can differ only by the cv
1543   // qualifier of the array element type.  That cv qualifier is not
1544   // removed by peel_qualified_type.  So we need to test this case
1545   // specifically.
1546   if (array_type_def *f_a = is_array_type(f))
1547     if (array_type_def *s_a = is_array_type(s))
1548       return equals_modulo_cv_qualifier(f_a, s_a);
1549 
1550   return *f == *s;
1551 }
1552 
1553 /// Test if an @ref fn_parm_diff node has a cv qualifier change on the
1554 /// type of the function parameter. That is, we are looking for
1555 /// changes like 'const char*' to 'char*'.
1556 ///
1557 /// @param diff the diff node to consider.  It should be a @ref
1558 /// fn_parm_diff, otherwise the function returns 'false' directly.
1559 ///
1560 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1561 /// cv qualifier change on the type of the function parameter.
1562 static bool
has_fn_parm_type_cv_qual_change(const diff * dif)1563 has_fn_parm_type_cv_qual_change(const diff* dif)
1564 {
1565   // is diff a "function parameter diff node?
1566   const fn_parm_diff* parm_diff = is_fn_parm_diff(dif);
1567 
1568   if (!parm_diff || !parm_diff->has_changes())
1569     // diff either carries no change or is not a function parameter
1570     // diff node.  So bail out.
1571     return false;
1572 
1573   const diff *type_dif = parm_diff->type_diff().get();
1574   return type_diff_has_cv_qual_change_only(type_dif);
1575 }
1576 
1577 /// Test if a function type or decl diff node carries a CV
1578 /// qualifier-only change on its return type.
1579 ///
1580 /// @param dif the diff node to consider.  Note that if this is
1581 /// neither a function type nor decl diff node, the function returns
1582 /// false.
1583 ///
1584 /// @return true iff @p dif is a function decl or type diff node which
1585 /// carries a CV qualifier-only change on its return type.
1586 static bool
has_fn_return_type_cv_qual_change(const diff * dif)1587 has_fn_return_type_cv_qual_change(const diff* dif)
1588 {
1589   const function_type_diff* fn_type_diff = is_function_type_diff(dif);
1590   if (!fn_type_diff)
1591     if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1592       fn_type_diff = fn_decl_diff->type_diff().get();
1593 
1594   if (!fn_type_diff)
1595     return false;
1596 
1597   const diff* return_type_diff = fn_type_diff->return_type_diff().get();
1598   return type_diff_has_cv_qual_change_only(return_type_diff);
1599 }
1600 
1601 /// Test if a function type or decl diff node carries a function
1602 /// parameter addition or removal.
1603 ///
1604 /// @param dif the diff node to consider.  Note that if this is
1605 /// neither a function type nor decl diff node, the function returns
1606 /// false.
1607 ///
1608 /// @return true iff @p dif is a function decl or type diff node which
1609 /// carries a function parameter addition or removal.
1610 static bool
has_added_or_removed_function_parameters(const diff * dif)1611 has_added_or_removed_function_parameters(const diff *dif)
1612 {
1613   const function_type_diff *fn_type_diff = is_function_type_diff(dif);
1614     if (!fn_type_diff)
1615     if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1616       fn_type_diff = fn_decl_diff->type_diff().get();
1617 
1618   if (!fn_type_diff)
1619     return false;
1620 
1621   if (!(fn_type_diff->sorted_deleted_parms().empty()
1622 	&& fn_type_diff->sorted_added_parms().empty()))
1623     return true;
1624 
1625   return false;
1626 }
1627 
1628 /// Test if a variable diff node carries a CV qualifier change on its type.
1629 ///
1630 /// @param dif the diff node to consider.  Note that if it's not of
1631 /// var_diff type, the function returns false.
1632 ///
1633 /// @return true iff the @p dif carries a CV qualifier change on its
1634 /// type.
1635 static bool
has_var_type_cv_qual_change(const diff * dif)1636 has_var_type_cv_qual_change(const diff* dif)
1637 {
1638   const var_diff *var_dif = is_var_diff(dif);
1639   if (!var_dif)
1640     return false;
1641 
1642   diff *type_dif = var_dif->type_diff().get();
1643   if (!type_dif)
1644     return false;
1645 
1646   return type_diff_has_cv_qual_change_only(type_dif);
1647 }
1648 
1649 /// Test if a diff node carries a void* to pointer type change.
1650 ///
1651 /// Note that this function looks through typedef and qualifier types
1652 /// to find the void pointer.
1653 ///
1654 /// @param dif the diff node to consider.
1655 ///
1656 /// @return true iff @p dif carries a void* to pointer type change.
1657 static bool
has_void_ptr_to_ptr_change(const diff * dif)1658 has_void_ptr_to_ptr_change(const diff* dif)
1659 {
1660   dif = peel_typedef_diff(dif);
1661 
1662   if (const distinct_diff *d = is_distinct_diff(dif))
1663     {
1664       const type_base *f = is_type(d->first().get());
1665       const type_base *s = is_type(d->second().get());
1666 
1667       f = peel_qualified_or_typedef_type(f);
1668       s = peel_qualified_or_typedef_type(s);
1669 
1670       if (is_void_pointer_type_equivalent(f)
1671 	  && is_pointer_type(s)
1672 	  && !is_void_pointer_type_equivalent(s)
1673 	  && f->get_size_in_bits() == s->get_size_in_bits())
1674 	return true;
1675     }
1676   else if (const pointer_diff *d = is_pointer_diff(dif))
1677     {
1678       const type_base *f = is_type(d->first_pointer()).get();
1679       const type_base *s = is_type(d->second_pointer()).get();
1680 
1681       f = peel_qualified_or_typedef_type(f);
1682       s = peel_qualified_or_typedef_type(s);
1683 
1684       if (is_void_pointer_type_equivalent(f)
1685 	  && is_pointer_type(s)
1686 	  && !is_void_pointer_type_equivalent(s)
1687 	  && f->get_size_in_bits() == s->get_size_in_bits())
1688 	return true;
1689     }
1690   else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
1691     {
1692       const type_base *f = is_type(d->first_qualified_type()).get();
1693       const type_base *s = is_type(d->second_qualified_type()).get();
1694 
1695       f = peel_qualified_or_typedef_type(f);
1696       s = peel_qualified_or_typedef_type(s);
1697 
1698       if (is_void_pointer_type_equivalent(f)
1699 	  && is_pointer_type(s)
1700 	  && !is_void_pointer_type_equivalent(s)
1701 	  && f->get_size_in_bits() == s->get_size_in_bits())
1702 	return true;
1703     }
1704 
1705   return false;
1706 }
1707 
1708 /// Test if a diff node carries a benign change to the size of a
1709 /// variable of type array.
1710 ///
1711 /// A benign size change is a change in size (from or to infinite) of
1712 /// the array as expressed by the debug info, but when the *ELF* size
1713 /// (what really matters) of the variable object hasn't changed.  This
1714 /// happens when the debug info emitter did have trouble figuring out
1715 /// the actual size of the array.
1716 ///
1717 /// @param dif the diff node to consider.
1718 ///
1719 /// @return true iff @p dif contains the benign array type size change.
1720 static bool
has_benign_array_of_unknown_size_change(const diff * dif)1721 has_benign_array_of_unknown_size_change(const diff* dif)
1722 {
1723   return is_var_1_dim_unknown_size_array_change(dif);
1724 }
1725 
1726 /// Test if a union diff node does have changes that don't impact its
1727 /// size.
1728 ///
1729 /// @param d the union diff node to consider.
1730 ///
1731 /// @return true iff @p d is a diff node which has changes that don't
1732 /// impact its size.
1733 bool
union_diff_has_harmless_changes(const diff * d)1734 union_diff_has_harmless_changes(const diff *d)
1735 {
1736   if (is_union_diff(d)
1737       && d->has_changes()
1738       && !has_type_size_change(d))
1739     return true;
1740 
1741   return false;
1742 }
1743 
1744 /// Detect if the changes carried by a given diff node are deemed
1745 /// harmless and do categorize the diff node accordingly.
1746 ///
1747 /// @param d the diff node being visited.
1748 ///
1749 /// @param pre this is true iff the node is being visited *before* the
1750 /// children nodes of @p d.
1751 ///
1752 /// @return true iff the traversal shall keep going after the
1753 /// completion of this function.
1754 static bool
categorize_harmless_diff_node(diff * d,bool pre)1755 categorize_harmless_diff_node(diff *d, bool pre)
1756 {
1757   if (!d->has_changes())
1758     return true;
1759 
1760   if (pre)
1761     {
1762       diff_category category = NO_CHANGE_CATEGORY;
1763 
1764       decl_base_sptr f = is_decl(d->first_subject()),
1765 	s = is_decl(d->second_subject());
1766 
1767       if (has_class_decl_only_def_change(d)
1768 	  || has_enum_decl_only_def_change(d))
1769 	category |= TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY;
1770 
1771       if (access_changed(f, s))
1772 	category |= ACCESS_CHANGE_CATEGORY;
1773 
1774       if (is_compatible_change(f, s))
1775 	category |= COMPATIBLE_TYPE_CHANGE_CATEGORY;
1776 
1777       if (has_harmless_name_change(f, s)
1778 	  || class_diff_has_harmless_odr_violation_change(d))
1779 	category |= HARMLESS_DECL_NAME_CHANGE_CATEGORY;
1780 
1781       if (union_diff_has_harmless_changes(d))
1782 	category |= HARMLESS_UNION_CHANGE_CATEGORY;
1783 
1784       if (has_non_virtual_mem_fn_change(d))
1785 	category |= NON_VIRT_MEM_FUN_CHANGE_CATEGORY;
1786 
1787       if (static_data_member_added_or_removed(d)
1788 	  || static_data_member_type_size_changed(f, s))
1789 	category |= STATIC_DATA_MEMBER_CHANGE_CATEGORY;
1790 
1791       if (has_data_member_replaced_by_anon_dm(d))
1792 	category |= HARMLESS_DATA_MEMBER_CHANGE_CATEGORY;
1793 
1794       if ((has_enumerator_insertion(d)
1795 	   && !has_harmful_enum_change(d))
1796 	  || has_harmless_enum_to_int_change(d))
1797 	category |= HARMLESS_ENUM_CHANGE_CATEGORY;
1798 
1799       if (function_name_changed_but_not_symbol(d))
1800 	category |= HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY;
1801 
1802       if (has_fn_parm_type_top_cv_qual_change(d))
1803 	category |= FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY;
1804 
1805       if (has_fn_parm_type_cv_qual_change(d))
1806 	category |= FN_PARM_TYPE_CV_CHANGE_CATEGORY;
1807 
1808       if (has_fn_return_type_cv_qual_change(d))
1809 	category |= FN_RETURN_TYPE_CV_CHANGE_CATEGORY;
1810 
1811       if (has_var_type_cv_qual_change(d))
1812 	category |= VAR_TYPE_CV_CHANGE_CATEGORY;
1813 
1814       if (has_void_ptr_to_ptr_change(d))
1815 	category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
1816 
1817       if (has_benign_array_of_unknown_size_change(d))
1818 	category |= BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY;
1819 
1820       if (category)
1821 	{
1822 	  d->add_to_local_and_inherited_categories(category);
1823 	  // Also update the category of the canonical node.
1824 	  if (diff * canonical = d->get_canonical_diff())
1825 	    canonical->add_to_local_and_inherited_categories(category);
1826 	}
1827     }
1828 
1829   return true;
1830 }
1831 
1832 /// Detect if the changes carried by a given diff node are deemed
1833 /// harmful and do categorize the diff node accordingly.
1834 ///
1835 /// @param d the diff node being visited.
1836 ///
1837 /// @param pre this is true iff the node is being visited *before* the
1838 /// children nodes of @p d.
1839 ///
1840 /// @return true iff the traversal shall keep going after the
1841 /// completion of this function.
1842 static bool
categorize_harmful_diff_node(diff * d,bool pre)1843 categorize_harmful_diff_node(diff *d, bool pre)
1844 {
1845   if (!d->has_changes())
1846     return true;
1847 
1848   if (pre)
1849     {
1850       diff_category category = NO_CHANGE_CATEGORY;
1851       decl_base_sptr f = is_decl(d->first_subject()),
1852 	s = is_decl(d->second_subject());
1853 
1854       // Detect size or offset changes as well as data member addition
1855       // or removal.
1856       //
1857       // TODO: be more specific -- not all size changes are harmful.
1858       if (!has_class_decl_only_def_change(d)
1859 	  && !has_enum_decl_only_def_change(d)
1860 	  && (type_size_changed(f, s)
1861 	      || data_member_offset_changed(f, s)
1862 	      || non_static_data_member_type_size_changed(f, s)
1863 	      || non_static_data_member_added_or_removed(d)
1864 	      || base_classes_added_or_removed(d)
1865 	      || has_harmful_enum_change(d)
1866 	      || crc_changed(d)
1867 	      || namespace_changed(d)))
1868 	category |= SIZE_OR_OFFSET_CHANGE_CATEGORY;
1869 
1870       if (has_virtual_mem_fn_change(d))
1871 	category |= VIRTUAL_MEMBER_CHANGE_CATEGORY;
1872 
1873       if (has_added_or_removed_function_parameters(d))
1874 	category |= FN_PARM_ADD_REMOVE_CHANGE_CATEGORY;
1875 
1876       if (category)
1877 	{
1878 	  d->add_to_local_and_inherited_categories(category);
1879 	  // Update the category of the canonical diff node too.
1880 	  if (diff * canonical = d->get_canonical_diff())
1881 	    canonical->add_to_local_and_inherited_categories(category);
1882 	}
1883     }
1884 
1885   return true;
1886 }
1887 
1888 /// The visiting code of the harmless_harmful_filter.
1889 ///
1890 /// @param d the diff node being visited.
1891 ///
1892 /// @param pre this is true iff the node is being visited *before* the
1893 /// children nodes of @p d.
1894 ///
1895 /// @return true iff the traversal shall keep going after the
1896 /// completion of this function.
1897 bool
visit(diff * d,bool pre)1898 harmless_harmful_filter::visit(diff* d, bool pre)
1899 {
1900   return (categorize_harmless_diff_node(d, pre)
1901 	  && categorize_harmful_diff_node(d, pre));
1902 }
1903 
1904 /// Part of the visiting code of the harmless_harmful_filter.
1905 ///
1906 /// This function is called after the visiting of a given diff node.
1907 /// Note that when this function is called, the visiting might not
1908 /// have taken place *if* the node (or an equivalent node) has already
1909 /// been visited.
1910 ///
1911 /// @param d the diff node that has either been visited or skipped
1912 /// (because it has already been visited during this traversing).
1913 void
visit_end(diff * d)1914 harmless_harmful_filter::visit_end(diff* d)
1915 {
1916   if (d->context()->diff_has_been_visited(d))
1917     {
1918       // This node or one of its equivalent node has already been
1919       // visited.  That means at this moment,
1920       // harmless_harmful_filter::visit() has *not* been called prior
1921       // to this harmless_harmful_filter::visit_end() is called.  In
1922       // other words, only harmless_harmful_filter::visit_begin() and
1923       // harmless_harmful_filter::visit_end() are called.
1924       //
1925       // So let's update the category of this diff node from its
1926       // canonical node.
1927       if (diff* c = d->get_canonical_diff())
1928 	d->add_to_local_and_inherited_categories(c->get_local_category());
1929     }
1930 }
1931 } // end namespace filtering
1932 } // end namespace comparison
1933 } // end namespace abigail
1934