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