Add new method to Semantics, method_receiver_kind, which returns the kind of self
The options are Shared, Mutable, Consuming, and Copied. Use this to add proper highlighting to methods based on usage.
This commit is contained in:
parent
a044ff0138
commit
3456e2eec7
@ -38,7 +38,7 @@ pub use crate::{
|
||||
Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{original_range, PathResolution, Semantics, SemanticsScope},
|
||||
semantics::{original_range, PathResolution, SelfKind, Semantics, SemanticsScope},
|
||||
};
|
||||
|
||||
pub use hir_def::{
|
||||
|
@ -6,8 +6,10 @@ use std::{cell::RefCell, fmt, iter::successors};
|
||||
|
||||
use base_db::{FileId, FileRange};
|
||||
use hir_def::{
|
||||
lang_item::LangItemTarget,
|
||||
resolver::{self, HasResolver, Resolver, TypeNs},
|
||||
AsMacroCall, FunctionId, TraitId, VariantId,
|
||||
src::HasSource,
|
||||
AsMacroCall, FunctionId, Lookup, TraitId, VariantId,
|
||||
};
|
||||
use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
|
||||
use hir_ty::associated_type_shorthand_candidates;
|
||||
@ -15,7 +17,7 @@ use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use syntax::{
|
||||
algo::{find_node_at_offset, skip_trivia_token},
|
||||
ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||
ast, AstNode, Direction, SmolStr, SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -79,6 +81,13 @@ impl PathResolution {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SelfKind {
|
||||
Shared,
|
||||
Mutable,
|
||||
Consuming,
|
||||
Copied,
|
||||
}
|
||||
|
||||
/// Primary API to get semantic information, like types, from syntax trees.
|
||||
pub struct Semantics<'db, DB> {
|
||||
pub db: &'db DB,
|
||||
@ -188,6 +197,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||
self.imp.type_of_self(param)
|
||||
}
|
||||
|
||||
pub fn method_reciever_kind(&self, call: &ast::MethodCallExpr) -> Option<SelfKind> {
|
||||
self.imp.method_receiver_kind(call)
|
||||
}
|
||||
|
||||
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
||||
self.imp.resolve_method_call(call).map(Function::from)
|
||||
}
|
||||
@ -410,6 +423,35 @@ impl<'db> SemanticsImpl<'db> {
|
||||
self.analyze(param.syntax()).type_of_self(self.db, ¶m)
|
||||
}
|
||||
|
||||
fn method_receiver_kind(&self, call: &ast::MethodCallExpr) -> Option<SelfKind> {
|
||||
self.resolve_method_call(call).and_then(|func| {
|
||||
let lookup = func.lookup(self.db.upcast());
|
||||
let src = lookup.source(self.db.upcast());
|
||||
let param_list = src.value.param_list()?;
|
||||
let self_param = param_list.self_param()?;
|
||||
if self_param.amp_token().is_some() {
|
||||
return Some(if self_param.mut_token().is_some() {
|
||||
SelfKind::Mutable
|
||||
} else {
|
||||
SelfKind::Shared
|
||||
});
|
||||
}
|
||||
|
||||
let ty = self.type_of_expr(&call.expr()?)?;
|
||||
let krate = Function::from(func).krate(self.db)?;
|
||||
let lang_item = self.db.lang_item(krate.id, SmolStr::new("copy"));
|
||||
let copy_trait = match lang_item? {
|
||||
LangItemTarget::TraitId(copy_trait) => Trait::from(copy_trait),
|
||||
_ => return None,
|
||||
};
|
||||
Some(if ty.impls_trait(self.db, copy_trait, &[]) {
|
||||
SelfKind::Copied
|
||||
} else {
|
||||
SelfKind::Consuming
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
|
||||
self.analyze(call.syntax()).resolve_method_call(self.db, call)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ mod injection;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use hir::{Mutability, Name, Semantics, VariantDef};
|
||||
use hir::{Mutability, Name, SelfKind, Semantics, VariantDef};
|
||||
use ide_db::{
|
||||
defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
|
||||
RootDatabase,
|
||||
@ -519,27 +519,29 @@ fn highlight_element(
|
||||
}
|
||||
NAME_REF => {
|
||||
let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
|
||||
let possibly_unsafe = is_possibly_unsafe(&name_ref);
|
||||
match classify_name_ref(sema, &name_ref) {
|
||||
Some(name_kind) => match name_kind {
|
||||
NameRefClass::ExternCrate(_) => HighlightTag::Module.into(),
|
||||
NameRefClass::Definition(def) => {
|
||||
if let Definition::Local(local) = &def {
|
||||
if let Some(name) = local.name(db) {
|
||||
let shadow_count =
|
||||
bindings_shadow_count.entry(name.clone()).or_default();
|
||||
binding_hash = Some(calc_binding_hash(&name, *shadow_count))
|
||||
}
|
||||
};
|
||||
highlight_name(sema, db, def, Some(name_ref), possibly_unsafe)
|
||||
highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
|
||||
let possibly_unsafe = is_possibly_unsafe(&name_ref);
|
||||
match classify_name_ref(sema, &name_ref) {
|
||||
Some(name_kind) => match name_kind {
|
||||
NameRefClass::ExternCrate(_) => HighlightTag::Module.into(),
|
||||
NameRefClass::Definition(def) => {
|
||||
if let Definition::Local(local) = &def {
|
||||
if let Some(name) = local.name(db) {
|
||||
let shadow_count =
|
||||
bindings_shadow_count.entry(name.clone()).or_default();
|
||||
binding_hash = Some(calc_binding_hash(&name, *shadow_count))
|
||||
}
|
||||
};
|
||||
highlight_name(sema, db, def, Some(name_ref), possibly_unsafe)
|
||||
}
|
||||
NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
|
||||
},
|
||||
None if syntactic_name_ref_highlighting => {
|
||||
highlight_name_ref_by_syntax(name_ref, sema)
|
||||
}
|
||||
NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
|
||||
},
|
||||
None if syntactic_name_ref_highlighting => {
|
||||
highlight_name_ref_by_syntax(name_ref, sema)
|
||||
None => HighlightTag::UnresolvedReference.into(),
|
||||
}
|
||||
None => HighlightTag::UnresolvedReference.into(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Simple token-based highlighting
|
||||
@ -700,6 +702,35 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight_func_by_name_ref(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
name_ref: &ast::NameRef,
|
||||
) -> Option<Highlight> {
|
||||
let parent = name_ref.syntax().parent()?;
|
||||
let method_call = ast::MethodCallExpr::cast(parent)?;
|
||||
highlight_method_call(sema, &method_call)
|
||||
}
|
||||
|
||||
fn highlight_method_call(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
method_call: &ast::MethodCallExpr,
|
||||
) -> Option<Highlight> {
|
||||
let func = sema.resolve_method_call(&method_call)?;
|
||||
let mut h = HighlightTag::Function.into();
|
||||
if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
|
||||
h |= HighlightModifier::Unsafe;
|
||||
}
|
||||
|
||||
sema.method_reciever_kind(&method_call)
|
||||
.map(|self_kind| match self_kind {
|
||||
SelfKind::Shared => h,
|
||||
SelfKind::Mutable => h | HighlightModifier::Mutable,
|
||||
SelfKind::Consuming => h | HighlightModifier::Consuming,
|
||||
SelfKind::Copied => h,
|
||||
})
|
||||
.or_else(|| Some(h))
|
||||
}
|
||||
|
||||
fn highlight_name(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
db: &RootDatabase,
|
||||
@ -722,30 +753,26 @@ fn highlight_name(
|
||||
Definition::ModuleDef(def) => match def {
|
||||
hir::ModuleDef::Module(_) => HighlightTag::Module,
|
||||
hir::ModuleDef::Function(func) => {
|
||||
let mut h = HighlightTag::Function.into();
|
||||
if func.is_unsafe(db) {
|
||||
h |= HighlightModifier::Unsafe;
|
||||
} else {
|
||||
let is_unsafe = name_ref
|
||||
.and_then(|name_ref| name_ref.syntax().parent())
|
||||
.and_then(ast::MethodCallExpr::cast)
|
||||
.map(|method_call_expr| sema.is_unsafe_method_call(&method_call_expr))
|
||||
.unwrap_or(false);
|
||||
if is_unsafe {
|
||||
h |= HighlightModifier::Unsafe;
|
||||
}
|
||||
}
|
||||
return if func.has_self_param(db) {
|
||||
match func.mutability_of_self_param(db) {
|
||||
Some(mutability) => match mutability {
|
||||
Mutability::Mut => h | HighlightModifier::Mutable,
|
||||
Mutability::Shared => h,
|
||||
},
|
||||
None => h | HighlightModifier::Consuming,
|
||||
}
|
||||
} else {
|
||||
h
|
||||
};
|
||||
return name_ref
|
||||
.and_then(|name_ref| highlight_func_by_name_ref(sema, &name_ref))
|
||||
.unwrap_or_else(|| {
|
||||
let mut h = HighlightTag::Function.into();
|
||||
if func.is_unsafe(db) {
|
||||
h |= HighlightModifier::Unsafe;
|
||||
}
|
||||
|
||||
return if func.has_self_param(db) {
|
||||
match func.mutability_of_self_param(db) {
|
||||
Some(mutability) => match mutability {
|
||||
Mutability::Mut => h | HighlightModifier::Mutable,
|
||||
Mutability::Shared => h,
|
||||
},
|
||||
None => h,
|
||||
}
|
||||
} else {
|
||||
h
|
||||
};
|
||||
});
|
||||
}
|
||||
hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
|
||||
hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
|
||||
@ -817,27 +844,9 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
|
||||
|
||||
match parent.kind() {
|
||||
METHOD_CALL_EXPR => {
|
||||
let mut h = Highlight::new(HighlightTag::Function);
|
||||
ast::MethodCallExpr::cast(parent)
|
||||
.and_then(|method_call_expr| {
|
||||
if sema.is_unsafe_method_call(&method_call_expr) {
|
||||
h |= HighlightModifier::Unsafe;
|
||||
}
|
||||
|
||||
let func = sema.resolve_method_call(&method_call_expr)?;
|
||||
if !func.has_self_param(sema.db) {
|
||||
return Some(h);
|
||||
}
|
||||
|
||||
Some(match func.mutability_of_self_param(sema.db) {
|
||||
Some(mutability) => match mutability {
|
||||
Mutability::Mut => h | HighlightModifier::Mutable,
|
||||
Mutability::Shared => h,
|
||||
},
|
||||
None => h | HighlightModifier::Consuming,
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| h)
|
||||
return ast::MethodCallExpr::cast(parent)
|
||||
.and_then(|method_call| highlight_method_call(sema, &method_call))
|
||||
.unwrap_or_else(|| HighlightTag::Function.into());
|
||||
}
|
||||
FIELD_EXPR => {
|
||||
let h = HighlightTag::Field;
|
||||
|
@ -12,6 +12,12 @@ fn test_highlighting() {
|
||||
use inner::{self as inner_mod};
|
||||
mod inner {}
|
||||
|
||||
// Needed for function consuming vs normal
|
||||
pub mod marker {
|
||||
#[lang = "copy"]
|
||||
pub trait Copy {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Foo {
|
||||
pub x: i32,
|
||||
@ -42,6 +48,25 @@ impl Foo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct FooCopy {
|
||||
x: u32,
|
||||
}
|
||||
|
||||
impl FooCopy {
|
||||
fn baz(self) -> u32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
fn qux(&mut self) {
|
||||
self.x = 0;
|
||||
}
|
||||
|
||||
fn quop(&self) -> u32 {
|
||||
self.x
|
||||
}
|
||||
}
|
||||
|
||||
static mut STATIC_MUT: i32 = 0;
|
||||
|
||||
fn foo<'a, T>() -> T {
|
||||
@ -96,6 +121,11 @@ fn main() {
|
||||
foo.quop();
|
||||
foo.qux();
|
||||
foo.baz();
|
||||
|
||||
let mut copy = FooCopy { x };
|
||||
copy.quop();
|
||||
copy.qux();
|
||||
copy.baz();
|
||||
}
|
||||
|
||||
enum Option<T> {
|
||||
|
@ -38,6 +38,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||
<pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span>
|
||||
<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</span>
|
||||
|
||||
<span class="comment">// Needed for function consuming vs normal</span>
|
||||
<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span>
|
||||
<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"copy"</span><span class="attribute">]</span>
|
||||
<span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Clone</span><span class="punctuation">,</span><span class="attribute"> Debug</span><span class="punctuation">)</span><span class="attribute">]</span>
|
||||
<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span>
|
||||
<span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span>
|
||||
@ -55,7 +61,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
|
||||
<span class="keyword">fn</span> <span class="function declaration consuming">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
|
||||
<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
|
||||
<span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
@ -68,6 +74,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||
<span class="punctuation">}</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">,</span><span class="attribute"> Clone</span><span class="punctuation">)</span><span class="attribute">]</span>
|
||||
<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span>
|
||||
<span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span>
|
||||
<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
|
||||
<span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="keyword">fn</span> <span class="function declaration mutable">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
|
||||
<span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
|
||||
<span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
|
||||
<span class="punctuation">}</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
|
||||
|
||||
<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation"><</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">></span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="type_param">T</span> <span class="punctuation">{</span>
|
||||
@ -122,6 +147,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||
<span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
|
||||
<span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
|
||||
<span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
|
||||
|
||||
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
|
||||
<span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
|
||||
<span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
|
||||
<span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
|
||||
<span class="punctuation">}</span>
|
||||
|
||||
<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="punctuation">{</span>
|
||||
@ -131,7 +161,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||
<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="punctuation">;</span>
|
||||
|
||||
<span class="keyword">impl</span><span class="punctuation"><</span><span class="type_param declaration">T</span><span class="punctuation">></span> <span class="enum">Option</span><span class="punctuation"><</span><span class="type_param">T</span><span class="punctuation">></span> <span class="punctuation">{</span>
|
||||
<span class="keyword">fn</span> <span class="function declaration consuming">and</span><span class="punctuation"><</span><span class="type_param declaration">U</span><span class="punctuation">></span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">other</span><span class="punctuation">:</span> <span class="enum">Option</span><span class="punctuation"><</span><span class="type_param">U</span><span class="punctuation">></span><span class="punctuation">)</span> <span class="operator">-></span> <span class="enum">Option</span><span class="punctuation"><</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">,</span> <span class="type_param">U</span><span class="punctuation">)</span><span class="punctuation">></span> <span class="punctuation">{</span>
|
||||
<span class="keyword">fn</span> <span class="function declaration">and</span><span class="punctuation"><</span><span class="type_param declaration">U</span><span class="punctuation">></span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">other</span><span class="punctuation">:</span> <span class="enum">Option</span><span class="punctuation"><</span><span class="type_param">U</span><span class="punctuation">></span><span class="punctuation">)</span> <span class="operator">-></span> <span class="enum">Option</span><span class="punctuation"><</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">,</span> <span class="type_param">U</span><span class="punctuation">)</span><span class="punctuation">></span> <span class="punctuation">{</span>
|
||||
<span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span>
|
||||
<span class="enum_variant">None</span> <span class="operator">=></span> <span class="macro">unimplemented!</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
|
||||
<span class="variable declaration">Nope</span> <span class="operator">=></span> <span class="variable">Nope</span><span class="punctuation">,</span>
|
||||
|
Loading…
x
Reference in New Issue
Block a user