Skip to content

Commit 3df5ca6

Browse files
authored
Merge pull request swiftlang#36409 from beccadax/behave-yourself
Soften errors for invalid @objc attributes added by access notes
2 parents 00fd10e + fa058f3 commit 3df5ca6

File tree

15 files changed

+1061
-517
lines changed

15 files changed

+1061
-517
lines changed

include/swift/AST/Attr.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,15 @@ class DeclAttribute : public AttributeBase {
110110
union {
111111
uint64_t OpaqueBits;
112112

113-
SWIFT_INLINE_BITFIELD_BASE(DeclAttribute, bitmax(NumDeclAttrKindBits,8)+1+1,
113+
SWIFT_INLINE_BITFIELD_BASE(DeclAttribute, bitmax(NumDeclAttrKindBits,8)+1+1+1,
114114
Kind : bitmax(NumDeclAttrKindBits,8),
115115
// Whether this attribute was implicitly added.
116116
Implicit : 1,
117117

118-
Invalid : 1
118+
Invalid : 1,
119+
120+
/// Whether the attribute was created by an access note.
121+
AddedByAccessNote : 1
119122
);
120123

121124
SWIFT_INLINE_BITFIELD(ObjCAttr, DeclAttribute, 1+1+1,
@@ -191,6 +194,7 @@ class DeclAttribute : public AttributeBase {
191194
Bits.DeclAttribute.Kind = static_cast<unsigned>(DK);
192195
Bits.DeclAttribute.Implicit = Implicit;
193196
Bits.DeclAttribute.Invalid = false;
197+
Bits.DeclAttribute.AddedByAccessNote = false;
194198
}
195199

196200
private:
@@ -329,6 +333,18 @@ class DeclAttribute : public AttributeBase {
329333

330334
bool isValid() const { return !isInvalid(); }
331335

336+
337+
/// Determine whether this attribute was added by an access note. If it was,
338+
/// the compiler will generally recover from failures involving this attribute
339+
/// as though it is not present.
340+
bool getAddedByAccessNote() const {
341+
return Bits.DeclAttribute.AddedByAccessNote;
342+
}
343+
344+
void setAddedByAccessNote(bool accessNote = true) {
345+
Bits.DeclAttribute.AddedByAccessNote = accessNote;
346+
}
347+
332348
/// Returns the address of the next pointer field.
333349
/// Used for object deserialization.
334350
DeclAttribute **getMutableNext() {

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4830,7 +4830,7 @@ ERROR(objc_extension_not_class,none,
48304830
"'@objc' can only be applied to an extension of a class", ())
48314831

48324832
// If you change this, also change enum ObjCReason
4833-
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)}"
4833+
#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @IBOutlet|marked @IBAction|marked @IBSegueAction|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override|an implementation of an @objc requirement|marked @IBInspectable|marked @GKInspectable|in an @objc extension of a class (without @nonobjc)|marked @objc by an access note}"
48344834

48354835
WARNING(attribute_meaningless_when_nonobjc,none,
48364836
"'@%0' attribute is meaningless on a property that cannot be "

include/swift/Parse/Parser.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,10 +1822,10 @@ struct ParsedDeclName {
18221822
}
18231823

18241824
/// Form a declaration name from this parsed declaration name.
1825-
DeclName formDeclName(ASTContext &ctx) const;
1825+
DeclName formDeclName(ASTContext &ctx, bool isSubscript = false) const;
18261826

18271827
/// Form a declaration name from this parsed declaration name.
1828-
DeclNameRef formDeclNameRef(ASTContext &ctx) const;
1828+
DeclNameRef formDeclNameRef(ASTContext &ctx, bool isSubscript = false) const;
18291829
};
18301830

18311831
/// To assist debugging parser crashes, tell us the location of the
@@ -1846,14 +1846,16 @@ DeclName formDeclName(ASTContext &ctx,
18461846
StringRef baseName,
18471847
ArrayRef<StringRef> argumentLabels,
18481848
bool isFunctionName,
1849-
bool isInitializer);
1849+
bool isInitializer,
1850+
bool isSubscript = false);
18501851

18511852
/// Form a Swift declaration name referemce from its constituent parts.
18521853
DeclNameRef formDeclNameRef(ASTContext &ctx,
18531854
StringRef baseName,
18541855
ArrayRef<StringRef> argumentLabels,
18551856
bool isFunctionName,
1856-
bool isInitializer);
1857+
bool isInitializer,
1858+
bool isSubscript = false);
18571859

18581860
/// Parse a stringified Swift declaration name, e.g. "init(frame:)".
18591861
DeclName parseDeclName(ASTContext &ctx, StringRef name);

lib/AST/AccessNotes.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,7 @@ AccessNoteDeclName::AccessNoteDeclName(ASTContext &ctx, StringRef str) {
4646
else
4747
accessorKind = None;
4848

49-
name = parsedName.formDeclName(ctx);
50-
51-
// FIXME: parseDeclName() doesn't handle the special `subscript` name.
52-
// Fixing this without affecting existing uses in import-as-member will need
53-
// a bit of work. Hack around the problem for this specific caller instead.
54-
if (name.getBaseName() == ctx.getIdentifier("subscript"))
55-
name = DeclName(ctx, DeclBaseName::createSubscript(),
56-
name.getArgumentNames());
49+
name = parsedName.formDeclName(ctx, /*isSubscript=*/true);
5750
}
5851

5952
bool AccessNoteDeclName::matches(ValueDecl *VD) const {

lib/AST/Attr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,7 @@ SourceLoc ObjCAttr::getRParenLoc() const {
13321332
ObjCAttr *ObjCAttr::clone(ASTContext &context) const {
13331333
auto attr = new (context) ObjCAttr(getName(), isNameImplicit());
13341334
attr->setSwift3Inferred(isSwift3Inferred());
1335+
attr->setAddedByAccessNote(getAddedByAccessNote());
13351336
return attr;
13361337
}
13371338

lib/Parse/Parser.cpp

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,49 +1429,53 @@ ParsedDeclName swift::parseDeclName(StringRef name) {
14291429
}
14301430
} while (!parameters.empty());
14311431

1432-
// Drop the argument labels for a property accessor; they aren't used.
1433-
if (result.isPropertyAccessor())
1434-
result.ArgumentLabels.clear();
1435-
14361432
return result;
14371433
}
14381434

1439-
DeclName ParsedDeclName::formDeclName(ASTContext &ctx) const {
1440-
return formDeclNameRef(ctx).getFullName();
1435+
DeclName ParsedDeclName::formDeclName(ASTContext &ctx, bool isSubscript) const {
1436+
return formDeclNameRef(ctx, isSubscript).getFullName();
14411437
}
14421438

1443-
DeclNameRef ParsedDeclName::formDeclNameRef(ASTContext &ctx) const {
1439+
DeclNameRef ParsedDeclName::formDeclNameRef(ASTContext &ctx,
1440+
bool isSubscript) const {
14441441
return swift::formDeclNameRef(ctx, BaseName, ArgumentLabels, IsFunctionName,
1445-
/*IsInitializer=*/true);
1442+
/*IsInitializer=*/true, isSubscript);
14461443
}
14471444

14481445
DeclName swift::formDeclName(ASTContext &ctx,
14491446
StringRef baseName,
14501447
ArrayRef<StringRef> argumentLabels,
14511448
bool isFunctionName,
1452-
bool isInitializer) {
1449+
bool isInitializer,
1450+
bool isSubscript) {
14531451
return formDeclNameRef(ctx, baseName, argumentLabels, isFunctionName,
1454-
isInitializer).getFullName();
1452+
isInitializer, isSubscript).getFullName();
14551453
}
14561454

14571455
DeclNameRef swift::formDeclNameRef(ASTContext &ctx,
14581456
StringRef baseName,
14591457
ArrayRef<StringRef> argumentLabels,
14601458
bool isFunctionName,
1461-
bool isInitializer) {
1459+
bool isInitializer,
1460+
bool isSubscript) {
14621461
// We cannot import when the base name is not an identifier.
14631462
if (baseName.empty())
14641463
return DeclNameRef();
14651464
if (!Lexer::isIdentifier(baseName) && !Lexer::isOperator(baseName))
14661465
return DeclNameRef();
14671466

14681467
// Get the identifier for the base name. Special-case `init`.
1469-
DeclBaseName baseNameId = ((isInitializer && baseName == "init")
1470-
? DeclBaseName::createConstructor()
1471-
: ctx.getIdentifier(baseName));
1468+
DeclBaseName baseNameId;
1469+
if (isInitializer && baseName == "init")
1470+
baseNameId = DeclBaseName::createConstructor();
1471+
else if (isSubscript && baseName == "subscript")
1472+
baseNameId = DeclBaseName::createSubscript();
1473+
else
1474+
baseNameId = ctx.getIdentifier(baseName);
14721475

14731476
// For non-functions, just use the base name.
1474-
if (!isFunctionName) return DeclNameRef(baseNameId);
1477+
if (!isFunctionName && !baseNameId.isSubscript())
1478+
return DeclNameRef(baseNameId);
14751479

14761480
// For functions, we need to form a complete name.
14771481

lib/Sema/TypeCheckAttr.cpp

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,16 @@ using namespace swift;
4747
namespace {
4848
/// This emits a diagnostic with a fixit to remove the attribute.
4949
template<typename ...ArgTypes>
50-
void diagnoseAndRemoveAttr(DiagnosticEngine &Diags, Decl *D,
51-
DeclAttribute *attr, ArgTypes &&...Args) {
50+
InFlightDiagnostic
51+
diagnoseAndRemoveAttr(DiagnosticEngine &Diags, Decl *D, DeclAttribute *attr,
52+
ArgTypes &&...Args) {
53+
attr->setInvalid();
54+
5255
assert(!D->hasClangNode() && "Clang importer propagated a bogus attribute");
5356
if (!D->hasClangNode()) {
5457
SourceLoc loc = attr->getLocation();
5558
#ifndef NDEBUG
56-
if (!loc.isValid()) {
59+
if (!loc.isValid() && !attr->getAddedByAccessNote()) {
5760
llvm::errs() << "Attribute '";
5861
attr->print(llvm::errs());
5962
llvm::errs() << "' has invalid location, failed to diagnose!\n";
@@ -64,12 +67,11 @@ namespace {
6467
loc = D->getLoc();
6568
}
6669
if (loc.isValid()) {
67-
Diags.diagnose(loc, std::forward<ArgTypes>(Args)...)
68-
.fixItRemove(attr->getRangeWithAt());
70+
return std::move(Diags.diagnose(loc, std::forward<ArgTypes>(Args)...)
71+
.fixItRemove(attr->getRangeWithAt()));
6972
}
7073
}
71-
72-
attr->setInvalid();
74+
return InFlightDiagnostic();
7375
}
7476

7577
/// This visits each attribute on a decl. The visitor should return true if
@@ -83,9 +85,10 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
8385

8486
/// This emits a diagnostic with a fixit to remove the attribute.
8587
template<typename ...ArgTypes>
86-
void diagnoseAndRemoveAttr(DeclAttribute *attr, ArgTypes &&...Args) {
87-
::diagnoseAndRemoveAttr(Ctx.Diags, D, attr,
88-
std::forward<ArgTypes>(Args)...);
88+
InFlightDiagnostic diagnoseAndRemoveAttr(DeclAttribute *attr,
89+
ArgTypes &&...Args) {
90+
return ::diagnoseAndRemoveAttr(Ctx.Diags, D, attr,
91+
std::forward<ArgTypes>(Args)...);
8992
}
9093

9194
template <typename... ArgTypes>
@@ -936,7 +939,8 @@ static bool checkObjCDeclContext(Decl *D) {
936939
return false;
937940
}
938941

939-
static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl) {
942+
static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl,
943+
DiagnosticBehavior behavior) {
940944
auto *SF = decl->getDeclContext()->getParentSourceFile();
941945
assert(SF);
942946

@@ -948,7 +952,8 @@ static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl) {
948952

949953
if (!ctx.LangOpts.EnableObjCInterop) {
950954
ctx.Diags.diagnose(attr->getLocation(), diag::objc_interop_disabled)
951-
.fixItRemove(attr->getRangeWithAt());
955+
.fixItRemove(attr->getRangeWithAt())
956+
.limitBehavior(behavior);
952957
return;
953958
}
954959

@@ -968,10 +973,14 @@ static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl) {
968973
ctx.Diags.diagnose(attr->getLocation(),
969974
diag::attr_used_without_required_module, attr,
970975
ctx.Id_Foundation)
971-
.highlight(attr->getRangeWithAt());
976+
.highlight(attr->getRangeWithAt())
977+
.limitBehavior(behavior);
972978
}
973979

974980
void AttributeChecker::visitObjCAttr(ObjCAttr *attr) {
981+
auto reason = objCReasonForObjCAttr(attr);
982+
auto behavior = behaviorLimitForObjCReason(reason, Ctx);
983+
975984
// Only certain decls can be ObjC.
976985
Optional<Diag<>> error;
977986
if (isa<ClassDecl>(D) ||
@@ -1007,7 +1016,7 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) {
10071016
}
10081017

10091018
if (error) {
1010-
diagnoseAndRemoveAttr(attr, *error);
1019+
diagnoseAndRemoveAttr(attr, *error).limitBehavior(behavior);
10111020
return;
10121021
}
10131022

@@ -1021,21 +1030,32 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) {
10211030
// names. Complain and recover by chopping off everything
10221031
// after the first name.
10231032
if (objcName->getNumArgs() > 0) {
1024-
SourceLoc firstNameLoc = attr->getNameLocs().front();
1025-
SourceLoc afterFirstNameLoc =
1026-
Lexer::getLocForEndOfToken(Ctx.SourceMgr, firstNameLoc);
1033+
SourceLoc firstNameLoc, afterFirstNameLoc;
1034+
if (!attr->getNameLocs().empty()) {
1035+
firstNameLoc = attr->getNameLocs().front();
1036+
afterFirstNameLoc =
1037+
Lexer::getLocForEndOfToken(Ctx.SourceMgr, firstNameLoc);
1038+
}
1039+
else {
1040+
firstNameLoc = D->getLoc();
1041+
}
10271042
diagnose(firstNameLoc, diag::objc_name_req_nullary,
10281043
D->getDescriptiveKind())
1029-
.fixItRemoveChars(afterFirstNameLoc, attr->getRParenLoc());
1044+
.fixItRemoveChars(afterFirstNameLoc, attr->getRParenLoc())
1045+
.limitBehavior(behavior);
10301046
const_cast<ObjCAttr *>(attr)->setName(
10311047
ObjCSelector(Ctx, 0, objcName->getSelectorPieces()[0]),
10321048
/*implicit=*/false);
10331049
}
10341050
} else if (isa<SubscriptDecl>(D) || isa<DestructorDecl>(D)) {
1035-
diagnose(attr->getLParenLoc(),
1051+
SourceLoc diagLoc = attr->getLParenLoc();
1052+
if (diagLoc.isInvalid())
1053+
diagLoc = D->getLoc();
1054+
diagnose(diagLoc,
10361055
isa<SubscriptDecl>(D)
10371056
? diag::objc_name_subscript
1038-
: diag::objc_name_deinit);
1057+
: diag::objc_name_deinit)
1058+
.limitBehavior(behavior);
10391059
const_cast<ObjCAttr *>(attr)->clearName();
10401060
} else {
10411061
auto func = cast<AbstractFunctionDecl>(D);
@@ -1063,26 +1083,31 @@ void AttributeChecker::visitObjCAttr(ObjCAttr *attr) {
10631083

10641084
unsigned numArgumentNames = objcName->getNumArgs();
10651085
if (numArgumentNames != numParameters) {
1066-
diagnose(attr->getNameLocs().front(),
1086+
SourceLoc firstNameLoc = func->getLoc();
1087+
if (!attr->getNameLocs().empty())
1088+
firstNameLoc = attr->getNameLocs().front();
1089+
diagnose(firstNameLoc,
10671090
diag::objc_name_func_mismatch,
10681091
isa<FuncDecl>(func),
10691092
numArgumentNames,
10701093
numArgumentNames != 1,
10711094
numParameters,
10721095
numParameters != 1,
1073-
func->hasThrows());
1096+
func->hasThrows())
1097+
.limitBehavior(behavior);
10741098
D->getAttrs().add(
10751099
ObjCAttr::createUnnamed(Ctx, attr->AtLoc, attr->Range.Start));
10761100
D->getAttrs().removeAttribute(attr);
10771101
}
10781102
}
10791103
} else if (isa<EnumElementDecl>(D)) {
10801104
// Enum elements require names.
1081-
diagnoseAndRemoveAttr(attr, diag::objc_enum_case_req_name);
1105+
diagnoseAndRemoveAttr(attr, diag::objc_enum_case_req_name)
1106+
.limitBehavior(behavior);
10821107
}
10831108

10841109
// Diagnose an @objc attribute used without importing Foundation.
1085-
diagnoseObjCAttrWithoutFoundation(attr, D);
1110+
diagnoseObjCAttrWithoutFoundation(attr, D, behavior);
10861111
}
10871112

10881113
void AttributeChecker::visitNonObjCAttr(NonObjCAttr *attr) {
@@ -1130,6 +1155,9 @@ void AttributeChecker::visitOptionalAttr(OptionalAttr *attr) {
11301155
}
11311156

11321157
void TypeChecker::checkDeclAttributes(Decl *D) {
1158+
if (auto VD = dyn_cast<ValueDecl>(D))
1159+
TypeChecker::applyAccessNote(VD);
1160+
11331161
AttributeChecker Checker(D);
11341162
// We need to check all OriginallyDefinedInAttr relative to each other, so
11351163
// collect them and check in batch later.
@@ -1174,13 +1202,20 @@ void TypeChecker::checkDeclAttributes(Decl *D) {
11741202
default: break;
11751203
}
11761204

1205+
DiagnosticBehavior behavior = attr->getAddedByAccessNote()
1206+
? DiagnosticBehavior::Remark
1207+
: DiagnosticBehavior::Unspecified;
1208+
11771209
if (!OnlyKind.empty())
11781210
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind,
1179-
attr, OnlyKind);
1211+
attr, OnlyKind)
1212+
.limitBehavior(behavior);
11801213
else if (attr->isDeclModifier())
1181-
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
1214+
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr)
1215+
.limitBehavior(behavior);
11821216
else
1183-
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr);
1217+
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr)
1218+
.limitBehavior(behavior);
11841219
}
11851220
Checker.checkOriginalDefinedInAttrs(D, ODIAttrs);
11861221
}

0 commit comments

Comments
 (0)