Renamed TypeErrorAdditionalDiags (was Error0308Subdiags) and ObligationCauseFailureCode (was FailureCodeDiagnostics)
This commit is contained in:
parent
aa33a6fca2
commit
fd18d9a584
3 changed files with 52 additions and 48 deletions
|
@ -1361,7 +1361,7 @@ impl AddToDiagnostic for SuggestTuplePatternMany {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
pub enum Error0308Subdiags {
|
pub enum TypeErrorAdditionalDiags {
|
||||||
#[suggestion(
|
#[suggestion(
|
||||||
infer_meant_byte_literal,
|
infer_meant_byte_literal,
|
||||||
code = "b'{code}'",
|
code = "b'{code}'",
|
||||||
|
@ -1429,48 +1429,48 @@ pub enum Error0308Subdiags {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
pub enum FailureCodeDiagnostics {
|
pub enum ObligationCauseFailureCode {
|
||||||
#[diag(infer_oc_method_compat, code = "E0308")]
|
#[diag(infer_oc_method_compat, code = "E0308")]
|
||||||
MethodCompat {
|
MethodCompat {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_type_compat, code = "E0308")]
|
#[diag(infer_oc_type_compat, code = "E0308")]
|
||||||
TypeCompat {
|
TypeCompat {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_const_compat, code = "E0308")]
|
#[diag(infer_oc_const_compat, code = "E0308")]
|
||||||
ConstCompat {
|
ConstCompat {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_try_compat, code = "E0308")]
|
#[diag(infer_oc_try_compat, code = "E0308")]
|
||||||
TryCompat {
|
TryCompat {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_match_compat, code = "E0308")]
|
#[diag(infer_oc_match_compat, code = "E0308")]
|
||||||
MatchCompat {
|
MatchCompat {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_if_else_different, code = "E0308")]
|
#[diag(infer_oc_if_else_different, code = "E0308")]
|
||||||
IfElseDifferent {
|
IfElseDifferent {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_no_else, code = "E0317")]
|
#[diag(infer_oc_no_else, code = "E0317")]
|
||||||
NoElse {
|
NoElse {
|
||||||
|
@ -1482,7 +1482,7 @@ pub enum FailureCodeDiagnostics {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_fn_main_correct_type, code = "E0580")]
|
#[diag(infer_oc_fn_main_correct_type, code = "E0580")]
|
||||||
FnMainCorrectType {
|
FnMainCorrectType {
|
||||||
|
@ -1494,21 +1494,21 @@ pub enum FailureCodeDiagnostics {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_intristic_correct_type, code = "E0308")]
|
#[diag(infer_oc_intristic_correct_type, code = "E0308")]
|
||||||
IntristicCorrectType {
|
IntristicCorrectType {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_method_correct_type, code = "E0308")]
|
#[diag(infer_oc_method_correct_type, code = "E0308")]
|
||||||
MethodCorrectType {
|
MethodCorrectType {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_closure_selfref, code = "E0644")]
|
#[diag(infer_oc_closure_selfref, code = "E0644")]
|
||||||
ClosureSelfref {
|
ClosureSelfref {
|
||||||
|
@ -1520,13 +1520,13 @@ pub enum FailureCodeDiagnostics {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
#[diag(infer_oc_generic, code = "E0308")]
|
#[diag(infer_oc_generic, code = "E0308")]
|
||||||
Generic {
|
Generic {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: Span,
|
span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ use super::lexical_region_resolve::RegionResolutionError;
|
||||||
use super::region_constraints::GenericKind;
|
use super::region_constraints::GenericKind;
|
||||||
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
|
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
|
||||||
|
|
||||||
use crate::errors::{self, Error0308Subdiags, FailureCodeDiagnostics};
|
use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags};
|
||||||
use crate::infer;
|
use crate::infer;
|
||||||
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
|
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
|
||||||
use crate::infer::ExpectedFound;
|
use crate::infer::ExpectedFound;
|
||||||
|
@ -1924,7 +1924,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
&self,
|
&self,
|
||||||
trace: &TypeTrace<'tcx>,
|
trace: &TypeTrace<'tcx>,
|
||||||
terr: TypeError<'tcx>,
|
terr: TypeError<'tcx>,
|
||||||
) -> Vec<Error0308Subdiags> {
|
) -> Vec<TypeErrorAdditionalDiags> {
|
||||||
use crate::traits::ObligationCauseCode::MatchExpressionArm;
|
use crate::traits::ObligationCauseCode::MatchExpressionArm;
|
||||||
let mut suggestions = Vec::new();
|
let mut suggestions = Vec::new();
|
||||||
let span = trace.cause.span();
|
let span = trace.cause.span();
|
||||||
|
@ -1946,7 +1946,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
&& !code.starts_with("\\u") // forbid all Unicode escapes
|
&& !code.starts_with("\\u") // forbid all Unicode escapes
|
||||||
&& code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII
|
&& code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII
|
||||||
{
|
{
|
||||||
suggestions.push(Error0308Subdiags::MeantByteLiteral { span, code: escape_literal(code) })
|
suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral { span, code: escape_literal(code) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If a character was expected and the found expression is a string literal
|
// If a character was expected and the found expression is a string literal
|
||||||
|
@ -1957,7 +1957,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
&& let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
|
&& let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
|
||||||
&& code.chars().count() == 1
|
&& code.chars().count() == 1
|
||||||
{
|
{
|
||||||
suggestions.push(Error0308Subdiags::MeantCharLiteral { span, code: escape_literal(code) })
|
suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral { span, code: escape_literal(code) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If a string was expected and the found expression is a character literal,
|
// If a string was expected and the found expression is a character literal,
|
||||||
|
@ -1967,7 +1967,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
if let Some(code) =
|
if let Some(code) =
|
||||||
code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
|
code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
|
||||||
{
|
{
|
||||||
suggestions.push(Error0308Subdiags::MeantStrLiteral { span, code: escape_literal(code) })
|
suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { span, code: escape_literal(code) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2032,7 +2032,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
|
&& let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length
|
||||||
&& let Some(span) = self.tcx.hir().opt_span(*hir_id)
|
&& let Some(span) = self.tcx.hir().opt_span(*hir_id)
|
||||||
{
|
{
|
||||||
suggestions.push(Error0308Subdiags::ConsiderSpecifyingLength { span, length: sz.found });
|
suggestions.push(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -2043,7 +2043,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
&& let hir::MatchSource::TryDesugar = source
|
&& let hir::MatchSource::TryDesugar = source
|
||||||
&& let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
|
&& let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
|
||||||
{
|
{
|
||||||
suggestions.push(Error0308Subdiags::TryCannotConvert { found: found_ty.content(), expected: expected_ty.content() });
|
suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert { found: found_ty.content(), expected: expected_ty.content() });
|
||||||
}
|
}
|
||||||
suggestions
|
suggestions
|
||||||
}
|
}
|
||||||
|
@ -2071,7 +2071,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
span: Span,
|
span: Span,
|
||||||
found: Ty<'tcx>,
|
found: Ty<'tcx>,
|
||||||
expected_fields: &List<Ty<'tcx>>,
|
expected_fields: &List<Ty<'tcx>>,
|
||||||
) -> Option<Error0308Subdiags> {
|
) -> Option<TypeErrorAdditionalDiags> {
|
||||||
let [expected_tup_elem] = expected_fields[..] else { return None};
|
let [expected_tup_elem] = expected_fields[..] else { return None};
|
||||||
|
|
||||||
if !self.same_type_modulo_infer(expected_tup_elem, found) {
|
if !self.same_type_modulo_infer(expected_tup_elem, found) {
|
||||||
|
@ -2083,9 +2083,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
let sugg = if code.starts_with('(') && code.ends_with(')') {
|
let sugg = if code.starts_with('(') && code.ends_with(')') {
|
||||||
let before_close = span.hi() - BytePos::from_u32(1);
|
let before_close = span.hi() - BytePos::from_u32(1);
|
||||||
Error0308Subdiags::TupleOnlyComma { span: span.with_hi(before_close).shrink_to_hi() }
|
TypeErrorAdditionalDiags::TupleOnlyComma {
|
||||||
|
span: span.with_hi(before_close).shrink_to_hi(),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Error0308Subdiags::TupleAlsoParentheses {
|
TypeErrorAdditionalDiags::TupleAlsoParentheses {
|
||||||
span_low: span.shrink_to_lo(),
|
span_low: span.shrink_to_lo(),
|
||||||
span_high: span.shrink_to_hi(),
|
span_high: span.shrink_to_hi(),
|
||||||
}
|
}
|
||||||
|
@ -2806,8 +2808,8 @@ pub trait ObligationCauseExt<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
terr: TypeError<'tcx>,
|
terr: TypeError<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
) -> FailureCodeDiagnostics;
|
) -> ObligationCauseFailureCode;
|
||||||
fn as_requirement_str(&self) -> &'static str;
|
fn as_requirement_str(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2840,42 +2842,44 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
terr: TypeError<'tcx>,
|
terr: TypeError<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
subdiags: Vec<Error0308Subdiags>,
|
subdiags: Vec<TypeErrorAdditionalDiags>,
|
||||||
) -> FailureCodeDiagnostics {
|
) -> ObligationCauseFailureCode {
|
||||||
use crate::traits::ObligationCauseCode::*;
|
use crate::traits::ObligationCauseCode::*;
|
||||||
match self.code() {
|
match self.code() {
|
||||||
CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
|
CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => {
|
||||||
FailureCodeDiagnostics::MethodCompat { span, subdiags }
|
ObligationCauseFailureCode::MethodCompat { span, subdiags }
|
||||||
}
|
}
|
||||||
CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
|
CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => {
|
||||||
FailureCodeDiagnostics::TypeCompat { span, subdiags }
|
ObligationCauseFailureCode::TypeCompat { span, subdiags }
|
||||||
}
|
}
|
||||||
CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
|
CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => {
|
||||||
FailureCodeDiagnostics::ConstCompat { span, subdiags }
|
ObligationCauseFailureCode::ConstCompat { span, subdiags }
|
||||||
}
|
}
|
||||||
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
|
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
|
||||||
hir::MatchSource::TryDesugar => {
|
hir::MatchSource::TryDesugar => {
|
||||||
FailureCodeDiagnostics::TryCompat { span, subdiags }
|
ObligationCauseFailureCode::TryCompat { span, subdiags }
|
||||||
}
|
}
|
||||||
_ => FailureCodeDiagnostics::MatchCompat { span, subdiags },
|
_ => ObligationCauseFailureCode::MatchCompat { span, subdiags },
|
||||||
},
|
},
|
||||||
IfExpression { .. } => FailureCodeDiagnostics::IfElseDifferent { span, subdiags },
|
IfExpression { .. } => ObligationCauseFailureCode::IfElseDifferent { span, subdiags },
|
||||||
IfExpressionWithNoElse => FailureCodeDiagnostics::NoElse { span },
|
IfExpressionWithNoElse => ObligationCauseFailureCode::NoElse { span },
|
||||||
LetElse => FailureCodeDiagnostics::NoDiverge { span, subdiags },
|
LetElse => ObligationCauseFailureCode::NoDiverge { span, subdiags },
|
||||||
MainFunctionType => FailureCodeDiagnostics::FnMainCorrectType { span },
|
MainFunctionType => ObligationCauseFailureCode::FnMainCorrectType { span },
|
||||||
StartFunctionType => FailureCodeDiagnostics::FnStartCorrectType { span, subdiags },
|
StartFunctionType => ObligationCauseFailureCode::FnStartCorrectType { span, subdiags },
|
||||||
IntrinsicType => FailureCodeDiagnostics::IntristicCorrectType { span, subdiags },
|
IntrinsicType => ObligationCauseFailureCode::IntristicCorrectType { span, subdiags },
|
||||||
MethodReceiver => FailureCodeDiagnostics::MethodCorrectType { span, subdiags },
|
MethodReceiver => ObligationCauseFailureCode::MethodCorrectType { span, subdiags },
|
||||||
|
|
||||||
// In the case where we have no more specific thing to
|
// In the case where we have no more specific thing to
|
||||||
// say, also take a look at the error code, maybe we can
|
// say, also take a look at the error code, maybe we can
|
||||||
// tailor to that.
|
// tailor to that.
|
||||||
_ => match terr {
|
_ => match terr {
|
||||||
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
|
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => {
|
||||||
FailureCodeDiagnostics::ClosureSelfref { span }
|
ObligationCauseFailureCode::ClosureSelfref { span }
|
||||||
}
|
}
|
||||||
TypeError::IntrinsicCast => FailureCodeDiagnostics::CantCoerce { span, subdiags },
|
TypeError::IntrinsicCast => {
|
||||||
_ => FailureCodeDiagnostics::Generic { span, subdiags },
|
ObligationCauseFailureCode::CantCoerce { span, subdiags }
|
||||||
|
}
|
||||||
|
_ => ObligationCauseFailureCode::Generic { span, subdiags },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,10 @@ use rustc_span::{sym, BytePos, Span};
|
||||||
use rustc_target::abi::FieldIdx;
|
use rustc_target::abi::FieldIdx;
|
||||||
|
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
ConsiderAddingAwait, DiagArg, Error0308Subdiags, FnConsiderCasting, FnItemsAreDistinct,
|
ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
|
||||||
FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
|
FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
|
||||||
SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
|
SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
|
||||||
SuggestTuplePatternOne,
|
SuggestTuplePatternOne, TypeErrorAdditionalDiags,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::TypeErrCtxt;
|
use super::TypeErrCtxt;
|
||||||
|
@ -484,7 +484,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
&self,
|
&self,
|
||||||
cause: &ObligationCause<'_>,
|
cause: &ObligationCause<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Option<Error0308Subdiags> {
|
) -> Option<TypeErrorAdditionalDiags> {
|
||||||
let hir = self.tcx.hir();
|
let hir = self.tcx.hir();
|
||||||
if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
|
if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
|
||||||
let hir::Node::Item(hir::Item {
|
let hir::Node::Item(hir::Item {
|
||||||
|
@ -531,7 +531,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
|
||||||
visitor.visit_body(&body);
|
visitor.visit_body(&body);
|
||||||
if visitor.result {
|
if visitor.result {
|
||||||
return Some(Error0308Subdiags::AddLetForLetChains{span: span.shrink_to_lo()});
|
return Some(TypeErrorAdditionalDiags::AddLetForLetChains{span: span.shrink_to_lo()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue