Skip to content

Commit a414729

Browse files
committed
“Wrap” invalid access note diagnostics
This shows up better in Xcode, which unfortunately doesn’t really display notes very clearly.
1 parent 086f20a commit a414729

File tree

9 files changed

+278
-137
lines changed

9 files changed

+278
-137
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ namespace swift {
115115
VersionTuple,
116116
LayoutConstraint,
117117
ActorIsolation,
118+
Diagnostic
118119
};
119120

120121
namespace diag {
@@ -146,6 +147,7 @@ namespace swift {
146147
llvm::VersionTuple VersionVal;
147148
LayoutConstraint LayoutConstraintVal;
148149
ActorIsolation ActorIsolationVal;
150+
DiagnosticInfo *DiagnosticVal;
149151
};
150152

151153
public:
@@ -243,6 +245,11 @@ namespace swift {
243245
ActorIsolationVal(AI) {
244246
}
245247

248+
DiagnosticArgument(DiagnosticInfo *D)
249+
: Kind(DiagnosticArgumentKind::Diagnostic),
250+
DiagnosticVal(D) {
251+
}
252+
246253
/// Initializes a diagnostic argument using the underlying type of the
247254
/// given enum.
248255
template<
@@ -343,6 +350,11 @@ namespace swift {
343350
assert(Kind == DiagnosticArgumentKind::ActorIsolation);
344351
return ActorIsolationVal;
345352
}
353+
354+
DiagnosticInfo *getAsDiagnostic() const {
355+
assert(Kind == DiagnosticArgumentKind::Diagnostic);
356+
return DiagnosticVal;
357+
}
346358
};
347359

348360
/// Describes the current behavior to take with a diagnostic.
@@ -410,6 +422,7 @@ namespace swift {
410422
DiagnosticBehavior BehaviorLimit = DiagnosticBehavior::Unspecified;
411423

412424
friend DiagnosticEngine;
425+
friend class InFlightDiagnostic;
413426

414427
public:
415428
// All constructors are intentionally implicit.
@@ -515,6 +528,42 @@ namespace swift {
515528
/// emitted as a warning, but a note will still be emitted as a note.
516529
InFlightDiagnostic &limitBehavior(DiagnosticBehavior limit);
517530

531+
/// Wraps this diagnostic in another diagnostic. That is, \p wrapper will be
532+
/// emitted in place of the diagnostic that otherwise would have been
533+
/// emitted.
534+
///
535+
/// The first argument of \p wrapper must be of type 'Diagnostic *'.
536+
///
537+
/// The emitted diagnostic will have:
538+
///
539+
/// \li The ID, message, and behavior of \c wrapper.
540+
/// \li The arguments of \c wrapper, with the last argument replaced by the
541+
/// diagnostic currently in \c *this.
542+
/// \li The location, ranges, decl, fix-its, and behavior limit of the
543+
/// diagnostic currently in \c *this.
544+
InFlightDiagnostic &wrapIn(const Diagnostic &wrapper);
545+
546+
/// Wraps this diagnostic in another diagnostic. That is, \p ID and
547+
/// \p VArgs will be emitted in place of the diagnostic that otherwise would
548+
/// have been emitted.
549+
///
550+
/// The first argument of \p ID must be of type 'Diagnostic *'.
551+
///
552+
/// The emitted diagnostic will have:
553+
///
554+
/// \li The ID, message, and behavior of \c ID.
555+
/// \li The arguments of \c VArgs, with an argument appended for the
556+
/// diagnostic currently in \c *this.
557+
/// \li The location, ranges, decl, fix-its, and behavior limit of the
558+
/// diagnostic currently in \c *this.
559+
template<typename ...ArgTypes>
560+
InFlightDiagnostic &
561+
wrapIn(Diag<DiagnosticInfo *, ArgTypes...> ID,
562+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
563+
Diagnostic wrapper{ID, nullptr, std::move(VArgs)...};
564+
return wrapIn(wrapper);
565+
}
566+
518567
/// Add a token-based range to the currently-active diagnostic.
519568
InFlightDiagnostic &highlight(SourceRange R);
520569

@@ -678,6 +727,16 @@ namespace swift {
678727
ignoredDiagnostics[(unsigned)id] = ignored;
679728
}
680729

730+
void swap(DiagnosticState &other) {
731+
std::swap(showDiagnosticsAfterFatalError, other.showDiagnosticsAfterFatalError);
732+
std::swap(suppressWarnings, other.suppressWarnings);
733+
std::swap(warningsAsErrors, other.warningsAsErrors);
734+
std::swap(fatalErrorOccurred, other.fatalErrorOccurred);
735+
std::swap(anyErrorOccurred, other.anyErrorOccurred);
736+
std::swap(previousBehavior, other.previousBehavior);
737+
std::swap(ignoredDiagnostics, other.ignoredDiagnostics);
738+
}
739+
681740
private:
682741
// Make the state movable only
683742
DiagnosticState(const DiagnosticState &) = delete;
@@ -706,6 +765,10 @@ namespace swift {
706765
/// The currently active diagnostic, if there is one.
707766
Optional<Diagnostic> ActiveDiagnostic;
708767

768+
/// Diagnostics wrapped by ActiveDiagnostic, if any.
769+
SmallVector<DiagnosticInfo, 2> WrappedDiagnostics;
770+
SmallVector<std::vector<DiagnosticArgument>, 4> WrappedDiagnosticArgs;
771+
709772
/// All diagnostics that have are no longer active but have not yet
710773
/// been emitted due to an open transaction.
711774
SmallVector<Diagnostic, 4> TentativeDiagnostics;

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5879,9 +5879,10 @@ REMARK(attr_objc_name_conflicts_with_access_note, none,
58795879
(StringRef, DescriptiveDeclKind, ObjCSelector, ObjCSelector))
58805880

58815881
// Used on invalid @objc diagnostics
5882-
NOTE(note_invalid_attr_added_by_access_note, none,
5883-
"%select{attribute|modifier}0 '%1' was added by access note for %2",
5884-
(bool, StringRef, StringRef))
5882+
REMARK(wrap_invalid_attr_added_by_access_note, none,
5883+
"access note for %1 failed to add invalid "
5884+
"%select{attribute|modifier}2 '%3': %0",
5885+
(DiagnosticInfo *, StringRef, bool, StringRef))
58855886

58865887
#define UNDEFINE_DIAGNOSTIC_MACROS
58875888
#include "DefineDiagnosticMacros.h"

lib/AST/DiagnosticEngine.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,38 @@ InFlightDiagnostic::limitBehavior(DiagnosticBehavior limit) {
319319
return *this;
320320
}
321321

322+
InFlightDiagnostic &
323+
InFlightDiagnostic::wrapIn(const Diagnostic &wrapper) {
324+
// Save current active diagnostic into WrappedDiagnostics, ignoring state
325+
// so we don't get a None return or influence future diagnostics.
326+
DiagnosticState tempState;
327+
Engine->state.swap(tempState);
328+
llvm::SaveAndRestore<DiagnosticBehavior>
329+
limit(Engine->getActiveDiagnostic().BehaviorLimit,
330+
DiagnosticBehavior::Unspecified);
331+
332+
Engine->WrappedDiagnostics.push_back(
333+
*Engine->diagnosticInfoForDiagnostic(Engine->getActiveDiagnostic()));
334+
335+
Engine->state.swap(tempState);
336+
337+
auto &wrapped = Engine->WrappedDiagnostics.back();
338+
339+
// Copy and update its arg list.
340+
Engine->WrappedDiagnosticArgs.emplace_back(wrapped.FormatArgs);
341+
wrapped.FormatArgs = Engine->WrappedDiagnosticArgs.back();
342+
343+
// Overwrite the ID and argument with those from the wrapper.
344+
Engine->getActiveDiagnostic().ID = wrapper.ID;
345+
Engine->getActiveDiagnostic().Args = wrapper.Args;
346+
347+
// Set the argument to the diagnostic being wrapped.
348+
assert(wrapper.getArgs().front().getKind() == DiagnosticArgumentKind::Diagnostic);
349+
Engine->getActiveDiagnostic().Args.front() = &wrapped;
350+
351+
return *this;
352+
}
353+
322354
void InFlightDiagnostic::flush() {
323355
if (!IsActive)
324356
return;
@@ -525,7 +557,7 @@ static bool isMainActor(Type type) {
525557

526558
/// Format a single diagnostic argument and write it to the given
527559
/// stream.
528-
static void formatDiagnosticArgument(StringRef Modifier,
560+
static void formatDiagnosticArgument(StringRef Modifier,
529561
StringRef ModifierArguments,
530562
ArrayRef<DiagnosticArgument> Args,
531563
unsigned ArgIndex,
@@ -752,6 +784,7 @@ static void formatDiagnosticArgument(StringRef Modifier,
752784
<< FormatOpts.ClosingQuotationMark;
753785
break;
754786
case DiagnosticArgumentKind::ActorIsolation:
787+
assert(Modifier.empty() && "Improper modifier for ActorIsolation argument");
755788
switch (auto isolation = Arg.getAsActorIsolation()) {
756789
case ActorIsolation::ActorInstance:
757790
Out << "actor-isolated";
@@ -775,6 +808,15 @@ static void formatDiagnosticArgument(StringRef Modifier,
775808
Out << "nonisolated";
776809
break;
777810
}
811+
break;
812+
813+
case DiagnosticArgumentKind::Diagnostic: {
814+
assert(Modifier.empty() && "Improper modifier for Diagnostic argument");
815+
auto diagArg = Arg.getAsDiagnostic();
816+
DiagnosticEngine::formatDiagnosticText(Out, diagArg->FormatString,
817+
diagArg->FormatArgs);
818+
break;
819+
}
778820
}
779821
}
780822

@@ -953,6 +995,8 @@ void DiagnosticEngine::flushActiveDiagnostic() {
953995
assert(ActiveDiagnostic && "No active diagnostic to flush");
954996
if (TransactionCount == 0) {
955997
emitDiagnostic(*ActiveDiagnostic);
998+
WrappedDiagnostics.clear();
999+
WrappedDiagnosticArgs.clear();
9561000
} else {
9571001
onTentativeDiagnosticFlush(*ActiveDiagnostic);
9581002
TentativeDiagnostics.emplace_back(std::move(*ActiveDiagnostic));
@@ -965,6 +1009,8 @@ void DiagnosticEngine::emitTentativeDiagnostics() {
9651009
emitDiagnostic(diag);
9661010
}
9671011
TentativeDiagnostics.clear();
1012+
WrappedDiagnostics.clear();
1013+
WrappedDiagnosticArgs.clear();
9681014
}
9691015

9701016
/// Returns the access level of the least accessible PrettyPrintedDeclarations

lib/Sema/TypeCheckAttr.cpp

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,10 +1016,11 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) {
10161016
else {
10171017
firstNameLoc = D->getLoc();
10181018
}
1019-
diagnose(firstNameLoc, diag::objc_name_req_nullary,
1020-
D->getDescriptiveKind())
1021-
.fixItRemoveChars(afterFirstNameLoc, attr->getRParenLoc())
1022-
.limitBehavior(behavior);
1019+
softenIfAccessNote(D, attr,
1020+
diagnose(firstNameLoc, diag::objc_name_req_nullary,
1021+
D->getDescriptiveKind())
1022+
.fixItRemoveChars(afterFirstNameLoc, attr->getRParenLoc())
1023+
.limitBehavior(behavior));
10231024
const_cast<ObjCAttr *>(attr)->setName(
10241025
ObjCSelector(Ctx, 0, objcName->getSelectorPieces()[0]),
10251026
/*implicit=*/false);
@@ -1028,11 +1029,12 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) {
10281029
SourceLoc diagLoc = attr->getLParenLoc();
10291030
if (diagLoc.isInvalid())
10301031
diagLoc = D->getLoc();
1031-
diagnose(diagLoc,
1032-
isa<SubscriptDecl>(D)
1033-
? diag::objc_name_subscript
1034-
: diag::objc_name_deinit)
1035-
.limitBehavior(behavior);
1032+
softenIfAccessNote(D, attr,
1033+
diagnose(diagLoc,
1034+
isa<SubscriptDecl>(D)
1035+
? diag::objc_name_subscript
1036+
: diag::objc_name_deinit)
1037+
.limitBehavior(behavior));
10361038
const_cast<ObjCAttr *>(attr)->clearName();
10371039
} else {
10381040
auto func = cast<AbstractFunctionDecl>(D);
@@ -1063,15 +1065,16 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) {
10631065
SourceLoc firstNameLoc = func->getLoc();
10641066
if (!attr->getNameLocs().empty())
10651067
firstNameLoc = attr->getNameLocs().front();
1066-
diagnose(firstNameLoc,
1067-
diag::objc_name_func_mismatch,
1068-
isa<FuncDecl>(func),
1069-
numArgumentNames,
1070-
numArgumentNames != 1,
1071-
numParameters,
1072-
numParameters != 1,
1073-
func->hasThrows())
1074-
.limitBehavior(behavior);
1068+
softenIfAccessNote(D, attr,
1069+
diagnose(firstNameLoc,
1070+
diag::objc_name_func_mismatch,
1071+
isa<FuncDecl>(func),
1072+
numArgumentNames,
1073+
numArgumentNames != 1,
1074+
numParameters,
1075+
numParameters != 1,
1076+
func->hasThrows())
1077+
.limitBehavior(behavior));
10751078
D->getAttrs().add(
10761079
ObjCAttr::createUnnamed(Ctx, attr->AtLoc, attr->Range.Start));
10771080
D->getAttrs().removeAttribute(attr);
@@ -1180,26 +1183,13 @@ void TypeChecker::checkDeclAttributes(Decl *D) {
11801183
default: break;
11811184
}
11821185

1183-
DiagnosticBehavior behavior = attr->getAddedByAccessNote()
1184-
? DiagnosticBehavior::Remark
1185-
: DiagnosticBehavior::Unspecified;
1186-
11871186
if (!OnlyKind.empty())
11881187
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind,
1189-
attr, OnlyKind)
1190-
.limitBehavior(behavior);
1188+
attr, OnlyKind);
11911189
else if (attr->isDeclModifier())
1192-
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr)
1193-
.limitBehavior(behavior);
1190+
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
11941191
else
1195-
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr)
1196-
.limitBehavior(behavior);
1197-
1198-
if (attr->getAddedByAccessNote()) {
1199-
auto notesFileReason = D->getModuleContext()->getAccessNotes().Reason;
1200-
D->diagnose(diag::note_invalid_attr_added_by_access_note,
1201-
attr->isDeclModifier(), attr->getAttrName(), notesFileReason);
1202-
}
1192+
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr);
12031193
}
12041194
Checker.checkOriginalDefinedInAttrs(D, ODIAttrs);
12051195
}

0 commit comments

Comments
 (0)