1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
//! This module contains all representations of paths in the AST.
//!
//! See: <https://doc.rust-lang.org/stable/reference/paths.html>

use crate::{
    ast::{generic::GenericArgs, ty::TyKind},
    common::{GenericId, ItemId, VarId, VariantId},
    ffi::{FfiOption, FfiSlice},
    span::Ident,
};

/// [`AstPath`]s are used to identify items. A qualified path (`QPath`) can be
/// used in expressions and types to identify associated items on types. For
/// traits it's additionally possible to specify the type that should be used
/// as `Self`. This is sometimes needed to disambiguate an item, if it exists
/// both as an associated item on a type, and as an associated item on traits,
/// that this type implements.
///
/// In the following example, the `Item` has two implementations of the associated
/// `foo()` function. One is provided by the `impl` block and the other one by the
/// `Foo` trait. When calling `Item::foo()` the `impl` block implementation will
/// targeted. To access the trait function, the path has to be specified, with `Item`
/// declared as the `Self` type.
///
/// ```
/// // Item
/// struct Item;
/// impl Item {
///     fn foo() {
///         println!("foo() from Item")
///     }
/// }
///
/// // trait
/// trait Foo {
///     fn foo();
/// }
/// impl Foo for Item {
///     fn foo() {
///         println!("foo() from Foo trait");
///     }
/// }
///
/// // Calls the `foo()` method of `impl Item`
/// Item::foo(); // -> "foo() from Item"
///
/// // Calls the `foo()` method of `trait Foo` with `Item` as the `Self` type
/// <Item as Foo>::foo(); // -> "foo() from Foo trait"
/// ```
///
/// This representation can also be used to reference non-associated items, to
/// make it more flexible. For these items, the path type will be [`None`]. The
/// target can be resolved via the [`resolve()`](AstQPath::resolve) method.
/// Alternatively, the [`AstPath`] representation can be accessed via
/// [`as_path_lossy()`](AstQPath::as_path_lossy) or the [`TryInto`] implementation.
#[repr(C)]
#[derive(Debug)]
pub struct AstQPath<'ast> {
    self_ty: FfiOption<TyKind<'ast>>,
    path_ty: FfiOption<TyKind<'ast>>,
    path: AstPath<'ast>,
    target: AstPathTarget,
}

impl<'ast> AstQPath<'ast> {
    /// This method will return [`Some`], if the path has a specified `Self`
    /// type. The main type for type relative paths is provided by
    /// [`path_ty()`][`AstQPath::path_ty()`].
    ///
    /// ```
    /// # let _: Vec<i32> =
    ///     <Vec<_> as Default>::default();
    /// //   ^^^^^^ The specified `Self` type `Vec<_>`
    /// ```
    ///
    /// The [`AstQPath`] description contains more details, when this might
    /// be necessary.
    pub fn self_ty(&self) -> Option<TyKind<'ast>> {
        self.self_ty.copy()
    }

    /// This method will return [`Some`], if the path is type relative.
    ///
    /// ```
    /// # let _: Vec<i32> =
    ///     <Vec<_> as Default>::default();
    /// //             ^^^^^^^ The path is relative to the `Default` trait
    /// ```
    ///
    /// The optional `Self` type can be accessed via [`self_ty`](AstQPath::self_ty()).
    pub fn path_ty(&self) -> Option<TyKind<'ast>> {
        self.path_ty.copy()
    }

    /// This returns a [`AstPath`] of the referenced item. For type relative
    /// paths, this will include the type itself, if the type can be expressed
    /// as a [`AstPathSegment`]. For some types l,ike slices, this is not possible.
    /// The type has to be retrieved from [`path_ty()`][`AstQPath::path_ty()`].
    ///
    /// ### Examples:
    ///
    /// ```
    /// let _: Vec<i32> = Vec::default();
    /// // AstPath: `Vec::default`
    ///
    /// let _: Vec<i32> = Default::default();
    /// // AstPath: `Default::default`
    ///
    /// let _: Vec<i32> = <Vec<_> as Default>::default();
    /// // AstPath: `Default::default`
    ///
    ///  let _ = [0_u8].is_ascii();
    /// // AstPath: `is_ascii`
    /// ```
    ///
    /// ### Warning
    ///
    /// The method is lossy, as the optional `Self` type isn't included in this
    /// path. The path type might also be missing, if it can't be represented as
    /// a [`AstPathSegment`]. The conversion is lossless, if both types are none
    /// or if the path type can be represented. To resolve a qualified path
    /// [`resolve()`](Self::resolve()) should be used.
    ///
    /// Omitting the `Self` type can be useful, in cases, where access to associated
    /// trait items should be analyzed, regardless of potential `Self` types.
    /// Alternatively, [`segments()`](AstQPath::segments()) can be used to access the
    /// segments directly.
    pub fn as_path_lossy(&self) -> &AstPath<'ast> {
        &self.path
    }

    /// This returns the [`AstPathSegment`]s of the path. For type relative
    /// paths, this will include the type itself. The optional `Self` type
    /// isn't represented in these segments. These segments are identical with
    /// the segments provided by the path of
    /// [`as_path_lossy()`](AstQPath::as_path_lossy()). The documentation
    /// of that function contains more details.
    pub fn segments(&self) -> &[AstPathSegment<'ast>] {
        self.path.segments()
    }

    /// This function resolves the target of this path.
    pub fn resolve(&self) -> AstPathTarget {
        // For rust-analyzer or future drivers, it might make sense to return
        // `Option<AstPathTarget>` instead, as the path might be dead,
        // when a lint crate calls this function. However, I have the feeling
        // that this would make the API less ergonomic. The `MarkerContext` will
        // already need to handle these cases explicitly. Currently, a user can
        // get a resolved id from the target, but the resolution of the ID, by
        // the `MarkerContext`, might fail. The outcome is the same, but all
        // "failable" resolution will be grouped in the `MarkerContext`
        self.target
    }

    /// This returns the [`GenericArgs`] specified on the last segment of the path.
    /// This is especially useful, for paths pointing to types or functions. For
    /// example, the `u32` of the path `Vec<u32>`, is stored in the [`GenericArgs`]
    /// as a type parameter.
    pub fn generics(&self) -> &GenericArgs<'ast> {
        self.path.generics()
    }
}

impl<'a, 'ast> TryFrom<&'a AstQPath<'ast>> for &'a AstPath<'ast> {
    type Error = ();

    fn try_from(value: &'a AstQPath<'ast>) -> Result<Self, Self::Error> {
        fn is_segment_representable(ty: Option<TyKind<'_>>) -> bool {
            if let Some(ty) = ty {
                ty.is_primitive_ty()
                    || matches!(ty, TyKind::Path(path_ty) if is_segment_representable(path_ty.path().path_ty()))
            } else {
                true
            }
        }
        if value.self_ty.is_some() && is_segment_representable(value.path_ty.copy()) {
            Err(())
        } else {
            Ok(&value.path)
        }
    }
}

#[cfg(feature = "driver-api")]
impl<'ast> AstQPath<'ast> {
    pub fn new(
        self_ty: Option<TyKind<'ast>>,
        path_ty: Option<TyKind<'ast>>,
        path: AstPath<'ast>,
        target: AstPathTarget,
    ) -> Self {
        Self {
            self_ty: self_ty.into(),
            path_ty: path_ty.into(),
            path,
            target,
        }
    }
}

#[repr(C)]
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum AstPathTarget {
    /// The `Self` type, the [`ItemId`] points to the item,
    /// that the `Self` originates from. This will usually be an
    /// [`ImplItem`](crate::ast::item::ImplItem) or
    /// [`TraitItem`](crate::ast::item::TraitItem).
    SelfTy(ItemId),
    /// The path points to an item, identified by the [`ItemId`]. For example
    /// [`ConstItem`](crate::ast::item::ConstItem),
    /// [`StaticItem`](crate::ast::item::ImplItem),
    /// [`FnItem`](crate::ast::item::FnItem).
    Item(ItemId),
    /// The path target is a variant from an enum, identified by the [`VariantId`]
    Variant(VariantId),
    /// The path target is a local variable, identified by the [`VarId`].
    Var(VarId),
    /// The path target is a generic type, identified by the [`GenericId`].
    Generic(GenericId),
    /// The target can't be resolved in the current context. This can happen
    /// for paths in generic bounds.
    Unresolved,
}

#[repr(C)]
#[derive(Debug)]
pub struct AstPath<'ast> {
    segments: FfiSlice<'ast, AstPathSegment<'ast>>,
    // FIXME(xFrednet): Maybe add an optional target ID for values, lifetimes, etc this path.
}

#[cfg(feature = "driver-api")]
impl<'ast> AstPath<'ast> {
    pub fn new(segments: &'ast [AstPathSegment<'ast>]) -> Self {
        debug_assert!(!segments.is_empty());
        Self {
            segments: segments.into(),
        }
    }
}

impl<'ast> AstPath<'ast> {
    pub fn segments(&self) -> &[AstPathSegment<'ast>] {
        self.segments.get()
    }

    /// This returns the [`GenericArgs`] specified on the last segment of the path.
    /// This is especially useful, for paths pointing to types or functions. For
    /// example, the `u32` of the path `Vec<u32>`, is stored in the [`GenericArgs`]
    /// as a type parameter.
    pub fn generics(&self) -> &GenericArgs<'ast> {
        self.segments
            .get()
            .last()
            .expect("a path always has at least one segment")
            .generics()
    }
}

#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "driver-api", derive(Clone))]
pub struct AstPathSegment<'ast> {
    ident: Ident<'ast>,
    generics: GenericArgs<'ast>,
}

#[cfg(feature = "driver-api")]
impl<'ast> AstPathSegment<'ast> {
    pub fn new(ident: Ident<'ast>, generics: GenericArgs<'ast>) -> Self {
        Self { ident, generics }
    }
}

impl<'ast> AstPathSegment<'ast> {
    pub fn ident(&self) -> &Ident<'ast> {
        &self.ident
    }

    pub fn generics(&self) -> &GenericArgs<'ast> {
        &self.generics
    }
}