• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2020 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(false);
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 a class_diff node has static members added or removed.
637 ///
638 /// @param diff the diff node to consider.
639 ///
640 /// @return true iff the class_diff node has static members added
641 /// or removed.
642 static bool
static_data_member_added_or_removed(const class_diff * diff)643 static_data_member_added_or_removed(const class_diff* diff)
644 {
645   if (diff && !diff_involves_decl_only_class(diff))
646     {
647       for (string_decl_base_sptr_map::const_iterator i =
648 	     diff->inserted_data_members().begin();
649 	   i != diff->inserted_data_members().end();
650 	   ++i)
651 	if (get_member_is_static(i->second))
652 	  return true;
653 
654       for (string_decl_base_sptr_map::const_iterator i =
655 	     diff->deleted_data_members().begin();
656 	   i != diff->deleted_data_members().end();
657 	   ++i)
658 	if (get_member_is_static(i->second))
659 	  return true;
660     }
661 
662   return false;
663 }
664 
665 /// Test if a class_diff node has a harmless "One Definition Rule"
666 /// violation that will cause a diagnostic rule.
667 ///
668 /// The conditions this function looks for are:
669 ///
670 ///  1/ The two subject of the diff must be canonically different
671 ///
672 ///  2/ The two subjects of the diff must be structurally equal
673 ///
674 ///  3/ The canonical types of the subjects of the diff must be
675 ///  structurally different.
676 ///
677 /// These conditions makes the diff node appears as it carries changes
678 /// (because of a ODR glitch present in the binary), but the glitch
679 /// has no effect on the structural equality of the subjects of the
680 /// diff.  If we do not detect these conditions, we'd end up with a
681 /// diagnostic glitch where the reporter thinks there is an ABI change
682 /// (because of the canonical difference), but then it fails to give
683 /// any detail about it, because there is no structural change.
684 ///
685 /// @param diff the diff node to consider.
686 ///
687 /// @return true iff the the diff node has a harmless "One Definition
688 /// Rule" violation that cause an empty false positive.
689 static bool
class_diff_has_harmless_odr_violation_change(const diff * dif)690 class_diff_has_harmless_odr_violation_change(const diff* dif)
691 {
692   class_diff* d =  dynamic_cast<class_diff*>(const_cast<diff*>(dif));
693   if (!d || !d->has_changes())
694     return false;
695 
696   class_decl_sptr first = d->first_class_decl();
697   class_decl_sptr second = d->second_class_decl();
698 
699   if (equals(*first, *second, 0))
700     {
701       class_decl_sptr fc = is_class_type(first->get_canonical_type());
702       class_decl_sptr sc = is_class_type(second->get_canonical_type());
703 
704       if (!equals(*fc, *sc, 0))
705 	return true;
706     }
707 
708   return false;
709 }
710 
711 /// Test if a class_diff node has static members added or
712 /// removed.
713 ///
714 /// @param diff the diff node to consider.
715 ///
716 /// @return true iff the class_diff node has static members added
717 /// or removed.
718 static bool
static_data_member_added_or_removed(const diff * diff)719 static_data_member_added_or_removed(const diff* diff)
720 {
721   return static_data_member_added_or_removed
722     (dynamic_cast<const class_diff*>(diff));
723 }
724 
725 /// Test if the class_diff node has a change involving virtual member
726 /// functions.
727 ///
728 /// That means whether there is an added, removed or changed virtual
729 /// member function.
730 ///
731 /// @param diff the class_diff node to consider.
732 ///
733 /// @return true iff the class_diff node contains changes involving
734 /// virtual member functions.
735 static bool
has_virtual_mem_fn_change(const class_diff * diff)736 has_virtual_mem_fn_change(const class_diff* diff)
737 {
738   if (!diff || diff_involves_decl_only_class(diff))
739     return false;
740 
741   for (string_member_function_sptr_map::const_iterator i =
742 	 diff->deleted_member_fns().begin();
743        i != diff->deleted_member_fns().end();
744        ++i)
745     {
746       if (get_member_function_is_virtual(i->second))
747 	{
748 	  // Do not consider a virtual function that got deleted from
749 	  // an offset and re-inserted at the same offset as a
750 	  // "virtual member function change".
751 	  string_member_function_sptr_map::const_iterator j =
752 	    diff->inserted_member_fns().find(i->first);
753 	  if (j != diff->inserted_member_fns().end()
754 	      && (get_member_function_vtable_offset(i->second)
755 		  == get_member_function_vtable_offset(j->second)))
756 	    continue;
757 
758 	  return true;
759 	}
760     }
761 
762   for (string_member_function_sptr_map::const_iterator i =
763 	 diff->inserted_member_fns().begin();
764        i != diff->inserted_member_fns().end();
765        ++i)
766     {
767       if (get_member_function_is_virtual(i->second))
768 	{
769 	  // Do not consider a virtual function that got deleted from
770 	  // an offset and re-inserted at the same offset as a
771 	  // "virtual member function change".
772 	  string_member_function_sptr_map::const_iterator j =
773 	    diff->deleted_member_fns().find(i->first);
774 	  if (j != diff->deleted_member_fns().end()
775 	      && (get_member_function_vtable_offset(i->second)
776 		  == get_member_function_vtable_offset(j->second)))
777 	    continue;
778 
779 	  return true;
780 	}
781     }
782 
783   for (function_decl_diff_sptrs_type::const_iterator i =
784 	 diff->changed_member_fns().begin();
785        i != diff->changed_member_fns().end();
786        ++i)
787     if (get_member_function_is_virtual((*i)->first_function_decl())
788 	|| get_member_function_is_virtual((*i)->second_function_decl()))
789       {
790 	if (get_member_function_vtable_offset((*i)->first_function_decl())
791 	    == get_member_function_vtable_offset((*i)->second_function_decl()))
792 	  continue;
793 
794 	return true;
795       }
796 
797   return false;
798 }
799 
800 /// Test if the function_decl_diff node has a change involving virtual
801 /// member functions.
802 ///
803 /// That means whether there is an added, removed or changed virtual
804 /// member function.
805 ///
806 /// @param diff the function_decl_diff node to consider.
807 ///
808 /// @return true iff the function_decl_diff node contains changes
809 /// involving virtual member functions.
810 bool
has_virtual_mem_fn_change(const function_decl_diff * diff)811 has_virtual_mem_fn_change(const function_decl_diff* diff)
812 {
813   if (!diff)
814     return false;
815 
816   function_decl_sptr ff = diff->first_function_decl(),
817     sf = diff->second_function_decl();
818 
819   if (!is_member_function(ff)
820       || !is_member_function(sf))
821     return false;
822 
823   bool ff_is_virtual = get_member_function_is_virtual(ff),
824     sf_is_virtual = get_member_function_is_virtual(sf);
825 
826   if (ff_is_virtual != sf_is_virtual)
827     return true;
828 
829   size_t ff_vtable_offset = get_member_function_vtable_offset(ff),
830     sf_vtable_offset = get_member_function_vtable_offset(sf);
831 
832   if (ff_vtable_offset != sf_vtable_offset)
833     return true;
834 
835   return false;
836 }
837 
838 /// Test if the class_diff node has a change involving virtual member
839 /// functions.
840 ///
841 /// That means whether there is an added, removed or changed virtual
842 /// member function.
843 ///
844 /// @param diff the class_diff node to consider.
845 ///
846 /// @return true iff the class_diff node contains changes involving
847 /// virtual member functions.
848 static bool
has_virtual_mem_fn_change(const diff * diff)849 has_virtual_mem_fn_change(const diff* diff)
850 {
851   return (has_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff))
852 	  || has_virtual_mem_fn_change(dynamic_cast<const function_decl_diff*>(diff)));
853 }
854 
855 /// Test if the class_diff has changes to non virtual member
856 /// functions.
857 ///
858 ///@param diff the class_diff nod e to consider.
859 ///
860 /// @retrurn iff the class_diff node has changes to non virtual member
861 /// functions.
862 static bool
has_non_virtual_mem_fn_change(const class_diff * diff)863 has_non_virtual_mem_fn_change(const class_diff* diff)
864 {
865   if (!diff || diff_involves_decl_only_class(diff))
866     return false;
867 
868   for (string_member_function_sptr_map::const_iterator i =
869 	 diff->deleted_member_fns().begin();
870        i != diff->deleted_member_fns().end();
871        ++i)
872     if (!get_member_function_is_virtual(i->second))
873       return true;
874 
875   for (string_member_function_sptr_map::const_iterator i =
876 	 diff->inserted_member_fns().begin();
877        i != diff->inserted_member_fns().end();
878        ++i)
879     if (!get_member_function_is_virtual(i->second))
880       return true;
881 
882   for (function_decl_diff_sptrs_type::const_iterator i =
883 	 diff->changed_member_fns().begin();
884        i != diff->changed_member_fns().end();
885        ++i)
886     if(!get_member_function_is_virtual((*i)->first_function_decl())
887        && !get_member_function_is_virtual((*i)->second_function_decl()))
888       return true;
889 
890   return false;
891 }
892 
893 /// Test if the class_diff has changes to non virtual member
894 /// functions.
895 ///
896 ///@param diff the class_diff nod e to consider.
897 ///
898 /// @retrurn iff the class_diff node has changes to non virtual member
899 /// functions.
900 static bool
has_non_virtual_mem_fn_change(const diff * diff)901 has_non_virtual_mem_fn_change(const diff* diff)
902 {return has_non_virtual_mem_fn_change(dynamic_cast<const class_diff*>(diff));}
903 
904 /// Test if a class_diff carries base classes adding or removals.
905 ///
906 /// @param diff the class_diff to consider.
907 ///
908 /// @return true iff @p diff carries base classes adding or removals.
909 static bool
base_classes_added_or_removed(const class_diff * diff)910 base_classes_added_or_removed(const class_diff* diff)
911 {
912   if (!diff)
913     return false;
914   return diff->deleted_bases().size() || diff->inserted_bases().size();
915 }
916 
917 /// Test if a class_diff carries base classes adding or removals.
918 ///
919 /// @param diff the class_diff to consider.
920 ///
921 /// @return true iff @p diff carries base classes adding or removals.
922 static bool
base_classes_added_or_removed(const diff * diff)923 base_classes_added_or_removed(const diff* diff)
924 {return base_classes_added_or_removed(dynamic_cast<const class_diff*>(diff));}
925 
926 /// Test if two classes that are decl-only (have the decl-only flag
927 /// and carry no data members) but are different just by their size.
928 ///
929 /// In some weird DWARF representation, it happens that a decl-only
930 /// class (with no data member) actually carries a non-zero size.
931 /// That shouldn't happen, but hey, we need to deal with real life.
932 /// So we need to detect that case first.
933 ///
934 /// @param first the first class or union to consider.
935 ///
936 /// @param seconf the second class or union to consider.
937 ///
938 /// @return true if the two classes are decl-only and differ in their
939 /// size.
940 bool
is_decl_only_class_with_size_change(const class_or_union & first,const class_or_union & second)941 is_decl_only_class_with_size_change(const class_or_union& first,
942 				    const class_or_union& second)
943 {
944   if (first.get_qualified_name() != second.get_qualified_name())
945     return false;
946 
947   if (!first.get_is_declaration_only() || !second.get_is_declaration_only())
948     return false;
949 
950   bool f_is_empty = first.get_data_members().empty();
951   bool s_is_empty = second.get_data_members().empty();
952 
953   return f_is_empty && s_is_empty;
954 }
955 
956 /// Test if two classes that are decl-only (have the decl-only flag
957 /// and carry no data members) but are different just by their size.
958 ///
959 /// In some weird DWARF representation, it happens that a decl-only
960 /// class (with no data member) actually carries a non-zero size.
961 /// That shouldn't happen, but hey, we need to deal with real life.
962 /// So we need to detect that case first.
963 ///
964 /// @param first the first class or union to consider.
965 ///
966 /// @param seconf the second class or union to consider.
967 ///
968 /// @return true if the two classes are decl-only and differ in their
969 /// size.
970 bool
is_decl_only_class_with_size_change(const class_or_union_sptr & first,const class_or_union_sptr & second)971 is_decl_only_class_with_size_change(const class_or_union_sptr& first,
972 				    const class_or_union_sptr& second)
973 {
974   if (!first || !second)
975     return false;
976 
977   class_or_union_sptr f = look_through_decl_only_class(first);
978   class_or_union_sptr s = look_through_decl_only_class(second);
979 
980   return is_decl_only_class_with_size_change(*f, *s);
981 }
982 
983 /// Test if a diff node is for two classes that are decl-only (have
984 /// the decl-only flag and carry no data members) but are different
985 /// just by their size.
986 ///
987 /// In some weird DWARF representation, it happens that a decl-only
988 /// class (with no data member) actually carries a non-zero size.
989 /// That shouldn't happen, but hey, we need to deal with real life.
990 /// So we need to detect that case first.
991 ///
992 /// @param diff the diff node to consider.
993 ///
994 /// @return true if the two classes are decl-only and differ in their
995 /// size.
996 bool
is_decl_only_class_with_size_change(const diff * diff)997 is_decl_only_class_with_size_change(const diff *diff)
998 {
999   const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1000   if (!d)
1001     return false;
1002 
1003   class_or_union_sptr f =
1004     look_through_decl_only_class(d->first_class_or_union());
1005   class_or_union_sptr s =
1006     look_through_decl_only_class(d->second_class_or_union());
1007 
1008   return is_decl_only_class_with_size_change(f, s);
1009 }
1010 
1011 /// Test if two @ref decl_base_sptr are different just by the
1012 /// fact that one is decl-only and the other one is defined.
1013 ///
1014 /// @param first the first decl to consider.
1015 ///
1016 /// @param second the second decl to consider.
1017 ///
1018 /// @return true iff the two arguments are different just by the fact
1019 /// that one is decl-only and the other one is defined.
1020 bool
has_decl_only_def_change(const decl_base_sptr & first,const decl_base_sptr & second)1021 has_decl_only_def_change(const decl_base_sptr& first,
1022 			 const decl_base_sptr& second)
1023 {
1024   if (!first || !second)
1025     return false;
1026 
1027   decl_base_sptr f =
1028     look_through_decl_only(first);
1029   decl_base_sptr s =
1030     look_through_decl_only(second);
1031 
1032   if (f->get_qualified_name() != s->get_qualified_name())
1033     return false;
1034 
1035   return f->get_is_declaration_only() != s->get_is_declaration_only();
1036 }
1037 
1038 /// Test if a diff carries a change in which the two decls are
1039 /// different by the fact that one is a decl-only and the other one is
1040 /// defined.
1041 ///
1042 /// @param diff the diff node to consider.
1043 ///
1044 /// @return true if the diff carries a change in which the two decls
1045 /// are different by the fact that one is a decl-only and the other
1046 /// one is defined.
1047 bool
has_decl_only_def_change(const diff * d)1048 has_decl_only_def_change(const diff *d)
1049 {
1050   if (!d)
1051     return false;
1052 
1053   decl_base_sptr f =
1054     look_through_decl_only(is_decl(d->first_subject()));
1055   decl_base_sptr s =
1056     look_through_decl_only(is_decl(d->second_subject()));
1057 
1058   return has_decl_only_def_change(f, s);
1059 }
1060 
1061 
1062 /// Test if two @ref class_or_union_sptr are different just by the
1063 /// fact that one is decl-only and the other one is defined.
1064 ///
1065 /// @param first the first class or union to consider.
1066 ///
1067 /// @param second the second class or union to consider.
1068 ///
1069 /// @return true iff the two arguments are different just by the fact
1070 /// that one is decl-only and the other one is defined.
1071 bool
has_class_decl_only_def_change(const class_or_union_sptr & first,const class_or_union_sptr & second)1072 has_class_decl_only_def_change(const class_or_union_sptr& first,
1073 			       const class_or_union_sptr& second)
1074 {
1075   if (!first || !second)
1076     return false;
1077 
1078   class_or_union_sptr f =
1079     look_through_decl_only_class(first);
1080   class_or_union_sptr s =
1081     look_through_decl_only_class(second);
1082 
1083   if (f->get_qualified_name() != s->get_qualified_name())
1084     return false;
1085 
1086   return f->get_is_declaration_only() != s->get_is_declaration_only();
1087 }
1088 
1089 /// Test if two @ref enum_sptr are different just by the
1090 /// fact that one is decl-only and the other one is defined.
1091 ///
1092 /// @param first the first enum to consider.
1093 ///
1094 /// @param second the second enum to consider.
1095 ///
1096 /// @return true iff the two arguments are different just by the fact
1097 /// that one is decl-only and the other one is defined.
1098 bool
has_enum_decl_only_def_change(const enum_type_decl_sptr & first,const enum_type_decl_sptr & second)1099 has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
1100 			      const enum_type_decl_sptr& second)
1101 {
1102   if (!first || !second)
1103     return false;
1104 
1105   enum_type_decl_sptr f = look_through_decl_only_enum(first);
1106   enum_type_decl_sptr s = look_through_decl_only_enum(second);
1107 
1108   if (f->get_qualified_name() != s->get_qualified_name())
1109     return false;
1110 
1111   return f->get_is_declaration_only() != s->get_is_declaration_only();
1112 }
1113 
1114 /// Test if a class_or_union_diff carries a change in which the two
1115 /// classes are different by the fact that one is a decl-only and the
1116 /// other one is defined.
1117 ///
1118 /// @param diff the diff node to consider.
1119 ///
1120 /// @return true if the class_or_union_diff carries a change in which
1121 /// the two classes are different by the fact that one is a decl-only
1122 /// and the other one is defined.
1123 bool
has_class_decl_only_def_change(const diff * diff)1124 has_class_decl_only_def_change(const diff *diff)
1125 {
1126   const class_or_union_diff *d = dynamic_cast<const class_or_union_diff*>(diff);
1127   if (!d)
1128     return false;
1129 
1130   class_or_union_sptr f =
1131     look_through_decl_only_class(d->first_class_or_union());
1132   class_or_union_sptr s =
1133     look_through_decl_only_class(d->second_class_or_union());
1134 
1135   return has_class_decl_only_def_change(f, s);
1136 }
1137 
1138 /// Test if a enum_diff carries a change in which the two enums are
1139 /// different by the fact that one is a decl-only and the other one is
1140 /// defined.
1141 ///
1142 /// @param diff the diff node to consider.
1143 ///
1144 /// @return true if the enum_diff carries a change in which the two
1145 /// enums are different by the fact that one is a decl-only and the
1146 /// other one is defined.
1147 bool
has_enum_decl_only_def_change(const diff * diff)1148 has_enum_decl_only_def_change(const diff *diff)
1149 {
1150   const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
1151   if (!d)
1152     return false;
1153 
1154   enum_type_decl_sptr f = look_through_decl_only_enum(d->first_enum());
1155   enum_type_decl_sptr s = look_through_decl_only_enum(d->second_enum());
1156 
1157   return has_enum_decl_only_def_change(f, s);
1158 }
1159 
1160 /// Test if a diff node carries a basic type name change.
1161 ///
1162 /// @param d the diff node to consider.
1163 ///
1164 /// @return true iff the diff node carries a basic type name change.
1165 bool
has_basic_type_name_change(const diff * d)1166 has_basic_type_name_change(const diff *d)
1167 {
1168   if (const type_decl_diff *dif = is_diff_of_basic_type(d))
1169     if (decl_name_changed(dif))
1170       return true;
1171 
1172   return false;
1173 }
1174 
1175 /// Test if a diff node carries a class or union type name change.
1176 ///
1177 /// @param d the diff node to consider.
1178 ///
1179 /// @return true iff the diff node carries a class or union type name
1180 /// change.
1181 bool
has_class_or_union_type_name_change(const diff * d)1182 has_class_or_union_type_name_change(const diff *d)
1183 {
1184   if (const class_or_union_diff *dif = is_diff_of_class_or_union_type(d))
1185     if (decl_name_changed(dif))
1186       return true;
1187 
1188   return false;
1189 }
1190 
1191 /// Test if a diff node carries a basic or class type name change.
1192 ///
1193 /// @param d the diff node to consider.
1194 ///
1195 /// @return true iff the diff node carries a basic or class type name
1196 /// change.
1197 bool
has_basic_or_class_type_name_change(const diff * d)1198 has_basic_or_class_type_name_change(const diff *d)
1199 {
1200   return (has_basic_type_name_change(d)
1201 	  || has_class_or_union_type_name_change(d));
1202 }
1203 
1204 /// Test if a diff node carries a distinct type change or a
1205 /// pointer/reference/typedef to distinct type change.
1206 ///
1207 /// Note that a distinct type change is a change where the two
1208 /// subjects of the change are not of the same kind, e.g, a basic type
1209 /// that got changed into a qualified type.
1210 ///
1211 /// @param d the diff node to consider.
1212 ///
1213 /// @return true iff @p d is mostly a distinct diff.
1214 bool
is_mostly_distinct_diff(const diff * d)1215 is_mostly_distinct_diff(const diff *d)
1216 {
1217   if (is_distinct_diff(d))
1218     return true;
1219 
1220   // Let's consider that 'd' is a type diff ...
1221   diff * td = const_cast<type_diff_base*>(is_type_diff(d));
1222   if (!td)
1223     {
1224       // ... or a function parameter diff.  In which case, let's get
1225       // its child type diff ...
1226       fn_parm_diff *pd = const_cast<fn_parm_diff*>(is_fn_parm_diff(d));
1227       if (pd)
1228 	{
1229 	  td = const_cast<type_diff_base*>(is_type_diff(pd->type_diff().get()));
1230 	  if (!td)
1231 	    // if the diff of the fn_parm_diff is a a distinct diff
1232 	    // then handle it.
1233 	    td = const_cast<distinct_diff*>
1234 	      (is_distinct_diff(pd->type_diff().get()));
1235 	}
1236       else
1237 	return false;
1238     }
1239 
1240   // At this point, if we are not looking at a type diff we must have
1241   // bailed out already.
1242   ABG_ASSERT(td);
1243 
1244   type_base_sptr first = is_type(td->first_subject());
1245   type_base_sptr second = is_type(td->second_subject());
1246 
1247   first = peel_typedef_pointer_or_reference_type(first);
1248   second = peel_typedef_pointer_or_reference_type(second);
1249   ABG_ASSERT(first && second);
1250 
1251   return distinct_diff::entities_are_of_distinct_kinds(first, second);
1252 }
1253 
1254 /// Test if a diff node carries a non-anonymous data member to
1255 /// anonymous data member change, or vice-versa.
1256 ///
1257 /// @param d the diff node to consider.
1258 ///
1259 /// @return true iff @p d carries a non-anonymous to anonymous data
1260 /// member change, or vice-versa.
1261 bool
has_anonymous_data_member_change(const diff * d)1262 has_anonymous_data_member_change(const diff *d)
1263 {
1264   if (is_anonymous_data_member(d->first_subject())
1265       || is_anonymous_data_member(d->second_subject()))
1266     return true;
1267   return false;
1268 }
1269 
1270 /// Test if a diff node carries a non-anonymous data member to
1271 /// anonymous data member change, or vice-versa.
1272 ///
1273 /// @param d the diff node to consider.
1274 ///
1275 /// @return true iff @p d carries a non-anonymous to anonymous data
1276 /// member change, or vice-versa.
1277 bool
has_anonymous_data_member_change(const diff_sptr & d)1278 has_anonymous_data_member_change(const diff_sptr &d)
1279 {return has_anonymous_data_member_change(d.get());}
1280 
1281 /// Test if an enum_diff carries an enumerator insertion.
1282 ///
1283 /// @param diff the enum_diff to consider.
1284 ///
1285 /// @return true iff @p diff carries an enumerator insertion.
1286 static bool
has_enumerator_insertion(const diff * diff)1287 has_enumerator_insertion(const diff* diff)
1288 {
1289   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1290     return !d->inserted_enumerators().empty();
1291   return false;
1292 }
1293 
1294 /// Test if an enum_diff carries an enumerator removal.
1295 ///
1296 /// @param diff the enum_diff to consider.
1297 ///
1298 /// @return true iff @p diff carries an enumerator removal or change.
1299 static bool
has_enumerator_removal_or_change(const diff * diff)1300 has_enumerator_removal_or_change(const diff* diff)
1301 {
1302   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1303     return (!d->deleted_enumerators().empty()
1304 	    || !d->changed_enumerators().empty());
1305   return false;
1306 }
1307 
1308 /// Test if an enum_diff carries a harmful change.
1309 ///
1310 /// @param diff the enum_diff to consider.
1311 ///
1312 /// @return true iff @p diff carries a harmful change.
1313 static bool
has_harmful_enum_change(const diff * diff)1314 has_harmful_enum_change(const diff* diff)
1315 {
1316   if (const enum_diff* d = dynamic_cast<const enum_diff*>(diff))
1317     return (has_enumerator_removal_or_change(d)
1318 	    || has_type_size_change(d));
1319   return false;
1320 }
1321 
1322 /// Test if a diff node carries a harmless change of an enum into an
1323 /// integer (or vice-versa).
1324 ///
1325 /// The test takes into account the fact change we care about might be
1326 /// wrapped into a typedef or qualified type diff.
1327 ///
1328 /// @param diff the diff node to consider.
1329 ///
1330 /// @return true if @p diff is a harmless enum to integer change.
1331 static bool
has_harmless_enum_to_int_change(const diff * diff)1332 has_harmless_enum_to_int_change(const diff* diff)
1333 {
1334   if (!diff)
1335     return false;
1336 
1337   diff = peel_typedef_or_qualified_type_diff(diff);
1338 
1339   if (const distinct_diff *d = is_distinct_diff(diff))
1340     {
1341       const enum_type_decl *enum_type = 0;
1342       const type_base *integer_type = 0;
1343 
1344       type_base *first_type =
1345 	peel_qualified_or_typedef_type(is_type(d->first().get()));
1346       type_base *second_type =
1347 	peel_qualified_or_typedef_type(is_type(d->second().get()));
1348 
1349       if (const enum_type_decl *e = is_enum_type(first_type))
1350 	enum_type = e;
1351       else if (const enum_type_decl *e = is_enum_type(second_type))
1352 	enum_type = e;
1353 
1354       if (const type_base * i = is_type_decl(first_type))
1355 	integer_type = i;
1356       else if (const type_base *i = is_type_decl(second_type))
1357 	integer_type = i;
1358 
1359       if (enum_type
1360 	  && integer_type
1361 	  && enum_type->get_size_in_bits() == integer_type->get_size_in_bits())
1362 	return true;
1363     }
1364 
1365   return false;
1366 }
1367 
1368 /// Test if an @ref fn_parm_diff node has a top cv qualifier change on
1369 /// the type of the function parameter.
1370 ///
1371 /// @param diff the diff node to consider.  It should be a @ref
1372 /// fn_parm_diff, otherwise the function returns 'false' directly.
1373 ///
1374 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1375 /// top cv qualifier change on the type of the function parameter.
1376 static bool
has_fn_parm_type_top_cv_qual_change(const diff * diff)1377 has_fn_parm_type_top_cv_qual_change(const diff* diff)
1378 {
1379   // is diff a "function parameter diff node?
1380   const fn_parm_diff* parm_diff = is_fn_parm_diff(diff);
1381 
1382   if (!parm_diff || !parm_diff->has_changes())
1383     // diff either carries no change or is not a function parameter
1384     // diff node.  So bail out.
1385     return false;
1386 
1387   function_decl::parameter_sptr first_parm = parm_diff->first_parameter();
1388   function_decl::parameter_sptr second_parm = parm_diff->second_parameter();
1389 
1390   type_base_sptr first_parm_type = first_parm->get_type();
1391   type_base_sptr second_parm_type = second_parm->get_type();
1392 
1393   if (!is_qualified_type(first_parm_type)
1394       && !is_qualified_type(second_parm_type))
1395     // None of the parameter types is qualified.
1396     return false;
1397 
1398   qualified_type_def::CV cv_quals_1 = qualified_type_def::CV_NONE;
1399   qualified_type_def::CV cv_quals_2 = qualified_type_def::CV_NONE;
1400   type_base_sptr peeled_type_1 = first_parm_type;
1401   type_base_sptr peeled_type_2 = second_parm_type;
1402 
1403   if (qualified_type_def_sptr qtype1 = is_qualified_type(first_parm_type))
1404     {
1405       cv_quals_1 = qtype1->get_cv_quals();
1406       peeled_type_1 = peel_qualified_type(qtype1);
1407     }
1408 
1409   if (qualified_type_def_sptr qtype2 = is_qualified_type(second_parm_type))
1410     {
1411       cv_quals_2 = qtype2->get_cv_quals();
1412       peeled_type_2 = peel_qualified_type(qtype2);
1413     }
1414 
1415   if (peeled_type_1
1416       && peeled_type_2
1417       && get_type_name(peeled_type_1) == get_type_name(peeled_type_2)
1418       && cv_quals_1 != cv_quals_2)
1419     // The top-level CV qualifiers of the function type are different
1420     // and the un-qualified variant (peeled) of said function types
1421     // are equal.  This means the only change the function types have
1422     // are about top-level CV qualifiers.
1423     return true;
1424 
1425   return false;
1426 }
1427 
1428 /// Test if a type diff only carries a CV qualifier-only change.
1429 ///
1430 /// @param type_dif the type dif to consider.
1431 ///
1432 /// @return true iff the type_diff carries a CV qualifier only change.
1433 static bool
type_diff_has_cv_qual_change_only(const diff * type_dif)1434 type_diff_has_cv_qual_change_only(const diff *type_dif)
1435 {
1436   if (!is_type_diff(type_dif))
1437     return false;
1438 
1439   if (is_pointer_diff(type_dif))
1440     type_dif = peel_pointer_diff(type_dif);
1441   else if (is_reference_diff(type_dif))
1442     type_dif = peel_reference_diff(type_dif);
1443 
1444   const type_base *f = 0;
1445   const type_base *s = 0;
1446   if (const distinct_diff *d = is_distinct_diff(type_dif))
1447     {
1448       if (is_qualified_type(d->first()) == is_qualified_type(d->second()))
1449 	return false;
1450       else
1451 	{
1452 	  f = is_type(d->first()).get();
1453 	  s = is_type(d->second()).get();
1454 	}
1455     }
1456   else if (const qualified_type_diff *d = is_qualified_type_diff(type_dif))
1457     {
1458       f = is_type(d->first_qualified_type()).get();
1459       s = is_type(d->second_qualified_type()).get();
1460     }
1461   else
1462     return false;
1463 
1464   f = peel_qualified_type(f);
1465   s = peel_qualified_type(s);
1466 
1467   // If f and s are arrays, note that they can differ only by the cv
1468   // qualifier of the array element type.  That cv qualifier is not
1469   // removed by peel_qualified_type.  So we need to test this case
1470   // specifically.
1471   if (array_type_def *f_a = is_array_type(f))
1472     if (array_type_def *s_a = is_array_type(s))
1473       return equals_modulo_cv_qualifier(f_a, s_a);
1474 
1475   return *f == *s;
1476 }
1477 
1478 /// Test if an @ref fn_parm_diff node has a cv qualifier change on the
1479 /// type of the function parameter. That is, we are looking for
1480 /// changes like 'const char*' to 'char*'.
1481 ///
1482 /// @param diff the diff node to consider.  It should be a @ref
1483 /// fn_parm_diff, otherwise the function returns 'false' directly.
1484 ///
1485 /// @return true iff @p diff is a @ref fn_parm_diff node that has a
1486 /// cv qualifier change on the type of the function parameter.
1487 static bool
has_fn_parm_type_cv_qual_change(const diff * dif)1488 has_fn_parm_type_cv_qual_change(const diff* dif)
1489 {
1490   // is diff a "function parameter diff node?
1491   const fn_parm_diff* parm_diff = is_fn_parm_diff(dif);
1492 
1493   if (!parm_diff || !parm_diff->has_changes())
1494     // diff either carries no change or is not a function parameter
1495     // diff node.  So bail out.
1496     return false;
1497 
1498   const diff *type_dif = parm_diff->type_diff().get();
1499   return type_diff_has_cv_qual_change_only(type_dif);
1500 }
1501 
1502 /// Test if a function type or decl diff node carries a CV
1503 /// qualifier-only change on its return type.
1504 ///
1505 /// @param dif the diff node to consider.  Note that if this is
1506 /// neither a function type nor decl diff node, the function returns
1507 /// false.
1508 ///
1509 /// @return true iff @p dif is a function decl or type diff node which
1510 /// carries a CV qualifier-only change on its return type.
1511 static bool
has_fn_return_type_cv_qual_change(const diff * dif)1512 has_fn_return_type_cv_qual_change(const diff* dif)
1513 {
1514   const function_type_diff* fn_type_diff = is_function_type_diff(dif);
1515   if (!fn_type_diff)
1516     if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1517       fn_type_diff = fn_decl_diff->type_diff().get();
1518 
1519   if (!fn_type_diff)
1520     return false;
1521 
1522   const diff* return_type_diff = fn_type_diff->return_type_diff().get();
1523   return type_diff_has_cv_qual_change_only(return_type_diff);
1524 }
1525 
1526 /// Test if a function type or decl diff node carries a function
1527 /// parameter addition or removal.
1528 ///
1529 /// @param dif the diff node to consider.  Note that if this is
1530 /// neither a function type nor decl diff node, the function returns
1531 /// false.
1532 ///
1533 /// @return true iff @p dif is a function decl or type diff node which
1534 /// carries a function parameter addition or removal.
1535 static bool
has_added_or_removed_function_parameters(const diff * dif)1536 has_added_or_removed_function_parameters(const diff *dif)
1537 {
1538   const function_type_diff *fn_type_diff = is_function_type_diff(dif);
1539     if (!fn_type_diff)
1540     if (const function_decl_diff* fn_decl_diff = is_function_decl_diff(dif))
1541       fn_type_diff = fn_decl_diff->type_diff().get();
1542 
1543   if (!fn_type_diff)
1544     return false;
1545 
1546   if (!(fn_type_diff->sorted_deleted_parms().empty()
1547 	&& fn_type_diff->sorted_added_parms().empty()))
1548     return true;
1549 
1550   return false;
1551 }
1552 
1553 /// Test if a variable diff node carries a CV qualifier change on its type.
1554 ///
1555 /// @param dif the diff node to consider.  Note that if it's not of
1556 /// var_diff type, the function returns false.
1557 ///
1558 /// @return true iff the @p dif carries a CV qualifier change on its
1559 /// type.
1560 static bool
has_var_type_cv_qual_change(const diff * dif)1561 has_var_type_cv_qual_change(const diff* dif)
1562 {
1563   const var_diff *var_dif = is_var_diff(dif);
1564   if (!var_dif)
1565     return false;
1566 
1567   {
1568     // Make sure the variable diff does carry a type change at least
1569     change_kind ch_kind = NO_CHANGE_KIND;
1570     if (equals(*var_dif->first_var(), *var_dif->second_var(), &ch_kind))
1571       return false;
1572 
1573     if (!(ch_kind & LOCAL_TYPE_CHANGE_KIND || ch_kind & SUBTYPE_CHANGE_KIND))
1574       return false;
1575   }
1576 
1577   diff *type_dif = var_dif->type_diff().get();
1578   ABG_ASSERT(type_dif);
1579 
1580   return type_diff_has_cv_qual_change_only(type_dif);
1581 }
1582 
1583 /// Test if a diff node carries a void* to pointer type change.
1584 ///
1585 /// Note that this function looks through typedef and qualifier types
1586 /// to find the void pointer.
1587 ///
1588 /// @param dif the diff node to consider.
1589 ///
1590 /// @return true iff @p dif carries a void* to pointer type change.
1591 static bool
has_void_ptr_to_ptr_change(const diff * dif)1592 has_void_ptr_to_ptr_change(const diff* dif)
1593 {
1594   dif = peel_typedef_diff(dif);
1595 
1596   if (const distinct_diff *d = is_distinct_diff(dif))
1597     {
1598       const type_base *f = is_type(d->first().get());
1599       const type_base *s = is_type(d->second().get());
1600 
1601       f = peel_qualified_or_typedef_type(f);
1602       s = peel_qualified_or_typedef_type(s);
1603 
1604       if (is_void_pointer_type(f)
1605 	  && is_pointer_type(s)
1606 	  && !is_void_pointer_type(s)
1607 	  && f->get_size_in_bits() == s->get_size_in_bits())
1608 	return true;
1609     }
1610   else if (const pointer_diff *d = is_pointer_diff(dif))
1611     {
1612       const type_base *f = is_type(d->first_pointer()).get();
1613       const type_base *s = is_type(d->second_pointer()).get();
1614 
1615       f = peel_qualified_or_typedef_type(f);
1616       s = peel_qualified_or_typedef_type(s);
1617 
1618       if (is_void_pointer_type(f)
1619 	  && is_pointer_type(s)
1620 	  && !is_void_pointer_type(s)
1621 	  && f->get_size_in_bits() == s->get_size_in_bits())
1622 	return true;
1623     }
1624   else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
1625     {
1626       const type_base *f = is_type(d->first_qualified_type()).get();
1627       const type_base *s = is_type(d->second_qualified_type()).get();
1628 
1629       f = peel_qualified_or_typedef_type(f);
1630       s = peel_qualified_or_typedef_type(s);
1631 
1632       if (is_void_pointer_type(f)
1633 	  && is_pointer_type(s)
1634 	  && !is_void_pointer_type(s)
1635 	  && f->get_size_in_bits() == s->get_size_in_bits())
1636 	return true;
1637     }
1638 
1639   return false;
1640 }
1641 
1642 /// Test if a diff node carries a benign change to the size of a
1643 /// variable of type array.
1644 ///
1645 /// A benign size change is a change in size (from or to infinite) of
1646 /// the array as expressed by the debug info, but when the *ELF* size
1647 /// (what really matters) of the variable object hasn't changed.  This
1648 /// happens when the debug info emitter did have trouble figuring out
1649 /// the actual size of the array.
1650 ///
1651 /// @param dif the diff node to consider.
1652 ///
1653 /// @return true iff @p dif contains the benign array type size change.
1654 static bool
has_benign_infinite_array_change(const diff * dif)1655 has_benign_infinite_array_change(const diff* dif)
1656 {
1657   if (const var_diff* var_dif = is_var_diff(dif))
1658     {
1659       if (!var_dif->first_var()->get_symbol()
1660 	  || var_dif->second_var()->get_symbol())
1661 	return false;
1662 
1663       if (var_dif->first_var()->get_symbol()->get_size()
1664 	  != var_dif->second_var()->get_symbol()->get_size())
1665 	return false;
1666 
1667       const diff *d = var_dif->type_diff().get();
1668       if (!d)
1669 	return false;
1670       d = peel_qualified_diff(d);
1671       if (const array_diff *a = is_array_diff(d))
1672 	{
1673 	  array_type_def_sptr f = a->first_array(), s = a->second_array();
1674 	  if (f->is_infinite() != s->is_infinite())
1675 	    return true;
1676 	}
1677     }
1678   return false;
1679 }
1680 
1681 /// Test if a union diff node does have changes that don't impact its
1682 /// size.
1683 ///
1684 /// @param d the union diff node to consider.
1685 ///
1686 /// @return true iff @p d is a diff node which has changes that don't
1687 /// impact its size.
1688 bool
union_diff_has_harmless_changes(const diff * d)1689 union_diff_has_harmless_changes(const diff *d)
1690 {
1691   if (is_union_diff(d)
1692       && d->has_changes()
1693       && !has_type_size_change(d))
1694     return true;
1695 
1696   return false;
1697 }
1698 
1699 /// Detect if the changes carried by a given diff node are deemed
1700 /// harmless and do categorize the diff node accordingly.
1701 ///
1702 /// @param d the diff node being visited.
1703 ///
1704 /// @param pre this is true iff the node is being visited *before* the
1705 /// children nodes of @p d.
1706 ///
1707 /// @return true iff the traversal shall keep going after the
1708 /// completion of this function.
1709 static bool
categorize_harmless_diff_node(diff * d,bool pre)1710 categorize_harmless_diff_node(diff *d, bool pre)
1711 {
1712   if (!d->has_changes())
1713     return true;
1714 
1715   if (pre)
1716     {
1717       diff_category category = NO_CHANGE_CATEGORY;
1718 
1719       decl_base_sptr f = is_decl(d->first_subject()),
1720 	s = is_decl(d->second_subject());
1721 
1722       if (has_class_decl_only_def_change(d)
1723 	  || has_enum_decl_only_def_change(d))
1724 	category |= TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY;
1725 
1726       if (access_changed(f, s))
1727 	category |= ACCESS_CHANGE_CATEGORY;
1728 
1729       if (is_compatible_change(f, s))
1730 	category |= COMPATIBLE_TYPE_CHANGE_CATEGORY;
1731 
1732       if (has_harmless_name_change(f, s)
1733 	  || class_diff_has_harmless_odr_violation_change(d))
1734 	category |= HARMLESS_DECL_NAME_CHANGE_CATEGORY;
1735 
1736       if (union_diff_has_harmless_changes(d))
1737 	category |= HARMLESS_UNION_CHANGE_CATEGORY;
1738 
1739       if (has_non_virtual_mem_fn_change(d))
1740 	category |= NON_VIRT_MEM_FUN_CHANGE_CATEGORY;
1741 
1742       if (static_data_member_added_or_removed(d)
1743 	  || static_data_member_type_size_changed(f, s))
1744 	category |= STATIC_DATA_MEMBER_CHANGE_CATEGORY;
1745 
1746       if (has_data_member_replaced_by_anon_dm(d))
1747 	category |= HARMLESS_DATA_MEMBER_CHANGE_CATEGORY;
1748 
1749       if ((has_enumerator_insertion(d)
1750 	   && !has_harmful_enum_change(d))
1751 	  || has_harmless_enum_to_int_change(d))
1752 	category |= HARMLESS_ENUM_CHANGE_CATEGORY;
1753 
1754       if (function_name_changed_but_not_symbol(d))
1755 	category |= HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY;
1756 
1757       if (has_fn_parm_type_top_cv_qual_change(d))
1758 	category |= FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY;
1759 
1760       if (has_fn_parm_type_cv_qual_change(d))
1761 	category |= FN_PARM_TYPE_CV_CHANGE_CATEGORY;
1762 
1763       if (has_fn_return_type_cv_qual_change(d))
1764 	category |= FN_RETURN_TYPE_CV_CHANGE_CATEGORY;
1765 
1766       if (has_var_type_cv_qual_change(d))
1767 	category |= VAR_TYPE_CV_CHANGE_CATEGORY;
1768 
1769       if (has_void_ptr_to_ptr_change(d))
1770 	category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
1771 
1772       if (has_benign_infinite_array_change(d))
1773 	category |= BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY;
1774 
1775       if (category)
1776 	{
1777 	  d->add_to_local_and_inherited_categories(category);
1778 	  // Also update the category of the canonical node.
1779 	  if (diff * canonical = d->get_canonical_diff())
1780 	    canonical->add_to_local_and_inherited_categories(category);
1781 	}
1782     }
1783 
1784   return true;
1785 }
1786 
1787 /// Detect if the changes carried by a given diff node are deemed
1788 /// harmful and do categorize the diff node accordingly.
1789 ///
1790 /// @param d the diff node being visited.
1791 ///
1792 /// @param pre this is true iff the node is being visited *before* the
1793 /// children nodes of @p d.
1794 ///
1795 /// @return true iff the traversal shall keep going after the
1796 /// completion of this function.
1797 static bool
categorize_harmful_diff_node(diff * d,bool pre)1798 categorize_harmful_diff_node(diff *d, bool pre)
1799 {
1800   if (!d->has_changes())
1801     return true;
1802 
1803   if (pre)
1804     {
1805       diff_category category = NO_CHANGE_CATEGORY;
1806       decl_base_sptr f = is_decl(d->first_subject()),
1807 	s = is_decl(d->second_subject());
1808 
1809       // Detect size or offset changes as well as data member addition
1810       // or removal.
1811       //
1812       // TODO: be more specific -- not all size changes are harmful.
1813       if (!has_class_decl_only_def_change(d)
1814 	  && !has_enum_decl_only_def_change(d)
1815 	  && (type_size_changed(f, s)
1816 	      || data_member_offset_changed(f, s)
1817 	      || non_static_data_member_type_size_changed(f, s)
1818 	      || non_static_data_member_added_or_removed(d)
1819 	      || base_classes_added_or_removed(d)
1820 	      || has_harmful_enum_change(d)
1821 	      || crc_changed(d)
1822 	      || namespace_changed(d)))
1823 	category |= SIZE_OR_OFFSET_CHANGE_CATEGORY;
1824 
1825       if (has_virtual_mem_fn_change(d))
1826 	category |= VIRTUAL_MEMBER_CHANGE_CATEGORY;
1827 
1828       if (has_added_or_removed_function_parameters(d))
1829 	category |= FN_PARM_ADD_REMOVE_CHANGE_CATEGORY;
1830 
1831       if (category)
1832 	{
1833 	  d->add_to_local_and_inherited_categories(category);
1834 	  // Update the category of the canonical diff node too.
1835 	  if (diff * canonical = d->get_canonical_diff())
1836 	    canonical->add_to_local_and_inherited_categories(category);
1837 	}
1838     }
1839 
1840   return true;
1841 }
1842 
1843 /// The visiting code of the harmless_harmful_filter.
1844 ///
1845 /// @param d the diff node being visited.
1846 ///
1847 /// @param pre this is true iff the node is being visited *before* the
1848 /// children nodes of @p d.
1849 ///
1850 /// @return true iff the traversal shall keep going after the
1851 /// completion of this function.
1852 bool
visit(diff * d,bool pre)1853 harmless_harmful_filter::visit(diff* d, bool pre)
1854 {
1855   return (categorize_harmless_diff_node(d, pre)
1856 	  && categorize_harmful_diff_node(d, pre));
1857 }
1858 
1859 /// Part of the visiting code of the harmless_harmful_filter.
1860 ///
1861 /// This function is called after the visiting of a given diff node.
1862 /// Note that when this function is called, the visiting might not
1863 /// have taken place *if* the node (or an equivalent node) has already
1864 /// been visited.
1865 ///
1866 /// @param d the diff node that has either been visited or skipped
1867 /// (because it has already been visited during this traversing).
1868 void
visit_end(diff * d)1869 harmless_harmful_filter::visit_end(diff* d)
1870 {
1871   if (d->context()->diff_has_been_visited(d))
1872     {
1873       // This node or one of its equivalent node has already been
1874       // visited.  That means at this moment,
1875       // harmless_harmful_filter::visit() has *not* been called prior
1876       // to this harmless_harmful_filter::visit_end() is called.  In
1877       // other words, only harmless_harmful_filter::visit_begin() and
1878       // harmless_harmful_filter::visit_end() are called.
1879       //
1880       // So let's update the category of this diff node from its
1881       // canonical node.
1882       if (diff* c = d->get_canonical_diff())
1883 	d->add_to_local_and_inherited_categories(c->get_local_category());
1884     }
1885 }
1886 } // end namespace filtering
1887 } // end namespace comparison
1888 } // end namespace abigail
1889