Skip to content

Commit 2ab1e05

Browse files
committed
[NFC] Add new DiagnosticEngine Decl features
Implements several enhancements to DiagnosticEngine’s handling of Decl arguments: • All Decl classes, not just ValueDecls, are now valid to use as arguments. • There is built-in logic to handle accessors by printing a phrase like `getter for 'foo'`, so this no longer has to be special-cased for each diagnostic. • `%kind` can be used to insert the descriptive kind before a name; `%kindonly` inserts only the descriptive kind. This can eliminate kind/name argument pairs. • `%base` can insert only the base name, leaving out any argument labels; `%kindbase` combines `%kind` and `%base`. This PR is marked NFC because there are no intentional uses of these changes yet.
1 parent fc1043e commit 2ab1e05

File tree

3 files changed

+93
-12
lines changed

3 files changed

+93
-12
lines changed

docs/Diagnostics.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,26 @@ If you run into any issues or have questions while following the steps above, fe
128128

129129
- `%0`, `%1`, etc - Formats the specified diagnostic argument based on its type.
130130

131-
- `%select{a|b|c}0` - Chooses from a list of alternatives, separated by vertical bars, based on the value of the given argument. In this example, a value of 2 in diagnostic argument 0 would result in "c" being output. The argument to the %select may be an integer, enum, or StringRef. If it's a StringRef, the specifier acts as an emptiness check.
131+
- `%select{a|b|c}0` - Chooses from a list of alternatives, separated by vertical bars, based on the value of the given argument. In this example, a value of 2 in diagnostic argument 0 would result in "c" being output. The argument to the %select may be an integer, enum, StringRef, Identifier, or Decl. If it's a StringRef or Identifier, the specifier acts as an emptiness check; if it's a Decl, it acts as a null check.
132132

133133
- `%s0` - Produces an "s" if the given argument is anything other than 1, as meant for an English plural. This isn't particularly localizable without a more general `%plural` form, but most diagnostics try to avoid cases where a plural/singular distinction would be necessary in the first place.
134134

135135
- `%error` - Represents a branch in a `%select` that should never be taken. In debug builds of the compiler this produces an assertion failure.
136136

137137
- `%%` - Emits a literal percent sign.
138138

139+
There are several format specifiers that are specific to `Decl` parameters:
140+
141+
- `%kind0` - Prefixes the declaration's name with its descriptive decl kind (e.g. `instance method 'foo(x:)'`).
142+
143+
- `%kindonly0` - Inserts only the descriptive decl kind (e.g. `instance method`).
144+
145+
- `%base0` - Inserts only the base name, removing any argument labels (e.g. `'foo'`).
146+
147+
- `%kindbase0` - Combines `kind` and `base` (e.g. `instance method 'foo'`).
148+
149+
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.
150+
139151
### Diagnostic Verifier ###
140152

141153
(This section is specific to the Swift compiler's diagnostic engine.)

include/swift/AST/DiagnosticEngine.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class NamedDecl;
3838
}
3939

4040
namespace swift {
41+
class ConstructorDecl;
4142
class Decl;
4243
class DeclAttribute;
4344
class DiagnosticEngine;
@@ -109,7 +110,7 @@ namespace swift {
109110
Unsigned,
110111
Identifier,
111112
ObjCSelector,
112-
ValueDecl,
113+
Decl,
113114
Type,
114115
TypeRepr,
115116
FullyQualifiedType,
@@ -143,7 +144,7 @@ namespace swift {
143144
StringRef StringVal;
144145
DeclNameRef IdentifierVal;
145146
ObjCSelector ObjCSelectorVal;
146-
const ValueDecl *TheValueDecl;
147+
const Decl *TheDecl;
147148
Type TypeVal;
148149
TypeRepr *TyR;
149150
FullyQualified<Type> FullyQualifiedTypeVal;
@@ -194,8 +195,8 @@ namespace swift {
194195
: Kind(DiagnosticArgumentKind::ObjCSelector), ObjCSelectorVal(S) {
195196
}
196197

197-
DiagnosticArgument(const ValueDecl *VD)
198-
: Kind(DiagnosticArgumentKind::ValueDecl), TheValueDecl(VD) {
198+
DiagnosticArgument(const Decl *VD)
199+
: Kind(DiagnosticArgumentKind::Decl), TheDecl(VD) {
199200
}
200201

201202
DiagnosticArgument(Type T)
@@ -305,9 +306,9 @@ namespace swift {
305306
return ObjCSelectorVal;
306307
}
307308

308-
const ValueDecl *getAsValueDecl() const {
309-
assert(Kind == DiagnosticArgumentKind::ValueDecl);
310-
return TheValueDecl;
309+
const Decl *getAsDecl() const {
310+
assert(Kind == DiagnosticArgumentKind::Decl);
311+
return TheDecl;
311312
}
312313

313314
Type getAsType() const {

lib/AST/DiagnosticEngine.cpp

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,11 +658,79 @@ static void formatDiagnosticArgument(StringRef Modifier,
658658
<< FormatOpts.ClosingQuotationMark;
659659
break;
660660

661-
case DiagnosticArgumentKind::ValueDecl:
662-
Out << FormatOpts.OpeningQuotationMark;
663-
Arg.getAsValueDecl()->getName().printPretty(Out);
664-
Out << FormatOpts.ClosingQuotationMark;
661+
case DiagnosticArgumentKind::Decl: {
662+
auto D = Arg.getAsDecl();
663+
664+
if (Modifier == "select") {
665+
formatSelectionArgument(ModifierArguments, Args, D ? 1 : 0, FormatOpts,
666+
Out);
667+
break;
668+
}
669+
670+
// Parse info out of modifier
671+
bool includeKind = false;
672+
bool includeName = true;
673+
bool baseNameOnly = false;
674+
675+
if (Modifier == "kind") {
676+
includeKind = true;
677+
} else if (Modifier == "base") {
678+
baseNameOnly = true;
679+
} else if (Modifier == "kindbase") {
680+
includeKind = true;
681+
baseNameOnly = true;
682+
} else if (Modifier == "kindonly") {
683+
includeName = false;
684+
} else {
685+
assert(Modifier.empty() && "Improper modifier for ValueDecl argument");
686+
}
687+
688+
// If it's an accessor, describe that and then switch to discussing its
689+
// storage.
690+
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
691+
Out << Decl::getDescriptiveKindName(D->getDescriptiveKind()) << " for ";
692+
D = accessor->getStorage();
693+
}
694+
695+
// If it's an extension, describe that and then switch to discussing its
696+
// nominal type.
697+
if (auto ext = dyn_cast<ExtensionDecl>(D)) {
698+
Out << Decl::getDescriptiveKindName(D->getDescriptiveKind()) << " of ";
699+
D = ext->getSelfNominalTypeDecl();
700+
}
701+
702+
// Figure out the name we want to print.
703+
DeclName name;
704+
if (includeName) {
705+
if (auto VD = dyn_cast<ValueDecl>(D))
706+
name = VD->getName();
707+
else if (auto PGD = dyn_cast<PrecedenceGroupDecl>(D))
708+
name = PGD->getName();
709+
else if (auto OD = dyn_cast<OperatorDecl>(D))
710+
name = OD->getName();
711+
else if (auto MMD = dyn_cast<MissingMemberDecl>(D))
712+
name = MMD->getName();
713+
714+
if (baseNameOnly && name)
715+
name = name.getBaseName();
716+
}
717+
718+
// If the declaration is anonymous or we asked for a descriptive kind, print
719+
// it.
720+
if (!name || includeKind) {
721+
Out << Decl::getDescriptiveKindName(D->getDescriptiveKind());
722+
if (name)
723+
Out << " ";
724+
}
725+
726+
// Print the name.
727+
if (name) {
728+
Out << FormatOpts.OpeningQuotationMark;
729+
name.printPretty(Out);
730+
Out << FormatOpts.ClosingQuotationMark;
731+
}
665732
break;
733+
}
666734

667735
case DiagnosticArgumentKind::FullyQualifiedType:
668736
case DiagnosticArgumentKind::Type: {

0 commit comments

Comments
 (0)