use std::fmt;
use serde::{Deserialize, Serialize};
use super::super::string::StringId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TypeOfContext {
Parameter,
Return,
Field,
Variable,
TypeParameter,
Constraint,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum FfiConvention {
#[default]
C,
Cdecl,
Stdcall,
Fastcall,
System,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
#[derive(Default)]
pub enum HttpMethod {
#[default]
Get,
Post,
Put,
Delete,
Patch,
Head,
Options,
All,
}
impl HttpMethod {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Get => "GET",
Self::Post => "POST",
Self::Put => "PUT",
Self::Delete => "DELETE",
Self::Patch => "PATCH",
Self::Head => "HEAD",
Self::Options => "OPTIONS",
Self::All => "ALL",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum DbQueryType {
#[default]
Select,
Insert,
Update,
Delete,
Execute,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TableWriteOp {
Insert,
Update,
Delete,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum ExportKind {
#[default]
Direct,
Reexport,
Default,
Namespace,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum MqProtocol {
#[default]
Kafka,
Sqs,
RabbitMq,
Nats,
Redis,
Other(StringId),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum LifetimeConstraintKind {
#[default]
Outlives,
TypeBound,
Reference,
Static,
HigherRanked,
TraitObject,
ImplTrait,
Elided,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum MacroExpansionKind {
Derive,
Attribute,
#[default]
Declarative,
Function,
CfgGate,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EdgeKind {
Defines,
Contains,
Calls {
argument_count: u8,
is_async: bool,
},
References,
Imports {
alias: Option<StringId>,
is_wildcard: bool,
},
Exports {
kind: ExportKind,
alias: Option<StringId>,
},
TypeOf {
context: Option<TypeOfContext>,
index: Option<u16>,
name: Option<StringId>,
},
Inherits,
Implements,
LifetimeConstraint {
constraint_kind: LifetimeConstraintKind,
},
TraitMethodBinding {
trait_name: StringId,
impl_type: StringId,
is_ambiguous: bool,
},
MacroExpansion {
expansion_kind: MacroExpansionKind,
is_verified: bool,
},
FfiCall {
convention: FfiConvention,
},
HttpRequest {
method: HttpMethod,
url: Option<StringId>,
},
GrpcCall {
service: StringId,
method: StringId,
},
WebAssemblyCall,
DbQuery {
query_type: DbQueryType,
table: Option<StringId>,
},
TableRead {
table_name: StringId,
schema: Option<StringId>,
},
TableWrite {
table_name: StringId,
schema: Option<StringId>,
operation: TableWriteOp,
},
TriggeredBy {
trigger_name: StringId,
schema: Option<StringId>,
},
MessageQueue {
protocol: MqProtocol,
topic: Option<StringId>,
},
WebSocket {
event: Option<StringId>,
},
GraphQLOperation {
operation: StringId,
},
ProcessExec {
command: StringId,
},
FileIpc {
path_pattern: Option<StringId>,
},
ProtocolCall {
protocol: StringId,
metadata: Option<StringId>,
},
}
impl EdgeKind {
#[inline]
#[must_use]
pub const fn is_call(&self) -> bool {
matches!(
self,
Self::Calls { .. }
| Self::FfiCall { .. }
| Self::HttpRequest { .. }
| Self::GrpcCall { .. }
| Self::WebAssemblyCall
)
}
#[inline]
#[must_use]
pub const fn is_structural(&self) -> bool {
matches!(self, Self::Defines | Self::Contains)
}
#[inline]
#[must_use]
pub const fn is_type_relation(&self) -> bool {
matches!(
self,
Self::Inherits | Self::Implements | Self::TypeOf { .. }
)
}
#[inline]
#[must_use]
pub const fn is_cross_boundary(&self) -> bool {
matches!(
self,
Self::FfiCall { .. }
| Self::HttpRequest { .. }
| Self::GrpcCall { .. }
| Self::WebAssemblyCall
| Self::DbQuery { .. }
| Self::TableRead { .. }
| Self::TableWrite { .. }
| Self::TriggeredBy { .. }
| Self::MessageQueue { .. }
| Self::WebSocket { .. }
| Self::GraphQLOperation { .. }
| Self::ProcessExec { .. }
| Self::FileIpc { .. }
| Self::ProtocolCall { .. }
)
}
#[inline]
#[must_use]
pub const fn is_async(&self) -> bool {
matches!(
self,
Self::MessageQueue { .. } | Self::WebSocket { .. } | Self::GraphQLOperation { .. }
)
}
#[inline]
#[must_use]
pub const fn is_rust_specific(&self) -> bool {
matches!(
self,
Self::LifetimeConstraint { .. }
| Self::TraitMethodBinding { .. }
| Self::MacroExpansion { .. }
)
}
#[inline]
#[must_use]
pub const fn is_lifetime_constraint(&self) -> bool {
matches!(self, Self::LifetimeConstraint { .. })
}
#[inline]
#[must_use]
pub const fn is_trait_method_binding(&self) -> bool {
matches!(self, Self::TraitMethodBinding { .. })
}
#[inline]
#[must_use]
pub const fn is_macro_expansion(&self) -> bool {
matches!(self, Self::MacroExpansion { .. })
}
#[must_use]
pub const fn tag(&self) -> &'static str {
match self {
Self::Defines => "defines",
Self::Contains => "contains",
Self::Calls { .. } => "calls",
Self::References => "references",
Self::Imports { .. } => "imports",
Self::Exports { .. } => "exports",
Self::TypeOf { .. } => "type_of",
Self::Inherits => "inherits",
Self::Implements => "implements",
Self::LifetimeConstraint { .. } => "lifetime_constraint",
Self::TraitMethodBinding { .. } => "trait_method_binding",
Self::MacroExpansion { .. } => "macro_expansion",
Self::FfiCall { .. } => "ffi_call",
Self::HttpRequest { .. } => "http_request",
Self::GrpcCall { .. } => "grpc_call",
Self::WebAssemblyCall => "web_assembly_call",
Self::DbQuery { .. } => "db_query",
Self::TableRead { .. } => "table_read",
Self::TableWrite { .. } => "table_write",
Self::TriggeredBy { .. } => "triggered_by",
Self::MessageQueue { .. } => "message_queue",
Self::WebSocket { .. } => "web_socket",
Self::GraphQLOperation { .. } => "graphql_operation",
Self::ProcessExec { .. } => "process_exec",
Self::FileIpc { .. } => "file_ipc",
Self::ProtocolCall { .. } => "protocol_call",
}
}
#[must_use]
pub const fn estimated_size(&self) -> usize {
match self {
Self::Defines
| Self::Contains
| Self::References
| Self::Inherits
| Self::Implements
| Self::WebAssemblyCall => 1,
Self::Calls { .. } | Self::MacroExpansion { .. } => 3,
Self::Imports { .. } | Self::Exports { .. } | Self::DbQuery { .. } => 7,
Self::FfiCall { .. } | Self::LifetimeConstraint { .. } => 2,
Self::TraitMethodBinding { .. }
| Self::TableRead { .. }
| Self::TriggeredBy { .. }
| Self::ProtocolCall { .. } => 10,
Self::HttpRequest { .. } | Self::WebSocket { .. } | Self::FileIpc { .. } => 6,
Self::GrpcCall { .. } => 9,
Self::TableWrite { .. } | Self::MessageQueue { .. } | Self::TypeOf { .. } => 11,
Self::GraphQLOperation { .. } | Self::ProcessExec { .. } => 5,
}
}
}
impl fmt::Display for EdgeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.tag())
}
}
impl Default for EdgeKind {
fn default() -> Self {
Self::Calls {
argument_count: 0,
is_async: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn calls() -> EdgeKind {
EdgeKind::Calls {
argument_count: 0,
is_async: false,
}
}
fn imports() -> EdgeKind {
EdgeKind::Imports {
alias: None,
is_wildcard: false,
}
}
fn exports() -> EdgeKind {
EdgeKind::Exports {
kind: ExportKind::Direct,
alias: None,
}
}
#[test]
fn test_edge_kind_tag() {
assert_eq!(calls().tag(), "calls");
assert_eq!(imports().tag(), "imports");
assert_eq!(exports().tag(), "exports");
assert_eq!(EdgeKind::Defines.tag(), "defines");
assert_eq!(
EdgeKind::HttpRequest {
method: HttpMethod::Get,
url: None
}
.tag(),
"http_request"
);
}
#[test]
fn test_edge_kind_display() {
assert_eq!(format!("{}", calls()), "calls");
assert_eq!(format!("{}", imports()), "imports");
assert_eq!(format!("{}", exports()), "exports");
assert_eq!(format!("{}", EdgeKind::Inherits), "inherits");
}
#[test]
fn test_is_call() {
assert!(calls().is_call());
assert!(
EdgeKind::Calls {
argument_count: 5,
is_async: true
}
.is_call()
);
assert!(
EdgeKind::FfiCall {
convention: FfiConvention::C
}
.is_call()
);
assert!(
EdgeKind::HttpRequest {
method: HttpMethod::Post,
url: None
}
.is_call()
);
assert!(!EdgeKind::Defines.is_call());
assert!(!EdgeKind::Inherits.is_call());
assert!(!imports().is_call());
assert!(!exports().is_call());
}
#[test]
fn test_is_structural() {
assert!(EdgeKind::Defines.is_structural());
assert!(EdgeKind::Contains.is_structural());
assert!(!calls().is_structural());
assert!(!imports().is_structural());
assert!(!exports().is_structural());
}
#[test]
fn test_is_type_relation() {
assert!(EdgeKind::Inherits.is_type_relation());
assert!(EdgeKind::Implements.is_type_relation());
assert!(
EdgeKind::TypeOf {
context: None,
index: None,
name: None,
}
.is_type_relation()
);
assert!(!calls().is_type_relation());
}
#[test]
fn test_is_cross_boundary() {
assert!(
EdgeKind::FfiCall {
convention: FfiConvention::C
}
.is_cross_boundary()
);
assert!(
EdgeKind::HttpRequest {
method: HttpMethod::Get,
url: None
}
.is_cross_boundary()
);
assert!(
EdgeKind::GrpcCall {
service: StringId::INVALID,
method: StringId::INVALID
}
.is_cross_boundary()
);
assert!(!calls().is_cross_boundary());
assert!(!imports().is_cross_boundary());
assert!(!exports().is_cross_boundary());
}
#[test]
fn test_is_async() {
assert!(
EdgeKind::MessageQueue {
protocol: MqProtocol::Kafka,
topic: None
}
.is_async()
);
assert!(EdgeKind::WebSocket { event: None }.is_async());
assert!(!calls().is_async());
assert!(
!EdgeKind::Calls {
argument_count: 0,
is_async: true
}
.is_async()
);
}
#[test]
fn test_default() {
assert_eq!(EdgeKind::default(), calls());
assert_eq!(HttpMethod::default(), HttpMethod::Get);
assert_eq!(FfiConvention::default(), FfiConvention::C);
assert_eq!(DbQueryType::default(), DbQueryType::Select);
assert_eq!(ExportKind::default(), ExportKind::Direct);
}
#[test]
fn test_http_method_as_str() {
assert_eq!(HttpMethod::Get.as_str(), "GET");
assert_eq!(HttpMethod::Post.as_str(), "POST");
assert_eq!(HttpMethod::Delete.as_str(), "DELETE");
assert_eq!(HttpMethod::All.as_str(), "ALL");
}
#[test]
fn test_calls_with_metadata() {
let sync_call = EdgeKind::Calls {
argument_count: 3,
is_async: false,
};
let async_call = EdgeKind::Calls {
argument_count: 0,
is_async: true,
};
assert_eq!(sync_call.tag(), "calls");
assert_eq!(async_call.tag(), "calls");
assert!(sync_call.is_call());
assert!(async_call.is_call());
assert_ne!(sync_call, async_call);
}
#[test]
fn test_imports_with_metadata() {
let simple = imports();
let aliased = EdgeKind::Imports {
alias: Some(StringId::new(42)),
is_wildcard: false,
};
let wildcard = EdgeKind::Imports {
alias: None,
is_wildcard: true,
};
assert_eq!(simple.tag(), "imports");
assert_eq!(aliased.tag(), "imports");
assert_eq!(wildcard.tag(), "imports");
assert_ne!(simple, aliased);
assert_ne!(simple, wildcard);
}
#[test]
fn test_exports_with_metadata() {
let direct = exports();
let reexport = EdgeKind::Exports {
kind: ExportKind::Reexport,
alias: None,
};
let default_export = EdgeKind::Exports {
kind: ExportKind::Default,
alias: None,
};
let namespace = EdgeKind::Exports {
kind: ExportKind::Namespace,
alias: Some(StringId::new(1)),
};
assert_eq!(direct.tag(), "exports");
assert_eq!(reexport.tag(), "exports");
assert_eq!(default_export.tag(), "exports");
assert_eq!(namespace.tag(), "exports");
assert_ne!(direct, reexport);
assert_ne!(direct, default_export);
}
#[test]
fn test_serde_calls_imports_exports() {
let calls = EdgeKind::Calls {
argument_count: 5,
is_async: true,
};
let json = serde_json::to_string(&calls).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(calls, deserialized);
assert!(json.contains("\"calls\""));
assert!(json.contains("\"argument_count\":5"));
assert!(json.contains("\"is_async\":true"));
let imports = EdgeKind::Imports {
alias: Some(StringId::new(10)),
is_wildcard: false,
};
let json = serde_json::to_string(&imports).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(imports, deserialized);
let exports = EdgeKind::Exports {
kind: ExportKind::Reexport,
alias: None,
};
let json = serde_json::to_string(&exports).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(exports, deserialized);
}
#[test]
fn test_serde_complex_variants() {
let http = EdgeKind::HttpRequest {
method: HttpMethod::Post,
url: None,
};
let json = serde_json::to_string(&http).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(http, deserialized);
let grpc = EdgeKind::GrpcCall {
service: StringId::new(1),
method: StringId::new(2),
};
let json = serde_json::to_string(&grpc).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(grpc, deserialized);
}
#[test]
fn test_postcard_roundtrip_simple_enums() {
for conv in [
FfiConvention::C,
FfiConvention::Cdecl,
FfiConvention::Stdcall,
] {
let bytes = postcard::to_allocvec(&conv).unwrap();
let deserialized: FfiConvention = postcard::from_bytes(&bytes).unwrap();
assert_eq!(conv, deserialized);
}
for method in [
HttpMethod::Get,
HttpMethod::Post,
HttpMethod::Delete,
HttpMethod::All,
] {
let bytes = postcard::to_allocvec(&method).unwrap();
let deserialized: HttpMethod = postcard::from_bytes(&bytes).unwrap();
assert_eq!(method, deserialized);
}
for query in [
DbQueryType::Select,
DbQueryType::Insert,
DbQueryType::Update,
] {
let bytes = postcard::to_allocvec(&query).unwrap();
let deserialized: DbQueryType = postcard::from_bytes(&bytes).unwrap();
assert_eq!(query, deserialized);
}
for kind in [
ExportKind::Direct,
ExportKind::Reexport,
ExportKind::Default,
ExportKind::Namespace,
] {
let bytes = postcard::to_allocvec(&kind).unwrap();
let deserialized: ExportKind = postcard::from_bytes(&bytes).unwrap();
assert_eq!(kind, deserialized);
}
}
#[test]
fn test_edge_kind_json_compatibility() {
let kinds = [
calls(),
imports(),
exports(),
EdgeKind::Defines,
EdgeKind::HttpRequest {
method: HttpMethod::Get,
url: None,
},
EdgeKind::MessageQueue {
protocol: MqProtocol::Kafka,
topic: Some(StringId::new(1)),
},
];
for kind in &kinds {
let json = serde_json::to_string(kind).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(*kind, deserialized);
let bytes = postcard::to_allocvec(kind).unwrap();
let from_postcard: EdgeKind = postcard::from_bytes(&bytes).unwrap();
assert_eq!(*kind, from_postcard);
}
}
#[test]
fn test_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(calls());
set.insert(imports());
set.insert(exports());
set.insert(EdgeKind::Defines);
set.insert(EdgeKind::HttpRequest {
method: HttpMethod::Get,
url: None,
});
assert!(set.contains(&calls()));
assert!(set.contains(&imports()));
assert!(set.contains(&exports()));
assert!(!set.contains(&EdgeKind::Inherits));
assert_eq!(set.len(), 5);
}
#[test]
fn test_ffi_convention_variants() {
let conventions = [
FfiConvention::C,
FfiConvention::Cdecl,
FfiConvention::Stdcall,
FfiConvention::Fastcall,
FfiConvention::System,
];
for conv in conventions {
let edge = EdgeKind::FfiCall { convention: conv };
assert!(edge.is_call());
assert!(edge.is_cross_boundary());
}
}
#[test]
fn test_mq_protocol_variants() {
let protocols = [
MqProtocol::Kafka,
MqProtocol::Sqs,
MqProtocol::RabbitMq,
MqProtocol::Nats,
MqProtocol::Redis,
MqProtocol::Other(StringId::new(1)),
];
for proto in protocols {
let edge = EdgeKind::MessageQueue {
protocol: proto.clone(),
topic: None,
};
assert!(edge.is_async());
assert!(edge.is_cross_boundary());
}
}
#[test]
fn test_export_kind_variants() {
let kinds = [
ExportKind::Direct,
ExportKind::Reexport,
ExportKind::Default,
ExportKind::Namespace,
];
for kind in kinds {
let edge = EdgeKind::Exports { kind, alias: None };
assert_eq!(edge.tag(), "exports");
assert!(!edge.is_call());
assert!(!edge.is_structural());
assert!(!edge.is_cross_boundary());
}
}
#[test]
fn test_estimated_size() {
assert_eq!(EdgeKind::Defines.estimated_size(), 1);
assert_eq!(EdgeKind::Contains.estimated_size(), 1);
assert_eq!(EdgeKind::References.estimated_size(), 1);
assert_eq!(calls().estimated_size(), 3);
assert_eq!(imports().estimated_size(), 7);
assert_eq!(exports().estimated_size(), 7);
assert_eq!(
EdgeKind::LifetimeConstraint {
constraint_kind: LifetimeConstraintKind::Outlives
}
.estimated_size(),
2
);
assert_eq!(
EdgeKind::MacroExpansion {
expansion_kind: MacroExpansionKind::Derive,
is_verified: true
}
.estimated_size(),
3
);
assert_eq!(
EdgeKind::TraitMethodBinding {
trait_name: StringId::INVALID,
impl_type: StringId::INVALID,
is_ambiguous: false
}
.estimated_size(),
10
);
}
#[test]
fn test_lifetime_constraint_kind_variants() {
let kinds = [
LifetimeConstraintKind::Outlives,
LifetimeConstraintKind::TypeBound,
LifetimeConstraintKind::Reference,
LifetimeConstraintKind::Static,
LifetimeConstraintKind::HigherRanked,
LifetimeConstraintKind::TraitObject,
LifetimeConstraintKind::ImplTrait,
LifetimeConstraintKind::Elided,
];
for constraint_kind in kinds {
let edge = EdgeKind::LifetimeConstraint { constraint_kind };
assert!(edge.is_rust_specific());
assert!(edge.is_lifetime_constraint());
assert!(!edge.is_call());
assert!(!edge.is_structural());
assert_eq!(edge.tag(), "lifetime_constraint");
}
}
#[test]
fn test_macro_expansion_kind_variants() {
let kinds = [
MacroExpansionKind::Derive,
MacroExpansionKind::Attribute,
MacroExpansionKind::Declarative,
MacroExpansionKind::Function,
MacroExpansionKind::CfgGate,
];
for expansion_kind in kinds {
let edge = EdgeKind::MacroExpansion {
expansion_kind,
is_verified: true,
};
assert!(edge.is_rust_specific());
assert!(edge.is_macro_expansion());
assert!(!edge.is_call());
assert_eq!(edge.tag(), "macro_expansion");
}
}
#[test]
fn test_trait_method_binding() {
let edge = EdgeKind::TraitMethodBinding {
trait_name: StringId::new(1),
impl_type: StringId::new(2),
is_ambiguous: false,
};
assert!(edge.is_rust_specific());
assert!(edge.is_trait_method_binding());
assert!(!edge.is_call());
assert_eq!(edge.tag(), "trait_method_binding");
let ambiguous = EdgeKind::TraitMethodBinding {
trait_name: StringId::new(1),
impl_type: StringId::new(2),
is_ambiguous: true,
};
assert!(ambiguous.is_trait_method_binding());
}
#[test]
fn test_rust_specific_edges_serde() {
let lifetime = EdgeKind::LifetimeConstraint {
constraint_kind: LifetimeConstraintKind::HigherRanked,
};
let json = serde_json::to_string(&lifetime).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(lifetime, deserialized);
let binding = EdgeKind::TraitMethodBinding {
trait_name: StringId::new(10),
impl_type: StringId::new(20),
is_ambiguous: true,
};
let json = serde_json::to_string(&binding).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(binding, deserialized);
let expansion = EdgeKind::MacroExpansion {
expansion_kind: MacroExpansionKind::Derive,
is_verified: false,
};
let json = serde_json::to_string(&expansion).unwrap();
let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
assert_eq!(expansion, deserialized);
}
#[test]
fn test_rust_specific_edges_postcard() {
let edges = [
EdgeKind::LifetimeConstraint {
constraint_kind: LifetimeConstraintKind::Outlives,
},
EdgeKind::TraitMethodBinding {
trait_name: StringId::new(5),
impl_type: StringId::new(6),
is_ambiguous: false,
},
EdgeKind::MacroExpansion {
expansion_kind: MacroExpansionKind::Attribute,
is_verified: true,
},
];
for edge in edges {
let bytes = postcard::to_allocvec(&edge).unwrap();
let deserialized: EdgeKind = postcard::from_bytes(&bytes).unwrap();
assert_eq!(edge, deserialized);
}
}
#[test]
fn test_lifetime_constraint_kind_defaults() {
assert_eq!(
LifetimeConstraintKind::default(),
LifetimeConstraintKind::Outlives
);
assert_eq!(
MacroExpansionKind::default(),
MacroExpansionKind::Declarative
);
}
}