protobuf/reflect/message/
is_initialized_is_always_true.rs1use std::collections::HashMap;
2use std::collections::HashSet;
3
4use crate::descriptor::field_descriptor_proto::Label;
5use crate::descriptor::FileDescriptorProto;
6use crate::reflect::field::index::FieldIndex;
7use crate::reflect::field::index::ForwardProtobufFieldType;
8use crate::reflect::field::index::ForwardProtobufTypeBox;
9use crate::reflect::file::index::MessageIndices;
10use crate::reflect::MessageDescriptor;
11use crate::reflect::RuntimeType;
12use crate::reflect::Syntax;
13
14pub(crate) fn compute_is_initialized_is_always_true(
15 messages: &mut [MessageIndices],
16 file_fields: &[FieldIndex],
17 file: &FileDescriptorProto,
18) {
19 for message in messages.iter_mut() {
20 message.is_initialized_is_always_true =
21 is_initialized_is_always_true_ignoring_deps(message, file);
22 }
23
24 let mut rdeps: HashMap<usize, Vec<usize>> = HashMap::new();
31
32 for i in 0..messages.len() {
33 let message = &mut messages[i];
34
35 if !message.is_initialized_is_always_true {
36 continue;
37 }
38
39 let mut is_initialized_is_always_true = true;
40 for ft in message_field_messages(message, file_fields) {
41 match ft {
42 MessageType::ThisFile(j) => {
43 rdeps.entry(j).or_default().push(i);
44 }
45 MessageType::OtherFile(m) => {
46 if !m.is_initialized_is_always_true() {
47 is_initialized_is_always_true = false;
48 }
49 }
50 }
51 }
52 message.is_initialized_is_always_true = is_initialized_is_always_true;
53 }
54
55 let mut invalidated: HashSet<usize> = HashSet::new();
56 let mut invalidate_stack: Vec<usize> = Vec::new();
57
58 for i in 0..messages.len() {
59 let message = &messages[i];
60 if message.is_initialized_is_always_true {
61 continue;
62 }
63
64 invalidate_stack.push(i);
65 }
66
67 while let Some(i) = invalidate_stack.pop() {
68 if !invalidated.insert(i) {
69 continue;
70 }
71
72 messages[i].is_initialized_is_always_true = false;
73 let next = rdeps.get(&i).map(|v| v.as_slice()).unwrap_or_default();
74 for next in next {
75 invalidate_stack.push(*next);
76 }
77 }
78}
79
80enum MessageType<'m> {
81 ThisFile(usize),
82 OtherFile(&'m MessageDescriptor),
83}
84
85fn message_field_messages<'a>(
86 message: &'a MessageIndices,
87 file_fields: &'a [FieldIndex],
88) -> impl Iterator<Item = MessageType<'a>> + 'a {
89 message_field_types(message, file_fields).filter_map(|f| match f {
90 ForwardProtobufTypeBox::ProtobufTypeBox(t) => match t.runtime() {
91 RuntimeType::Message(m) => Some(MessageType::OtherFile(m)),
92 _ => None,
93 },
94 ForwardProtobufTypeBox::CurrentFileEnum(_) => None,
95 ForwardProtobufTypeBox::CurrentFileMessage(i) => Some(MessageType::ThisFile(*i)),
96 })
97}
98
99fn message_field_types<'a>(
100 message: &'a MessageIndices,
101 file_fields: &'a [FieldIndex],
102) -> impl Iterator<Item = &'a ForwardProtobufTypeBox> {
103 enum Either<A, B> {
104 Left(A),
105 Right(B),
106 }
107
108 impl<T, A: Iterator<Item = T>, B: Iterator<Item = T>> Iterator for Either<A, B> {
109 type Item = T;
110
111 fn next(&mut self) -> Option<T> {
112 match self {
113 Either::Left(a) => a.next(),
114 Either::Right(b) => b.next(),
115 }
116 }
117 }
118
119 message
120 .message_index
121 .slice_fields(file_fields)
122 .iter()
123 .flat_map(|f| match &f.field_type {
124 ForwardProtobufFieldType::Singular(t) => Either::Left([t].into_iter()),
125 ForwardProtobufFieldType::Repeated(t) => Either::Left([t].into_iter()),
126 ForwardProtobufFieldType::Map(k, v) => Either::Right([k, v].into_iter()),
127 })
128}
129
130fn is_initialized_is_always_true_ignoring_deps(
131 message: &MessageIndices,
132 file: &FileDescriptorProto,
133) -> bool {
134 if Syntax::of_file(file) == Syntax::Proto3 {
136 return true;
137 }
138
139 if !message.proto.extension_range.is_empty() {
142 return false;
143 }
144
145 for field in &message.proto.field {
146 if field.label() == Label::LABEL_REQUIRED {
147 return false;
148 }
149 }
150 true
151}