2023-11-26 18:55:01 +00:00
|
|
|
use std::borrow::Cow;
|
|
|
|
|
2023-04-30 01:20:53 +00:00
|
|
|
use rustc_errors::{
|
2024-03-06 00:02:56 +00:00
|
|
|
codes::*, Applicability, DecorateLint, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic,
|
|
|
|
EmissionGuarantee, Level,
|
2023-04-30 01:20:53 +00:00
|
|
|
};
|
|
|
|
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
|
|
|
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
|
2023-05-16 10:47:50 +00:00
|
|
|
use rustc_middle::ty::TyCtxt;
|
2023-04-30 01:20:53 +00:00
|
|
|
use rustc_session::lint::{self, Lint};
|
2023-05-16 10:47:50 +00:00
|
|
|
use rustc_span::def_id::DefId;
|
2023-04-30 01:20:53 +00:00
|
|
|
use rustc_span::Span;
|
|
|
|
|
2023-11-26 18:55:01 +00:00
|
|
|
use crate::fluent_generated as fluent;
|
|
|
|
|
2023-04-30 01:20:53 +00:00
|
|
|
#[derive(LintDiagnostic)]
|
|
|
|
pub(crate) enum ConstMutate {
|
|
|
|
#[diag(mir_transform_const_modify)]
|
|
|
|
#[note]
|
|
|
|
Modify {
|
|
|
|
#[note(mir_transform_const_defined_here)]
|
|
|
|
konst: Span,
|
|
|
|
},
|
|
|
|
#[diag(mir_transform_const_mut_borrow)]
|
|
|
|
#[note]
|
|
|
|
#[note(mir_transform_note2)]
|
|
|
|
MutBorrow {
|
|
|
|
#[note(mir_transform_note3)]
|
|
|
|
method_call: Option<Span>,
|
|
|
|
#[note(mir_transform_const_defined_here)]
|
|
|
|
konst: Span,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Diagnostic)]
|
Stop using `String` for error codes.
Error codes are integers, but `String` is used everywhere to represent
them. Gross!
This commit introduces `ErrCode`, an integral newtype for error codes,
replacing `String`. It also introduces a constant for every error code,
e.g. `E0123`, and removes the `error_code!` macro. The constants are
imported wherever used with `use rustc_errors::codes::*`.
With the old code, we have three different ways to specify an error code
at a use point:
```
error_code!(E0123) // macro call
struct_span_code_err!(dcx, span, E0123, "msg"); // bare ident arg to macro call
\#[diag(name, code = "E0123")] // string
struct Diag;
```
With the new code, they all use the `E0123` constant.
```
E0123 // constant
struct_span_code_err!(dcx, span, E0123, "msg"); // constant
\#[diag(name, code = E0123)] // constant
struct Diag;
```
The commit also changes the structure of the error code definitions:
- `rustc_error_codes` now just defines a higher-order macro listing the
used error codes and nothing else.
- Because that's now the only thing in the `rustc_error_codes` crate, I
moved it into the `lib.rs` file and removed the `error_codes.rs` file.
- `rustc_errors` uses that macro to define everything, e.g. the error
code constants and the `DIAGNOSTIC_TABLES`. This is in its new
`codes.rs` file.
2024-01-13 23:57:07 +00:00
|
|
|
#[diag(mir_transform_unaligned_packed_ref, code = E0793)]
|
2023-04-30 01:20:53 +00:00
|
|
|
#[note]
|
|
|
|
#[note(mir_transform_note_ub)]
|
|
|
|
#[help]
|
|
|
|
pub(crate) struct UnalignedPackedRef {
|
|
|
|
#[primary_span]
|
|
|
|
pub span: Span,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(LintDiagnostic)]
|
|
|
|
#[diag(mir_transform_unused_unsafe)]
|
|
|
|
pub(crate) struct UnusedUnsafe {
|
|
|
|
#[label(mir_transform_unused_unsafe)]
|
|
|
|
pub span: Span,
|
|
|
|
#[label]
|
|
|
|
pub nested_parent: Option<Span>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) struct RequiresUnsafe {
|
|
|
|
pub span: Span,
|
|
|
|
pub details: RequiresUnsafeDetail,
|
|
|
|
pub enclosing: Option<Span>,
|
|
|
|
pub op_in_unsafe_fn_allowed: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
// The primary message for this diagnostic should be '{$label} is unsafe and...',
|
|
|
|
// so we need to eagerly translate the label here, which isn't supported by the derive API
|
|
|
|
// We could also exhaustively list out the primary messages for all unsafe violations,
|
|
|
|
// but this would result in a lot of duplication.
|
2024-03-06 00:02:56 +00:00
|
|
|
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for RequiresUnsafe {
|
2023-04-30 01:20:53 +00:00
|
|
|
#[track_caller]
|
2024-03-06 00:02:56 +00:00
|
|
|
fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
|
2024-02-22 23:20:45 +00:00
|
|
|
let mut diag = Diag::new(dcx, level, fluent::mir_transform_requires_unsafe);
|
Stop using `String` for error codes.
Error codes are integers, but `String` is used everywhere to represent
them. Gross!
This commit introduces `ErrCode`, an integral newtype for error codes,
replacing `String`. It also introduces a constant for every error code,
e.g. `E0123`, and removes the `error_code!` macro. The constants are
imported wherever used with `use rustc_errors::codes::*`.
With the old code, we have three different ways to specify an error code
at a use point:
```
error_code!(E0123) // macro call
struct_span_code_err!(dcx, span, E0123, "msg"); // bare ident arg to macro call
\#[diag(name, code = "E0123")] // string
struct Diag;
```
With the new code, they all use the `E0123` constant.
```
E0123 // constant
struct_span_code_err!(dcx, span, E0123, "msg"); // constant
\#[diag(name, code = E0123)] // constant
struct Diag;
```
The commit also changes the structure of the error code definitions:
- `rustc_error_codes` now just defines a higher-order macro listing the
used error codes and nothing else.
- Because that's now the only thing in the `rustc_error_codes` crate, I
moved it into the `lib.rs` file and removed the `error_codes.rs` file.
- `rustc_errors` uses that macro to define everything, e.g. the error
code constants and the `DIAGNOSTIC_TABLES`. This is in its new
`codes.rs` file.
2024-01-13 23:57:07 +00:00
|
|
|
diag.code(E0133);
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.span(self.span);
|
2023-04-30 01:20:53 +00:00
|
|
|
diag.span_label(self.span, self.details.label());
|
2023-12-17 23:15:45 +00:00
|
|
|
let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg("details", desc);
|
|
|
|
diag.arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
|
2023-11-26 18:55:01 +00:00
|
|
|
self.details.add_subdiagnostics(&mut diag);
|
2023-04-30 01:20:53 +00:00
|
|
|
if let Some(sp) = self.enclosing {
|
2023-11-26 18:55:01 +00:00
|
|
|
diag.span_label(sp, fluent::mir_transform_not_inherited);
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
diag
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-26 18:55:01 +00:00
|
|
|
#[derive(Clone)]
|
2023-04-30 01:20:53 +00:00
|
|
|
pub(crate) struct RequiresUnsafeDetail {
|
|
|
|
pub span: Span,
|
|
|
|
pub violation: UnsafetyViolationDetails,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RequiresUnsafeDetail {
|
Reduce capabilities of `Diagnostic`.
Currently many diagnostic modifier methods are available on both
`Diagnostic` and `DiagnosticBuilder`. This commit removes most of them
from `Diagnostic`. To minimize the diff size, it keeps them within
`diagnostic.rs` but changes the surrounding `impl Diagnostic` block to
`impl DiagnosticBuilder`. (I intend to move things around later, to give
a more sensible code layout.)
`Diagnostic` keeps a few methods that it still needs, like `sub`,
`arg`, and `replace_args`.
The `forward!` macro, which defined two additional methods per call
(e.g. `note` and `with_note`), is replaced by the `with_fn!` macro,
which defines one additional method per call (e.g. `with_note`). It's
now also only used when necessary -- not all modifier methods currently
need a `with_*` form. (New ones can be easily added as necessary.)
All this also requires changing `trait AddToDiagnostic` so its methods
take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many
mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`.
There are three subdiagnostics -- `DelayedAtWithoutNewline`,
`DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` --
that are created within the diagnostics machinery and appended to
external diagnostics. These are handled at the `Diagnostic` level, which
means it's now hard to construct them via `derive(Diagnostic)`, so
instead we construct them by hand. This has no effect on what they look
like when printed.
There are lots of new `allow` markers for `untranslatable_diagnostics`
and `diagnostics_outside_of_impl`. This is because
`#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic`
modifier methods, but missing from the `DiagnosticBuilder` modifier
methods. They're now present.
2024-02-06 05:44:30 +00:00
|
|
|
// FIXME: make this translatable
|
|
|
|
#[allow(rustc::diagnostic_outside_of_impl)]
|
|
|
|
#[allow(rustc::untranslatable_diagnostic)]
|
2024-02-22 23:20:45 +00:00
|
|
|
fn add_subdiagnostics<G: EmissionGuarantee>(&self, diag: &mut Diag<'_, G>) {
|
2023-04-30 01:20:53 +00:00
|
|
|
use UnsafetyViolationDetails::*;
|
|
|
|
match self.violation {
|
2023-11-26 18:55:01 +00:00
|
|
|
CallToUnsafeFunction => {
|
|
|
|
diag.note(fluent::mir_transform_call_to_unsafe_note);
|
|
|
|
}
|
|
|
|
UseOfInlineAssembly => {
|
|
|
|
diag.note(fluent::mir_transform_use_of_asm_note);
|
|
|
|
}
|
2023-04-30 01:20:53 +00:00
|
|
|
InitializingTypeWith => {
|
2023-11-26 18:55:01 +00:00
|
|
|
diag.note(fluent::mir_transform_initializing_valid_range_note);
|
|
|
|
}
|
|
|
|
CastOfPointerToInt => {
|
|
|
|
diag.note(fluent::mir_transform_const_ptr2int_note);
|
|
|
|
}
|
|
|
|
UseOfMutableStatic => {
|
|
|
|
diag.note(fluent::mir_transform_use_of_static_mut_note);
|
|
|
|
}
|
|
|
|
UseOfExternStatic => {
|
|
|
|
diag.note(fluent::mir_transform_use_of_extern_static_note);
|
|
|
|
}
|
|
|
|
DerefOfRawPointer => {
|
|
|
|
diag.note(fluent::mir_transform_deref_ptr_note);
|
|
|
|
}
|
|
|
|
AccessToUnionField => {
|
|
|
|
diag.note(fluent::mir_transform_union_access_note);
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
MutationOfLayoutConstrainedField => {
|
2023-11-26 18:55:01 +00:00
|
|
|
diag.note(fluent::mir_transform_mutation_layout_constrained_note);
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
BorrowOfLayoutConstrainedField => {
|
2023-11-26 18:55:01 +00:00
|
|
|
diag.note(fluent::mir_transform_mutation_layout_constrained_borrow_note);
|
|
|
|
}
|
|
|
|
CallToFunctionWith { ref missing, ref build_enabled } => {
|
|
|
|
diag.help(fluent::mir_transform_target_feature_call_help);
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg(
|
2023-11-26 18:55:01 +00:00
|
|
|
"missing_target_features",
|
2024-02-23 03:37:48 +00:00
|
|
|
DiagArgValue::StrListSepByAnd(
|
2024-01-30 04:27:16 +00:00
|
|
|
missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
|
2023-11-26 18:55:01 +00:00
|
|
|
),
|
|
|
|
);
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg("missing_target_features_count", missing.len());
|
2023-11-26 18:55:01 +00:00
|
|
|
if !build_enabled.is_empty() {
|
|
|
|
diag.note(fluent::mir_transform_target_feature_call_note);
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg(
|
2023-11-26 18:55:01 +00:00
|
|
|
"build_target_features",
|
2024-02-23 03:37:48 +00:00
|
|
|
DiagArgValue::StrListSepByAnd(
|
2023-11-26 18:55:01 +00:00
|
|
|
build_enabled
|
|
|
|
.iter()
|
2024-01-30 04:27:16 +00:00
|
|
|
.map(|feature| Cow::from(feature.to_string()))
|
2023-11-26 18:55:01 +00:00
|
|
|
.collect(),
|
|
|
|
),
|
|
|
|
);
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg("build_target_features_count", build_enabled.len());
|
2023-11-26 18:55:01 +00:00
|
|
|
}
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-29 00:58:51 +00:00
|
|
|
fn label(&self) -> DiagMessage {
|
2023-04-30 01:20:53 +00:00
|
|
|
use UnsafetyViolationDetails::*;
|
|
|
|
match self.violation {
|
2023-11-26 18:55:01 +00:00
|
|
|
CallToUnsafeFunction => fluent::mir_transform_call_to_unsafe_label,
|
|
|
|
UseOfInlineAssembly => fluent::mir_transform_use_of_asm_label,
|
|
|
|
InitializingTypeWith => fluent::mir_transform_initializing_valid_range_label,
|
|
|
|
CastOfPointerToInt => fluent::mir_transform_const_ptr2int_label,
|
|
|
|
UseOfMutableStatic => fluent::mir_transform_use_of_static_mut_label,
|
|
|
|
UseOfExternStatic => fluent::mir_transform_use_of_extern_static_label,
|
|
|
|
DerefOfRawPointer => fluent::mir_transform_deref_ptr_label,
|
|
|
|
AccessToUnionField => fluent::mir_transform_union_access_label,
|
2023-04-30 01:20:53 +00:00
|
|
|
MutationOfLayoutConstrainedField => {
|
2023-11-26 18:55:01 +00:00
|
|
|
fluent::mir_transform_mutation_layout_constrained_label
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
BorrowOfLayoutConstrainedField => {
|
2023-11-26 18:55:01 +00:00
|
|
|
fluent::mir_transform_mutation_layout_constrained_borrow_label
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
2023-11-26 18:55:01 +00:00
|
|
|
CallToFunctionWith { .. } => fluent::mir_transform_target_feature_call_label,
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) struct UnsafeOpInUnsafeFn {
|
|
|
|
pub details: RequiresUnsafeDetail,
|
2023-05-27 20:20:14 +00:00
|
|
|
|
|
|
|
/// These spans point to:
|
|
|
|
/// 1. the start of the function body
|
|
|
|
/// 2. the end of the function body
|
|
|
|
/// 3. the function signature
|
|
|
|
pub suggest_unsafe_block: Option<(Span, Span, Span)>,
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
|
|
|
|
#[track_caller]
|
2024-02-22 23:20:45 +00:00
|
|
|
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
|
2024-01-07 20:47:02 +00:00
|
|
|
let desc = diag.dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg("details", desc);
|
2023-04-30 01:20:53 +00:00
|
|
|
diag.span_label(self.details.span, self.details.label());
|
2023-11-26 18:55:01 +00:00
|
|
|
self.details.add_subdiagnostics(diag);
|
2022-07-27 22:03:16 +00:00
|
|
|
|
2023-05-27 20:20:14 +00:00
|
|
|
if let Some((start, end, fn_sig)) = self.suggest_unsafe_block {
|
2023-11-26 18:55:01 +00:00
|
|
|
diag.span_note(fn_sig, fluent::mir_transform_note);
|
2023-05-27 20:19:09 +00:00
|
|
|
diag.tool_only_multipart_suggestion(
|
2023-11-26 18:55:01 +00:00
|
|
|
fluent::mir_transform_suggestion,
|
2022-07-27 22:03:16 +00:00
|
|
|
vec![(start, " unsafe {".into()), (end, "}".into())],
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 00:58:51 +00:00
|
|
|
fn msg(&self) -> DiagMessage {
|
2023-11-26 18:55:01 +00:00
|
|
|
fluent::mir_transform_unsafe_op_in_unsafe_fn
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-15 13:18:50 +00:00
|
|
|
pub(crate) struct AssertLint<P> {
|
|
|
|
pub span: Span,
|
|
|
|
pub assert_kind: AssertKind<P>,
|
|
|
|
pub lint_kind: AssertLintKind,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) enum AssertLintKind {
|
|
|
|
ArithmeticOverflow,
|
|
|
|
UnconditionalPanic,
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
|
2024-02-22 23:20:45 +00:00
|
|
|
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
|
2024-01-15 13:18:50 +00:00
|
|
|
let message = self.assert_kind.diagnostic_message();
|
|
|
|
self.assert_kind.add_args(&mut |name, value| {
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg(name, value);
|
2023-05-17 10:30:14 +00:00
|
|
|
});
|
2024-01-15 13:18:50 +00:00
|
|
|
diag.span_label(self.span, message);
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 00:58:51 +00:00
|
|
|
fn msg(&self) -> DiagMessage {
|
2024-01-15 13:18:50 +00:00
|
|
|
match self.lint_kind {
|
|
|
|
AssertLintKind::ArithmeticOverflow => fluent::mir_transform_arithmetic_overflow,
|
|
|
|
AssertLintKind::UnconditionalPanic => fluent::mir_transform_operation_will_panic,
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-15 13:18:50 +00:00
|
|
|
impl AssertLintKind {
|
2023-04-30 01:20:53 +00:00
|
|
|
pub fn lint(&self) -> &'static Lint {
|
|
|
|
match self {
|
2024-01-15 13:18:50 +00:00
|
|
|
AssertLintKind::ArithmeticOverflow => lint::builtin::ARITHMETIC_OVERFLOW,
|
|
|
|
AssertLintKind::UnconditionalPanic => lint::builtin::UNCONDITIONAL_PANIC,
|
2023-04-30 01:20:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(LintDiagnostic)]
|
|
|
|
#[diag(mir_transform_ffi_unwind_call)]
|
|
|
|
pub(crate) struct FfiUnwindCall {
|
|
|
|
#[label(mir_transform_ffi_unwind_call)]
|
|
|
|
pub span: Span,
|
|
|
|
pub foreign: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(LintDiagnostic)]
|
|
|
|
#[diag(mir_transform_fn_item_ref)]
|
|
|
|
pub(crate) struct FnItemRef {
|
|
|
|
#[suggestion(code = "{sugg}", applicability = "unspecified")]
|
|
|
|
pub span: Span,
|
|
|
|
pub sugg: String,
|
|
|
|
pub ident: String,
|
|
|
|
}
|
|
|
|
|
2023-05-16 10:47:50 +00:00
|
|
|
pub(crate) struct MustNotSupend<'tcx, 'a> {
|
|
|
|
pub tcx: TyCtxt<'tcx>,
|
2023-04-30 01:20:53 +00:00
|
|
|
pub yield_sp: Span,
|
|
|
|
pub reason: Option<MustNotSuspendReason>,
|
|
|
|
pub src_sp: Span,
|
|
|
|
pub pre: &'a str,
|
2023-05-16 10:47:50 +00:00
|
|
|
pub def_id: DefId,
|
2023-04-30 01:20:53 +00:00
|
|
|
pub post: &'a str,
|
|
|
|
}
|
|
|
|
|
2023-05-16 10:47:50 +00:00
|
|
|
// Needed for def_path_str
|
|
|
|
impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
|
2024-02-22 23:20:45 +00:00
|
|
|
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
|
2023-11-26 18:55:01 +00:00
|
|
|
diag.span_label(self.yield_sp, fluent::_subdiag::label);
|
2023-05-16 10:47:50 +00:00
|
|
|
if let Some(reason) = self.reason {
|
2024-02-14 14:17:27 +00:00
|
|
|
diag.subdiagnostic(diag.dcx, reason);
|
2023-05-16 10:47:50 +00:00
|
|
|
}
|
2023-11-26 18:55:01 +00:00
|
|
|
diag.span_help(self.src_sp, fluent::_subdiag::help);
|
2023-12-23 22:08:41 +00:00
|
|
|
diag.arg("pre", self.pre);
|
|
|
|
diag.arg("def_path", self.tcx.def_path_str(self.def_id));
|
|
|
|
diag.arg("post", self.post);
|
2023-05-16 10:47:50 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 00:58:51 +00:00
|
|
|
fn msg(&self) -> rustc_errors::DiagMessage {
|
2023-11-26 18:55:01 +00:00
|
|
|
fluent::mir_transform_must_not_suspend
|
2023-05-16 10:47:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-30 01:20:53 +00:00
|
|
|
#[derive(Subdiagnostic)]
|
|
|
|
#[note(mir_transform_note)]
|
|
|
|
pub(crate) struct MustNotSuspendReason {
|
|
|
|
#[primary_span]
|
|
|
|
pub span: Span,
|
|
|
|
pub reason: String,
|
|
|
|
}
|