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