Skip to content

Commit 182fd73

Browse files
committed
DiagnosticEngine: Support TypeAttribute diagnostic arguments
1 parent cba4aeb commit 182fd73

26 files changed

+160
-65
lines changed

docs/Diagnostics.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ Clang also has a kind of diagnostic called a "remark", which represents informat
4141
- Normal: "cannot call 'super.init' outside of an initializer"
4242
- Better: "'super.init' cannot be called outside of an initializer"
4343

44-
- When referring to attributes by name, use *either* "the 'foo' attribute" or "'@foo'", rather than "the '@foo' attribute".
44+
- When referring to attributes by name, use *either* `attribute 'foo'` or
45+
`'@foo'`, rather than `attribute '@foo'`.
4546

4647
- Match the tone and phrasing of other diagnostics. Some common phrases:
4748

@@ -130,7 +131,10 @@ If you run into any issues or have questions while following the steps above, fe
130131

131132
- `%%` - Emits a literal percent sign.
132133

133-
There are several format specifiers that are specific to `Decl` parameters:
134+
The following subsections describe format specifiers that are specific to
135+
diagnostic arguments of a given type.
136+
137+
#### `Decl`
134138

135139
- `%kind0` - Prefixes the declaration's name with its descriptive decl kind (e.g. `instance method 'foo(x:)'`).
136140

@@ -140,6 +144,11 @@ There are several format specifiers that are specific to `Decl` parameters:
140144

141145
- `%kindbase0` - Combines `kind` and `base` (e.g. `instance method 'foo'`).
142146

147+
#### `TypeAttribute`
148+
149+
- `%kind0` - Replaced with `attribute 'foo'`, whereas `%0` is replaced with
150+
`'@foo'`.
151+
143152
Note: If your diagnostic could apply to accessors, be careful how you format the declaration's name; accessors have an empty name, so you need to display their accessor kind and the name of their storage decl instead. Inserting the name with a `Decl *` parameter will handle these complications automatically; if you want to use `DeclName` or `Identifier` instead, you'll probably need a separate version of the diagnostic for accessors.
144153

145154
### Diagnostic Verifier ###

include/swift/AST/Attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3781,6 +3781,10 @@ class alignas(1 << AttrAlignInBits) TypeAttribute
37813781
TypeAttrKind getKind() const {
37823782
return TypeAttrKind(Bits.TypeAttribute.Kind);
37833783
}
3784+
3785+
/// - Note: Do not call this directly when emitting a diagnostic. Instead,
3786+
/// define the diagnostic to accept a `const TypeAttribute *` and use the
3787+
/// appropriate format specifier.
37843788
const char *getAttrName() const {
37853789
return getAttrName(getKind());
37863790
}

include/swift/AST/DiagnosticEngine.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ namespace swift {
5252
class GeneratedSourceInfo;
5353
class SourceManager;
5454
class TypeAliasDecl;
55+
class TypeAttribute;
5556
class ValueDecl;
5657
class SourceFile;
5758

@@ -146,6 +147,7 @@ namespace swift {
146147
DescriptiveDeclKind,
147148
DescriptiveStmtKind,
148149
DeclAttribute,
150+
TypeAttribute,
149151
AvailabilityDomain,
150152
AvailabilityRange,
151153
VersionTuple,
@@ -185,6 +187,7 @@ namespace swift {
185187
DescriptiveDeclKind DescriptiveDeclKindVal;
186188
StmtKind DescriptiveStmtKindVal;
187189
const DeclAttribute *DeclAttributeVal;
190+
const TypeAttribute *TypeAttributeVal;
188191
AvailabilityDomain AvailabilityDomainVal;
189192
AvailabilityRange AvailabilityRangeVal;
190193
llvm::VersionTuple VersionVal;
@@ -287,6 +290,8 @@ namespace swift {
287290
: Kind(DiagnosticArgumentKind::DeclAttribute),
288291
DeclAttributeVal(attr) {}
289292

293+
DiagnosticArgument(const TypeAttribute *attr);
294+
290295
DiagnosticArgument(const AvailabilityDomain domain)
291296
: Kind(DiagnosticArgumentKind::AvailabilityDomain),
292297
AvailabilityDomainVal(domain) {}
@@ -420,6 +425,8 @@ namespace swift {
420425
return DeclAttributeVal;
421426
}
422427

428+
const TypeAttribute *getAsTypeAttribute() const;
429+
423430
const AvailabilityDomain getAsAvailabilityDomain() const {
424431
assert(Kind == DiagnosticArgumentKind::AvailabilityDomain);
425432
return AvailabilityDomainVal;

include/swift/AST/DiagnosticsCommon.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,10 @@ REMARK(remark_scanner_invalidate_missing_cas, none,
251251
// MARK: custom attribute diagnostics
252252
//------------------------------------------------------------------------------
253253

254-
ERROR(unknown_attribute,none,
254+
ERROR(unknown_attr_name,none,
255255
"unknown attribute '%0'", (StringRef))
256+
ERROR(unknown_type_attr,none,
257+
"unknown %kind0", (const TypeAttribute *))
256258

257259
//------------------------------------------------------------------------------
258260
// MARK: macro diagnostics

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1484,9 +1484,12 @@ ERROR(multiple_access_level_modifiers,none,
14841484
"multiple incompatible access-level modifiers specified", ())
14851485
NOTE(previous_access_level_modifier,none,
14861486
"previous modifier specified here", ())
1487-
ERROR(mutually_exclusive_attrs,none,
1487+
ERROR(mutually_exclusive_decl_attrs,none,
14881488
"%0 contradicts previous %select{attribute|modifier}2 %1",
14891489
(DeclAttribute, DeclAttribute, bool))
1490+
ERROR(mutually_exclusive_type_attrs,none,
1491+
"%0 contradicts previous %1",
1492+
(const TypeAttribute *, const TypeAttribute *))
14901493
ERROR(mutually_exclusive_attr_names,none,
14911494
"'%0' contradicts previous %select{attribute|modifier}2 '%1'",
14921495
(StringRef, StringRef, bool))

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5862,9 +5862,11 @@ ERROR(sendable_raw_storage,none,
58625862
"struct %0 with @_rawLayout does not conform to the 'Sendable' protocol", (DeclName))
58635863

58645864
ERROR(typeattr_not_inheritance_clause,none,
5865-
"'%0' attribute only applies in inheritance clauses", (StringRef))
5865+
"%0 only applies in inheritance clauses",
5866+
(const TypeAttribute *))
58665867
ERROR(typeattr_not_existential,none,
5867-
"'%0' attribute cannot apply to non-protocol type %1", (StringRef, Type))
5868+
"%0 cannot apply to non-protocol type %1",
5869+
(const TypeAttribute *, Type))
58685870

58695871
WARNING(unchecked_conformance_not_special,none,
58705872
"@unchecked conformance to %0 has no meaning", (Type))
@@ -6262,8 +6264,8 @@ NOTE(overridden_required_initializer_here,none,
62626264
"overridden required initializer is here", ())
62636265

62646266
// Functions
6265-
ERROR(attribute_requires_function_type,none,
6266-
"@%0 attribute only applies to function types", (StringRef))
6267+
ERROR(type_attr_requires_function_type,none,
6268+
"%0 only applies to function types", (const TypeAttribute *))
62676269
ERROR(generic_function_type,none,
62686270
"function values cannot be generic", ())
62696271
ERROR(unsupported_convention,none,
@@ -7603,8 +7605,8 @@ ERROR(marker_protocol_conditional_conformance,none,
76037605
//------------------------------------------------------------------------------
76047606

76057607
ERROR(differentiable_programming_attr_used_without_required_module, none,
7606-
"'@%0' attribute used without importing module %1",
7607-
(StringRef, Identifier))
7608+
"%0 used without importing module %1",
7609+
(const TypeAttribute *, Identifier))
76087610

76097611
//------------------------------------------------------------------------------
76107612
// MARK: OSLog

lib/AST/DiagnosticEngine.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ static constexpr const char *const fixItStrings[] = {
152152
"<not a fix-it>",
153153
};
154154

155+
DiagnosticArgument::DiagnosticArgument(const TypeAttribute *attr)
156+
: Kind(DiagnosticArgumentKind::TypeAttribute), TypeAttributeVal(attr) {}
157+
158+
const TypeAttribute *DiagnosticArgument::getAsTypeAttribute() const {
159+
ASSERT(Kind == DiagnosticArgumentKind::TypeAttribute);
160+
return TypeAttributeVal;
161+
}
162+
155163
DiagnosticState::DiagnosticState() {
156164
// Initialize our ignored diagnostics to defaults
157165
ignoredDiagnostics.reserve(NumDiagIDs);
@@ -1079,6 +1087,26 @@ static void formatDiagnosticArgument(StringRef Modifier,
10791087
}
10801088
break;
10811089
}
1090+
case DiagnosticArgumentKind::TypeAttribute: {
1091+
bool useAtStyle = true;
1092+
if (Modifier == "kind") {
1093+
useAtStyle = false;
1094+
} else {
1095+
ASSERT(Modifier.empty() &&
1096+
"Improper modifier for TypeAttribute argument");
1097+
}
1098+
1099+
if (!useAtStyle) {
1100+
Out << "attribute ";
1101+
}
1102+
Out << FormatOpts.OpeningQuotationMark;
1103+
if (useAtStyle) {
1104+
Out << '@';
1105+
}
1106+
Out << Arg.getAsTypeAttribute()->getAttrName();
1107+
Out << FormatOpts.ClosingQuotationMark;
1108+
break;
1109+
}
10821110
case DiagnosticArgumentKind::AvailabilityDomain:
10831111
assert(Modifier.empty() &&
10841112
"Improper modifier for AvailabilityDomain argument");

lib/Parse/ParseDecl.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4343,7 +4343,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,
43434343
if (TypeAttribute::getAttrKindFromString(Tok.getText()).has_value())
43444344
diagnose(Tok, diag::type_attribute_applied_to_decl);
43454345
else if (Tok.isContextualKeyword("unknown")) {
4346-
diagnose(Tok, diag::unknown_attribute, "unknown");
4346+
diagnose(Tok, diag::unknown_attr_name, "unknown");
43474347
} else {
43484348
// Change the context to create a custom attribute syntax.
43494349
auto customAttr = parseCustomAttribute(AtLoc);
@@ -4713,7 +4713,7 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result,
47134713
} else {
47144714
// We could use diag::only_allowed_in_sil here, but it's better
47154715
// not to mention SIL in general diagnostics.
4716-
diagnose(AtLoc, diag::unknown_attribute, Text);
4716+
diagnose(AtLoc, diag::unknown_attr_name, Text);
47174717
}
47184718
}
47194719
return makeParserSuccess();
@@ -5457,7 +5457,7 @@ static void diagnoseOperatorFixityAttributes(Parser &P,
54575457
for (auto it = fixityAttrs.begin(); it != fixityAttrs.end(); ++it) {
54585458
if (it != fixityAttrs.begin()) {
54595459
auto *attr = *it;
5460-
P.diagnose(attr->getLocation(), diag::mutually_exclusive_attrs, attr,
5460+
P.diagnose(attr->getLocation(), diag::mutually_exclusive_decl_attrs, attr,
54615461
fixityAttrs.front(), attr->isDeclModifier())
54625462
.fixItRemove(attr->getRange());
54635463
attr->setInvalid();

lib/Parse/ParseStmt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2720,7 +2720,7 @@ ParserResult<CaseStmt> Parser::parseStmtCase(bool IsActive) {
27202720
assert(peekToken().is(tok::identifier) && "isAtStartOfSwitchCase() lied");
27212721

27222722
consumeToken(tok::at_sign);
2723-
diagnose(Tok, diag::unknown_attribute, Tok.getText());
2723+
diagnose(Tok, diag::unknown_attr_name, Tok.getText());
27242724
consumeToken(tok::identifier);
27252725

27262726
if (Tok.is(tok::l_paren))

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4843,7 +4843,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
48434843
} else if (attr == "builtin") {
48444844
setFromBuiltin(true);
48454845
} else {
4846-
P.diagnose(identLoc, diag::unknown_attribute, attr);
4846+
P.diagnose(identLoc, diag::unknown_attr_name, attr);
48474847
}
48484848

48494849
if (!P.consumeIf(tok::r_square))
@@ -7045,14 +7045,14 @@ bool SILParser::parseCallInstruction(SILLocation InstLoc,
70457045
llvm::SmallString<64> name;
70467046
llvm::raw_svector_ostream os(name);
70477047
os << isolationCrossing->getCalleeIsolation();
7048-
P.diagnose(InstLoc.getSourceLoc(), diag::unknown_attribute, name);
7048+
P.diagnose(InstLoc.getSourceLoc(), diag::unknown_attr_name, name);
70497049
}
70507050
if (isolationCrossing->getCallerIsolation() !=
70517051
ActorIsolation::Unspecified) {
70527052
llvm::SmallString<64> name;
70537053
llvm::raw_svector_ostream os(name);
70547054
os << isolationCrossing->getCalleeIsolation();
7055-
P.diagnose(InstLoc.getSourceLoc(), diag::unknown_attribute, name);
7055+
P.diagnose(InstLoc.getSourceLoc(), diag::unknown_attr_name, name);
70567056
}
70577057
return true;
70587058
}

lib/SIL/Parser/SILParser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ class SILParser {
411411
if (allowed)
412412
setEnum(existing, value, name, loc);
413413
else
414-
P.diagnose(loc, diag::unknown_attribute, name);
414+
P.diagnose(loc, diag::unknown_attr_name, name);
415415
}
416416
};
417417

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4409,7 +4409,8 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
44094409
llvm::raw_string_ostream out(typeName);
44104410
typeRepr->print(out);
44114411

4412-
Ctx.Diags.diagnose(attr->getLocation(), diag::unknown_attribute, typeName);
4412+
Ctx.Diags.diagnose(attr->getLocation(), diag::unknown_attr_name,
4413+
typeName);
44134414
}
44144415

44154416
attr->setInvalid();

lib/Sema/TypeCheckType.cpp

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,7 +1468,7 @@ static Type diagnoseUnknownType(const TypeResolution &resolution,
14681468
if (parentType.isNull()) {
14691469
// Tailored diagnostic for custom attributes.
14701470
if (resolution.getOptions().is(TypeResolverContext::CustomAttr)) {
1471-
diags.diagnose(repr->getNameLoc(), diag::unknown_attribute,
1471+
diags.diagnose(repr->getNameLoc(), diag::unknown_attr_name,
14721472
repr->getNameRef().getBaseIdentifier().str());
14731473

14741474
return ErrorType::get(ctx);
@@ -3235,9 +3235,8 @@ void TypeAttrSet::diagnoseConflict(TypeAttrKind representativeKind,
32353235
}
32363236

32373237
// Generic conflict diagnostic
3238-
diagnose(secondAttr->getStartLoc(), diag::mutually_exclusive_attr_names,
3239-
secondAttr->getAttrName(), firstAttr->getAttrName(),
3240-
/*modifier*/ false);
3238+
diagnose(secondAttr->getStartLoc(), diag::mutually_exclusive_type_attrs,
3239+
secondAttr, firstAttr);
32413240
}
32423241

32433242
void TypeAttrSet::diagnoseUnclaimed(const TypeResolution &resolution,
@@ -3292,7 +3291,7 @@ void TypeAttrSet::diagnoseUnclaimed(CustomAttr *attr,
32923291
typeName = attr->getType().getString();
32933292
}
32943293

3295-
diagnose(attr->getLocation(), diag::unknown_attribute, typeName);
3294+
diagnose(attr->getLocation(), diag::unknown_attr_name, typeName);
32963295
}
32973296

32983297
static bool isFunctionAttribute(TypeAttrKind attrKind) {
@@ -3328,7 +3327,7 @@ void TypeAttrSet::diagnoseUnclaimed(TypeAttribute *attr,
33283327
// Use a special diagnostic for SIL attributes.
33293328
if (!(options & TypeResolutionFlags::SILType) &&
33303329
TypeAttribute::isSilOnly(attr->getKind())) {
3331-
diagnose(attr->getStartLoc(), diag::unknown_attribute, attr->getAttrName());
3330+
diagnose(attr->getStartLoc(), diag::unknown_type_attr, attr);
33323331
return;
33333332
}
33343333

@@ -3349,8 +3348,7 @@ void TypeAttrSet::diagnoseUnclaimed(TypeAttribute *attr,
33493348
}
33503349

33513350
auto diagnostic = diagnose(attr->getStartLoc(),
3352-
diag::attribute_requires_function_type,
3353-
attr->getAttrName());
3351+
diag::type_attr_requires_function_type, attr);
33543352
if (isa<EscapingTypeAttr>(attr))
33553353
diagnostic.fixItRemove(attr->getSourceRange());
33563354
return;
@@ -3497,12 +3495,11 @@ TypeResolver::resolveAttributedType(TypeRepr *repr, TypeResolutionOptions option
34973495
if (!options.is(TypeResolverContext::Inherited) ||
34983496
getDeclContext()->getSelfProtocolDecl()) {
34993497
diagnoseInvalid(repr, attr->getAtLoc(),
3500-
diag::typeattr_not_inheritance_clause,
3501-
attr->getAttrName());
3498+
diag::typeattr_not_inheritance_clause, attr);
35023499
ty = ErrorType::get(getASTContext());
35033500
} else if (!ty->isConstraintType()) {
3504-
diagnoseInvalid(repr, attr->getAtLoc(),
3505-
diag::typeattr_not_existential, attr->getAttrName(), ty);
3501+
diagnoseInvalid(repr, attr->getAtLoc(), diag::typeattr_not_existential,
3502+
attr, ty);
35063503
ty = ErrorType::get(getASTContext());
35073504
}
35083505

@@ -3637,8 +3634,7 @@ TypeResolver::resolveAttributedType(TypeRepr *repr, TypeResolutionOptions option
36373634
diagnose(
36383635
noDerivativeAttr->getAtLoc(),
36393636
diag::differentiable_programming_attr_used_without_required_module,
3640-
TypeAttribute::getAttrName(TypeAttrKind::NoDerivative),
3641-
getASTContext().Id_Differentiation);
3637+
noDerivativeAttr, getASTContext().Id_Differentiation);
36423638
} else if (!isNoDerivativeAllowed) {
36433639
bool isVariadicFunctionParam =
36443640
options.is(TypeResolverContext::VariadicFunctionInput) &&
@@ -4072,7 +4068,7 @@ NeverNullType TypeResolver::resolveASTFunctionType(
40724068
diagnoseInvalid(
40734069
repr, diffAttr->getAtLoc(),
40744070
diag::differentiable_programming_attr_used_without_required_module,
4075-
diffAttr->getAttrName(), ctx.Id_Differentiation);
4071+
diffAttr, ctx.Id_Differentiation);
40764072
}
40774073
}
40784074

@@ -4491,7 +4487,7 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr,
44914487
diagnoseInvalid(
44924488
repr, diffAttr->getAtLoc(),
44934489
diag::differentiable_programming_attr_used_without_required_module,
4494-
diffAttr->getAttrName(), getASTContext().Id_Differentiation);
4490+
diffAttr, getASTContext().Id_Differentiation);
44954491
hasError = true;
44964492
}
44974493
}
@@ -5023,7 +5019,7 @@ TypeResolver::resolveDeclRefTypeRepr(DeclRefTypeRepr *repr,
50235019
// Tailored diagnostic for custom attributes.
50245020
if (options.is(TypeResolverContext::CustomAttr)) {
50255021
auto &ctx = resolution.getASTContext();
5026-
ctx.Diags.diagnose(repr->getNameLoc(), diag::unknown_attribute,
5022+
ctx.Diags.diagnose(repr->getNameLoc(), diag::unknown_attr_name,
50275023
repr->getNameRef().getBaseIdentifier().str());
50285024

50295025
return ErrorType::get(ctx);

test/AutoDiff/Sema/differentiable_features_disabled.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// RUN: %target-swift-frontend -typecheck -verify %s
22

3-
// expected-error @+1 {{'@differentiable' attribute used without importing module '_Differentiation'}}
3+
// expected-error @+1 {{'@differentiable' used without importing module '_Differentiation'}}
44
let _: @differentiable(reverse) (Float) -> Float
55

6-
// expected-error @+2 {{'@differentiable' attribute used without importing module '_Differentiation'}}
7-
// expected-error @+1 {{'@noDerivative' attribute used without importing module '_Differentiation'}}
6+
// expected-error @+2 {{'@differentiable' used without importing module '_Differentiation'}}
7+
// expected-error @+1 {{'@noDerivative' used without importing module '_Differentiation'}}
88
let _: @differentiable(reverse) (Float, @noDerivative Float) -> Float
99

10-
// expected-error @+1 {{'@noDerivative' attribute used without importing module '_Differentiation'}}
10+
// expected-error @+1 {{'@noDerivative' used without importing module '_Differentiation'}}
1111
let _: (Float, @noDerivative Float) -> Float
1212

13-
// expected-error @+1 {{'@noDerivative' attribute used without importing module '_Differentiation'}}
13+
// expected-error @+1 {{'@noDerivative' used without importing module '_Differentiation'}}
1414
let _: @noDerivative Float
1515

1616
func id(_ x: Float) -> Float {

0 commit comments

Comments
 (0)