1 use std::collections::HashMap;
2 use std::collections::HashSet;
3 
4 use crate::descriptor::field_descriptor_proto::Label;
5 use crate::descriptor::FileDescriptorProto;
6 use crate::reflect::field::index::FieldIndex;
7 use crate::reflect::field::index::ForwardProtobufFieldType;
8 use crate::reflect::field::index::ForwardProtobufTypeBox;
9 use crate::reflect::file::index::MessageIndices;
10 use crate::reflect::MessageDescriptor;
11 use crate::reflect::RuntimeType;
12 use crate::reflect::Syntax;
13 
compute_is_initialized_is_always_true( messages: &mut [MessageIndices], file_fields: &[FieldIndex], file: &FileDescriptorProto, )14 pub(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     // Map from a message to messages who include it. E.g. for:
25     // ```
26     // 0: message A {}
27     // 1: message B { A a = 10; }
28     // ```
29     // This map will contain: `{0: [1]}`
30     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 
80 enum MessageType<'m> {
81     ThisFile(usize),
82     OtherFile(&'m MessageDescriptor),
83 }
84 
message_field_messages<'a>( message: &'a MessageIndices, file_fields: &'a [FieldIndex], ) -> impl Iterator<Item = MessageType<'a>> + 'a85 fn 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 
message_field_types<'a>( message: &'a MessageIndices, file_fields: &'a [FieldIndex], ) -> impl Iterator<Item = &'a ForwardProtobufTypeBox>99 fn 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 
is_initialized_is_always_true_ignoring_deps( message: &MessageIndices, file: &FileDescriptorProto, ) -> bool130 fn is_initialized_is_always_true_ignoring_deps(
131     message: &MessageIndices,
132     file: &FileDescriptorProto,
133 ) -> bool {
134     // Shortcut.
135     if Syntax::of_file(file) == Syntax::Proto3 {
136         return true;
137     }
138 
139     // We don't support extensions properly but if we did,
140     // extensions should have been checked for `is_initialized`.
141     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 }
152