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