Skip to content

Commit e5f6740

Browse files
committed
Delay Codable synthesis notes until after the original diagnostic.
The notes that go along with Codable synthesis were being emitted before the actual "type does not conform" diagnostic, so they would be associated with a prior diagnostic... or dropped on the floor. Queue up the synthesis notes and emit them after the "type does not conform" diagnostic.
1 parent d54abea commit e5f6740

File tree

1 file changed

+113
-52
lines changed

1 file changed

+113
-52
lines changed

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 113 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,21 @@ static EnumDecl *addImplicitCodingKeys(NominalTypeDecl *target) {
236236
return addImplicitCodingKeys(target, caseIdentifiers, C.Id_CodingKeys);
237237
}
238238

239+
namespace {
240+
/// Container for a set of functions that produces notes used when a
241+
/// synthesized conformance fails.
242+
struct DelayedNotes : public std::vector<std::function<void()>> {
243+
~DelayedNotes() {
244+
for (const auto &fn : *this) {
245+
fn();
246+
}
247+
}
248+
};
249+
}
250+
239251
static EnumDecl *validateCodingKeysType(const DerivedConformance &derived,
240-
TypeDecl *_codingKeysTypeDecl) {
252+
TypeDecl *_codingKeysTypeDecl,
253+
DelayedNotes &delayedNotes) {
241254
auto &C = derived.Context;
242255
// CodingKeys may be a typealias. If so, follow the alias to its canonical
243256
// type. We are creating a copy here, so we can hold on to the original
@@ -258,17 +271,22 @@ static EnumDecl *validateCodingKeysType(const DerivedConformance &derived,
258271
SourceLoc loc = codingKeysTypeDecl ? codingKeysTypeDecl->getLoc()
259272
: cast<TypeDecl>(_codingKeysTypeDecl)->getLoc();
260273

261-
C.Diags.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here,
262-
derived.getProtocolType());
274+
delayedNotes.push_back([=] {
275+
ASTContext &C = derived.getProtocolType()->getASTContext();
276+
C.Diags.diagnose(loc, diag::codable_codingkeys_type_does_not_conform_here,
277+
derived.getProtocolType());
278+
});
263279
return nullptr;
264280
}
265281

266282
auto *codingKeysDecl =
267283
dyn_cast_or_null<EnumDecl>(codingKeysType->getAnyNominal());
268284
if (!codingKeysDecl) {
269-
codingKeysTypeDecl->diagnose(
270-
diag::codable_codingkeys_type_is_not_an_enum_here,
271-
derived.getProtocolType());
285+
delayedNotes.push_back([=] {
286+
codingKeysTypeDecl->diagnose(
287+
diag::codable_codingkeys_type_is_not_an_enum_here,
288+
derived.getProtocolType());
289+
});
272290
return nullptr;
273291
}
274292

@@ -282,8 +300,10 @@ static EnumDecl *validateCodingKeysType(const DerivedConformance &derived,
282300
/// \param codingKeysTypeDecl The \c CodingKeys enum decl to validate.
283301
static bool validateCodingKeysEnum(const DerivedConformance &derived,
284302
llvm::SmallMapVector<Identifier, VarDecl *, 8> varDecls,
285-
TypeDecl *codingKeysTypeDecl) {
286-
auto *codingKeysDecl = validateCodingKeysType(derived, codingKeysTypeDecl);
303+
TypeDecl *codingKeysTypeDecl,
304+
DelayedNotes &delayedNotes) {
305+
auto *codingKeysDecl = validateCodingKeysType(
306+
derived, codingKeysTypeDecl, delayedNotes);
287307
if (!codingKeysDecl)
288308
return false;
289309

@@ -297,8 +317,10 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived,
297317
for (auto elt : codingKeysDecl->getAllElements()) {
298318
auto it = varDecls.find(elt->getBaseIdentifier());
299319
if (it == varDecls.end()) {
300-
elt->diagnose(diag::codable_extraneous_codingkey_case_here,
301-
elt->getBaseIdentifier());
320+
delayedNotes.push_back([=] {
321+
elt->diagnose(diag::codable_extraneous_codingkey_case_here,
322+
elt->getBaseIdentifier());
323+
});
302324
// TODO: Investigate typo-correction here; perhaps the case name was
303325
// misspelled and we can provide a fix-it.
304326
varDeclsAreValid = false;
@@ -315,8 +337,13 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived,
315337
it->second->getTypeReprOrParentPatternTypeRepr(),
316338
it->second->getType(),
317339
};
318-
it->second->diagnose(diag::codable_non_conforming_property_here,
319-
derived.getProtocolType(), typeLoc);
340+
341+
auto var = it->second;
342+
auto proto = derived.getProtocolType();
343+
delayedNotes.push_back([=] {
344+
var->diagnose(diag::codable_non_conforming_property_here,
345+
proto, typeLoc);
346+
});
320347
varDeclsAreValid = false;
321348
} else {
322349
// The property was valid. Remove it from the list.
@@ -350,16 +377,19 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived,
350377
// The var was not default initializable, and did not have an explicit
351378
// initial value.
352379
varDeclsAreValid = false;
353-
entry.second->diagnose(diag::codable_non_decoded_property_here,
354-
derived.getProtocolType(), entry.first);
380+
delayedNotes.push_back([=] {
381+
entry.second->diagnose(diag::codable_non_decoded_property_here,
382+
derived.getProtocolType(), entry.first);
383+
});
355384
}
356385
}
357386

358387
return varDeclsAreValid;
359388
}
360389

361390
static bool validateCodingKeysEnum_enum(const DerivedConformance &derived,
362-
TypeDecl *codingKeysTypeDecl) {
391+
TypeDecl *codingKeysTypeDecl,
392+
DelayedNotes &delayedNotes) {
363393
auto *enumDecl = dyn_cast<EnumDecl>(derived.Nominal);
364394
if (!enumDecl) {
365395
return false;
@@ -369,16 +399,18 @@ static bool validateCodingKeysEnum_enum(const DerivedConformance &derived,
369399
caseNames.insert(elt->getBaseIdentifier());
370400
}
371401

372-
auto *codingKeysDecl = validateCodingKeysType(derived,
373-
codingKeysTypeDecl);
402+
auto *codingKeysDecl = validateCodingKeysType(
403+
derived, codingKeysTypeDecl, delayedNotes);
374404
if (!codingKeysDecl)
375405
return false;
376406

377407
bool casesAreValid = true;
378408
for (auto *elt : codingKeysDecl->getAllElements()) {
379409
if (!caseNames.contains(elt->getBaseIdentifier())) {
380-
elt->diagnose(diag::codable_extraneous_codingkey_case_here,
381-
elt->getBaseIdentifier());
410+
delayedNotes.push_back([=] {
411+
elt->diagnose(diag::codable_extraneous_codingkey_case_here,
412+
elt->getBaseIdentifier());
413+
});
382414
casesAreValid = false;
383415
}
384416
}
@@ -388,7 +420,8 @@ static bool validateCodingKeysEnum_enum(const DerivedConformance &derived,
388420

389421
/// Looks up and validates a CodingKeys enum for the given DerivedConformance.
390422
/// If a CodingKeys enum does not exist, one will be derived.
391-
static bool validateCodingKeysEnum(const DerivedConformance &derived) {
423+
static bool validateCodingKeysEnum(const DerivedConformance &derived,
424+
DelayedNotes &delayedNotes) {
392425
auto &C = derived.Context;
393426

394427
auto codingKeysDecls =
@@ -403,13 +436,16 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived) {
403436
: codingKeysDecls.front();
404437
auto *codingKeysTypeDecl = dyn_cast<TypeDecl>(result);
405438
if (!codingKeysTypeDecl) {
406-
result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here,
407-
derived.getProtocolType());
439+
delayedNotes.push_back([=] {
440+
result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here,
441+
derived.getProtocolType());
442+
});
408443
return false;
409444
}
410445

411446
if (dyn_cast<EnumDecl>(derived.Nominal)) {
412-
return validateCodingKeysEnum_enum(derived, codingKeysTypeDecl);
447+
return validateCodingKeysEnum_enum(
448+
derived, codingKeysTypeDecl, delayedNotes);
413449
} else {
414450

415451
// Look through all var decls in the given type.
@@ -426,7 +462,8 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived) {
426462
properties[getVarNameForCoding(varDecl)] = varDecl;
427463
}
428464

429-
return validateCodingKeysEnum(derived, properties, codingKeysTypeDecl);
465+
return validateCodingKeysEnum(
466+
derived, properties, codingKeysTypeDecl, delayedNotes);
430467
}
431468
}
432469

@@ -435,7 +472,8 @@ static bool validateCodingKeysEnum(const DerivedConformance &derived) {
435472
///
436473
/// \param elementDecl The \c EnumElementDecl to validate against.
437474
static bool validateCaseCodingKeysEnum(const DerivedConformance &derived,
438-
EnumElementDecl *elementDecl) {
475+
EnumElementDecl *elementDecl,
476+
DelayedNotes &delayedNotes) {
439477
auto &C = derived.Context;
440478
auto *enumDecl = dyn_cast<EnumDecl>(derived.Nominal);
441479
if (!enumDecl) {
@@ -471,8 +509,10 @@ static bool validateCaseCodingKeysEnum(const DerivedConformance &derived,
471509

472510
auto *codingKeysTypeDecl = dyn_cast<TypeDecl>(result);
473511
if (!codingKeysTypeDecl) {
474-
result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here,
475-
derived.getProtocolType());
512+
delayedNotes.push_back([=] {
513+
result->diagnose(diag::codable_codingkeys_type_is_not_an_enum_here,
514+
derived.getProtocolType());
515+
});
476516
return false;
477517
}
478518

@@ -490,7 +530,8 @@ static bool validateCaseCodingKeysEnum(const DerivedConformance &derived,
490530
}
491531
}
492532

493-
return validateCodingKeysEnum(derived, properties, codingKeysTypeDecl);
533+
return validateCodingKeysEnum(
534+
derived, properties, codingKeysTypeDecl, delayedNotes);
494535
}
495536

496537
/// Creates a new var decl representing
@@ -1815,7 +1856,8 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) {
18151856
/// not, attempts to synthesize one for it.
18161857
///
18171858
/// \param requirement The requirement we want to synthesize.
1818-
static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
1859+
static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement,
1860+
DelayedNotes &delayedNotes) {
18191861
// Before we attempt to look up (or more importantly, synthesize) a CodingKeys
18201862
// entity on target, we need to make sure the type is otherwise valid.
18211863
//
@@ -1851,8 +1893,11 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
18511893

18521894
if (result.empty()) {
18531895
// No super initializer for us to call.
1854-
superclassDecl->diagnose(diag::decodable_no_super_init_here,
1855-
requirement->getName(), memberName);
1896+
delayedNotes.push_back([=] {
1897+
superclassDecl->diagnose(diag::decodable_no_super_init_here,
1898+
requirement->getName(), memberName);
1899+
});
1900+
18561901
return false;
18571902
} else if (result.size() > 1) {
18581903
// There are multiple results for this lookup. We'll end up producing a
@@ -1865,28 +1910,35 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
18651910
auto conformanceDC = derived.getConformanceContext();
18661911
if (!initializer->isDesignatedInit()) {
18671912
// We must call a superclass's designated initializer.
1868-
initializer->diagnose(diag::decodable_super_init_not_designated_here,
1869-
requirement->getName(), memberName);
1913+
delayedNotes.push_back([=] {
1914+
initializer->diagnose(
1915+
diag::decodable_super_init_not_designated_here,
1916+
requirement->getName(), memberName);
1917+
});
18701918
return false;
18711919
} else if (!initializer->isAccessibleFrom(conformanceDC)) {
18721920
// Cannot call an inaccessible method.
1873-
auto accessScope = initializer->getFormalAccessScope(conformanceDC);
1874-
initializer->diagnose(diag::decodable_inaccessible_super_init_here,
1875-
requirement->getName(), memberName,
1876-
accessScope.accessLevelForDiagnostics());
1921+
delayedNotes.push_back([=] {
1922+
auto accessScope = initializer->getFormalAccessScope(conformanceDC);
1923+
initializer->diagnose(diag::decodable_inaccessible_super_init_here,
1924+
requirement->getName(), memberName,
1925+
accessScope.accessLevelForDiagnostics());
1926+
});
18771927
return false;
18781928
} else if (initializer->isFailable()) {
18791929
// We can't call super.init() if it's failable, since init(from:)
18801930
// isn't failable.
1881-
initializer->diagnose(diag::decodable_super_init_is_failable_here,
1882-
requirement->getName(), memberName);
1931+
delayedNotes.push_back([=] {
1932+
initializer->diagnose(diag::decodable_super_init_is_failable_here,
1933+
requirement->getName(), memberName);
1934+
});
18831935
return false;
18841936
}
18851937
}
18861938
}
18871939
}
18881940

1889-
if (!validateCodingKeysEnum(derived)) {
1941+
if (!validateCodingKeysEnum(derived, delayedNotes)) {
18901942
return false;
18911943
}
18921944

@@ -1896,10 +1948,12 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
18961948
for (auto *elementDecl : enumDecl->getAllElements()) {
18971949
bool duplicate = false;
18981950
if (!caseNames.insert(elementDecl->getBaseIdentifier())) {
1899-
elementDecl->diagnose(diag::codable_enum_duplicate_case_name_here,
1900-
derived.getProtocolType(),
1901-
derived.Nominal->getDeclaredType(),
1902-
elementDecl->getBaseIdentifier());
1951+
delayedNotes.push_back([=] {
1952+
elementDecl->diagnose(diag::codable_enum_duplicate_case_name_here,
1953+
derived.getProtocolType(),
1954+
derived.Nominal->getDeclaredType(),
1955+
elementDecl->getBaseIdentifier());
1956+
});
19031957
allValid = false;
19041958
duplicate = true;
19051959
}
@@ -1925,17 +1979,20 @@ static bool canSynthesize(DerivedConformance &derived, ValueDecl *requirement) {
19251979
userDefinedParam = inserted.first->second;
19261980
}
19271981

1928-
userDefinedParam->diagnose(diag::codable_enum_duplicate_parameter_name_here,
1929-
derived.getProtocolType(),
1930-
derived.Nominal->getDeclaredType(),
1931-
paramIdentifier,
1932-
elementDecl->getBaseIdentifier());
1982+
delayedNotes.push_back([=] {
1983+
userDefinedParam->diagnose(diag::codable_enum_duplicate_parameter_name_here,
1984+
derived.getProtocolType(),
1985+
derived.Nominal->getDeclaredType(),
1986+
paramIdentifier,
1987+
elementDecl->getBaseIdentifier());
1988+
});
19331989
allValid = false;
19341990
}
19351991
}
19361992
}
19371993

1938-
if (!duplicate && !validateCaseCodingKeysEnum(derived, elementDecl)) {
1994+
if (!duplicate &&
1995+
!validateCaseCodingKeysEnum(derived, elementDecl, delayedNotes)) {
19391996
allValid = false;
19401997
}
19411998
}
@@ -1989,7 +2046,8 @@ ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) {
19892046

19902047
// Check other preconditions for synthesized conformance.
19912048
// This synthesizes a CodingKeys enum if possible.
1992-
if (!canSynthesize(*this, requirement)) {
2049+
DelayedNotes delayedNotes;
2050+
if (!canSynthesize(*this, requirement, delayedNotes)) {
19932051
ConformanceDecl->diagnose(diag::type_does_not_conform,
19942052
Nominal->getDeclaredType(), getProtocolType());
19952053
requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Func,
@@ -1998,6 +2056,7 @@ ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) {
19982056

19992057
return nullptr;
20002058
}
2059+
assert(delayedNotes.empty());
20012060

20022061
return deriveEncodable_encode(*this);
20032062
}
@@ -2019,7 +2078,8 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) {
20192078

20202079
// Check other preconditions for synthesized conformance.
20212080
// This synthesizes a CodingKeys enum if possible.
2022-
if (!canSynthesize(*this, requirement)) {
2081+
DelayedNotes delayedNotes;
2082+
if (!canSynthesize(*this, requirement, delayedNotes)) {
20232083
ConformanceDecl->diagnose(diag::type_does_not_conform,
20242084
Nominal->getDeclaredType(), getProtocolType());
20252085
requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Constructor,
@@ -2028,6 +2088,7 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) {
20282088

20292089
return nullptr;
20302090
}
2091+
assert(delayedNotes.empty());
20312092

20322093
return deriveDecodable_init(*this);
20332094
}

0 commit comments

Comments
 (0)