proptest_derive/void.rs
1// Copyright 2018 The proptest developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Provides the `IsUninhabited` trait.
10//!
11//! By nature, determining if a type is uninhabited or not given Rust's
12//! turing complete type system is undecidable. Furthermore, we don't even
13//! have access to all the information because we can't inspect type
14//! definitions, type macros, or projections via associated types.
15//!
16//! Any analysis we perform here is therefore incomplete but sound.
17//! That is, if we state that a type is uninhabited, it is so for sure.
18//! But we can't state that all uninhabited types are uninhabited.
19
20use syn::{self, visit};
21
22use crate::interp;
23use crate::util;
24
25//==============================================================================
26// Trait
27//==============================================================================
28
29/// A trait for types for which it is possible to check if the modelled
30/// object is uninhabited or not. A `false` answer means that we can not
31/// tell for sure that the thing is uninhabited, not that we are 100%
32/// certain that it is inhabited.
33pub trait IsUninhabited {
34 /// Returns true if the given type is known to be uninhabited.
35 /// There may be more scenarios under which the type is uninhabited.
36 /// Thus, this is not a complete and exhaustive check.
37 fn is_uninhabited(&self) -> bool;
38}
39
40//==============================================================================
41// Enum/Variants:
42//==============================================================================
43
44impl IsUninhabited for syn::DataEnum {
45 fn is_uninhabited(&self) -> bool {
46 self.variants.is_uninhabited()
47 }
48}
49
50impl<P> IsUninhabited for syn::punctuated::Punctuated<syn::Variant, P> {
51 fn is_uninhabited(&self) -> bool {
52 self.iter().all(IsUninhabited::is_uninhabited)
53 }
54}
55
56impl<'a> IsUninhabited for &'a [syn::Variant] {
57 fn is_uninhabited(&self) -> bool {
58 self.iter().all(IsUninhabited::is_uninhabited)
59 }
60}
61
62impl IsUninhabited for syn::Variant {
63 fn is_uninhabited(&self) -> bool {
64 self.fields.is_uninhabited()
65 }
66}
67
68//==============================================================================
69// Struct/Fields:
70//==============================================================================
71
72impl IsUninhabited for syn::Fields {
73 fn is_uninhabited(&self) -> bool {
74 self.iter().any(syn::Field::is_uninhabited)
75 }
76}
77
78impl<'a> IsUninhabited for &'a [syn::Field] {
79 fn is_uninhabited(&self) -> bool {
80 self.iter().any(syn::Field::is_uninhabited)
81 }
82}
83
84impl IsUninhabited for syn::Field {
85 fn is_uninhabited(&self) -> bool {
86 self.ty.is_uninhabited()
87 }
88}
89
90//==============================================================================
91// Types:
92//==============================================================================
93
94impl IsUninhabited for syn::Type {
95 fn is_uninhabited(&self) -> bool {
96 let mut uninhabited = Uninhabited(false);
97 visit::visit_type(&mut uninhabited, &self);
98 uninhabited.0
99 }
100}
101
102/// Tracks uninhabitedness.
103struct Uninhabited(bool);
104
105impl Uninhabited {
106 /// Set to uninhabited.
107 fn set(&mut self) {
108 self.0 = true;
109 }
110}
111
112// We are more strict than Rust is.
113// Our notion of uninhabited is if the type is generatable or not.
114// The second a type like *const ! is dereferenced you have UB.
115
116impl<'ast> visit::Visit<'ast> for Uninhabited {
117 //------------------------------------------------------------------
118 // If we get to one of these we have a knowably uninhabited type:
119 //------------------------------------------------------------------
120
121 // The ! (never) type is obviously uninhabited:
122 fn visit_type_never(&mut self, _: &'ast syn::TypeNever) {
123 self.set();
124 }
125
126 // A path is uninhabited if we get one we know is uninhabited.
127 // Even if `T` in `<T as Trait>::Item` is uninhabited, the associated item
128 // may be inhabited, so we can't say for sure that it is uninhabited.
129 fn visit_type_path(&mut self, type_path: &'ast syn::TypePath) {
130 const KNOWN_UNINHABITED: &[&str] =
131 &["std::string::ParseError", "::std::string::ParseError"];
132
133 if type_path.qself.is_none()
134 && util::match_pathsegs(&type_path.path, KNOWN_UNINHABITED)
135 {
136 self.set();
137 }
138 }
139
140 // An array is uninhabited iff: `[T; N]` where uninhabited(T) && N != 0
141 // We want to block decent if N == 0.
142 fn visit_type_array(&mut self, arr: &'ast syn::TypeArray) {
143 if let Some(len) = interp::eval_expr(&arr.len) {
144 if len > 0 {
145 self.visit_type(&arr.elem);
146 }
147 }
148 }
149
150 //------------------------------------------------------------------
151 // These are here to block decent:
152 //------------------------------------------------------------------
153
154 // An fn(I) -> O is never uninhabited even if I or O are:
155 fn visit_type_bare_fn(&mut self, _: &'ast syn::TypeBareFn) {}
156
157 // A macro may transform the inner type in ways we can't predict:
158 fn visit_macro(&mut self, _: &'ast syn::Macro) {}
159
160 // Both of these could be, but type is anonymous:
161 fn visit_type_impl_trait(&mut self, _: &'ast syn::TypeImplTrait) {}
162 fn visit_type_trait_object(&mut self, _: &'ast syn::TypeTraitObject) {}
163}