Skip to content

Commit 769ea4a

Browse files
committed
Share diagnoseAndRemoveAttr()
Merge together several helpers and code patterns for “diagnose/fix-it/invalidate bad attribute” into helper functions in TypeChecker.h. This requires minor test changes in some places where we’re testing ObjC interop without importing Foundation; it’s otherwise NFC.
1 parent d4ea93c commit 769ea4a

File tree

7 files changed

+101
-110
lines changed

7 files changed

+101
-110
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,10 +1587,7 @@ Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
15871587
}
15881588

15891589
if (attr) {
1590-
ctx.Diags.diagnose(
1591-
attr->getLocation(), diag::result_builder_remove_attr)
1592-
.fixItRemove(attr->getRangeWithAt());
1593-
attr->setInvalid();
1590+
diagnoseAndRemoveAttr(func, attr, diag::result_builder_remove_attr);
15941591
}
15951592

15961593
// Note that one can remove all of the return statements.

lib/Sema/TypeCheckAttr.cpp

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,6 @@
4545
using namespace swift;
4646

4747
namespace {
48-
/// This emits a diagnostic with a fixit to remove the attribute.
49-
template<typename ...ArgTypes>
50-
InFlightDiagnostic
51-
diagnoseAndRemoveAttr(DiagnosticEngine &Diags, Decl *D, DeclAttribute *attr,
52-
ArgTypes &&...Args) {
53-
attr->setInvalid();
54-
55-
assert(!D->hasClangNode() && "Clang importer propagated a bogus attribute");
56-
if (!D->hasClangNode()) {
57-
SourceLoc loc = attr->getLocation();
58-
#ifndef NDEBUG
59-
if (!loc.isValid() && !attr->getAddedByAccessNote()) {
60-
llvm::errs() << "Attribute '";
61-
attr->print(llvm::errs());
62-
llvm::errs() << "' has invalid location, failed to diagnose!\n";
63-
assert(false && "Diagnosing attribute with invalid location");
64-
}
65-
#endif
66-
if (loc.isInvalid()) {
67-
loc = D->getLoc();
68-
}
69-
if (loc.isValid()) {
70-
return std::move(Diags.diagnose(loc, std::forward<ArgTypes>(Args)...)
71-
.fixItRemove(attr->getRangeWithAt()));
72-
}
73-
}
74-
return InFlightDiagnostic();
75-
}
76-
7748
/// This visits each attribute on a decl. The visitor should return true if
7849
/// the attribute is invalid and should be marked as such.
7950
class AttributeChecker : public AttributeVisitor<AttributeChecker> {
@@ -87,8 +58,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
8758
template<typename ...ArgTypes>
8859
InFlightDiagnostic diagnoseAndRemoveAttr(DeclAttribute *attr,
8960
ArgTypes &&...Args) {
90-
return ::diagnoseAndRemoveAttr(Ctx.Diags, D, attr,
91-
std::forward<ArgTypes>(Args)...);
61+
return swift::diagnoseAndRemoveAttr(D, attr,
62+
std::forward<ArgTypes>(Args)...);
9263
}
9364

9465
template <typename... ArgTypes>
@@ -955,8 +926,7 @@ static void diagnoseObjCAttrWithoutFoundation(ObjCAttr *attr, Decl *decl,
955926
auto &ctx = SF->getASTContext();
956927

957928
if (!ctx.LangOpts.EnableObjCInterop) {
958-
ctx.Diags.diagnose(attr->getLocation(), diag::objc_interop_disabled)
959-
.fixItRemove(attr->getRangeWithAt())
929+
diagnoseAndRemoveAttr(decl, attr, diag::objc_interop_disabled)
960930
.limitBehavior(behavior);
961931
return;
962932
}
@@ -4186,8 +4156,6 @@ resolveDifferentiableAttrOriginalFunction(DifferentiableAttr *attr) {
41864156
auto *D = attr->getOriginalDeclaration();
41874157
assert(D &&
41884158
"Original declaration should be resolved by parsing/deserialization");
4189-
auto &ctx = D->getASTContext();
4190-
auto &diags = ctx.Diags;
41914159
auto *original = dyn_cast<AbstractFunctionDecl>(D);
41924160
if (auto *asd = dyn_cast<AbstractStorageDecl>(D)) {
41934161
// If `@differentiable` attribute is declared directly on a
@@ -4211,7 +4179,7 @@ resolveDifferentiableAttrOriginalFunction(DifferentiableAttr *attr) {
42114179
original = nullptr;
42124180
// Diagnose if original `AbstractFunctionDecl` could not be resolved.
42134181
if (!original) {
4214-
diagnoseAndRemoveAttr(diags, D, attr, diag::invalid_decl_attribute, attr);
4182+
diagnoseAndRemoveAttr(D, attr, diag::invalid_decl_attribute, attr);
42154183
attr->setInvalid();
42164184
return nullptr;
42174185
}
@@ -4541,8 +4509,7 @@ IndexSubset *DifferentiableAttributeTypeCheckRequest::evaluate(
45414509
{getterDecl, resolvedDiffParamIndices}, newAttr);
45424510
// Reject duplicate `@differentiable` attributes.
45434511
if (!insertion.second) {
4544-
diagnoseAndRemoveAttr(diags, D, attr,
4545-
diag::differentiable_attr_duplicate);
4512+
diagnoseAndRemoveAttr(D, attr, diag::differentiable_attr_duplicate);
45464513
diags.diagnose(insertion.first->getSecond()->getLocation(),
45474514
diag::differentiable_attr_duplicate_note);
45484515
return nullptr;
@@ -4558,7 +4525,7 @@ IndexSubset *DifferentiableAttributeTypeCheckRequest::evaluate(
45584525
auto insertion =
45594526
ctx.DifferentiableAttrs.try_emplace({D, resolvedDiffParamIndices}, attr);
45604527
if (!insertion.second && insertion.first->getSecond() != attr) {
4561-
diagnoseAndRemoveAttr(diags, D, attr, diag::differentiable_attr_duplicate);
4528+
diagnoseAndRemoveAttr(D, attr, diag::differentiable_attr_duplicate);
45624529
diags.diagnose(insertion.first->getSecond()->getLocation(),
45634530
diag::differentiable_attr_duplicate_note);
45644531
return nullptr;

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,10 +1160,6 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
11601160
auto reason = objCReasonForObjCAttr(attr);
11611161
auto behavior = behaviorLimitForObjCReason(reason, ctx);
11621162

1163-
SourceLoc attrLoc = attr->getLocation();
1164-
if (attrLoc.isInvalid())
1165-
attrLoc = CD->getLoc();
1166-
11671163
if (ancestry.contains(AncestryFlags::Generic)) {
11681164
if (attr->hasName() && !CD->isGenericContext()) {
11691165
// @objc with a name on a non-generic subclass of a generic class is
@@ -1173,8 +1169,7 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
11731169
return None;
11741170
}
11751171

1176-
ctx.Diags.diagnose(attrLoc, diag::objc_for_generic_class)
1177-
.fixItRemove(attr->getRangeWithAt())
1172+
swift::diagnoseAndRemoveAttr(CD, attr, diag::objc_for_generic_class)
11781173
.limitBehavior(behavior);
11791174
}
11801175

@@ -1194,12 +1189,11 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
11941189
auto platform = prettyPlatformString(targetPlatform(ctx.LangOpts));
11951190
auto range = getMinOSVersionForClassStubs(target);
11961191
auto *ancestor = getResilientAncestor(CD->getParentModule(), CD);
1197-
ctx.Diags.diagnose(attrLoc,
1198-
diag::objc_for_resilient_class,
1199-
ancestor->getName(),
1200-
platform,
1201-
range.getLowerEndpoint())
1202-
.fixItRemove(attr->getRangeWithAt())
1192+
swift::diagnoseAndRemoveAttr(CD, attr,
1193+
diag::objc_for_resilient_class,
1194+
ancestor->getName(),
1195+
platform,
1196+
range.getLowerEndpoint())
12031197
.limitBehavior(behavior);
12041198
}
12051199

@@ -1208,9 +1202,8 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
12081202
if (ancestry.contains(AncestryFlags::ObjC) &&
12091203
!ancestry.contains(AncestryFlags::ClangImported)) {
12101204
if (ctx.LangOpts.EnableObjCAttrRequiresFoundation) {
1211-
ctx.Diags.diagnose(attrLoc,
1212-
diag::invalid_objc_swift_rooted_class)
1213-
.fixItRemove(attr->getRangeWithAt())
1205+
swift::diagnoseAndRemoveAttr(CD, attr,
1206+
diag::invalid_objc_swift_rooted_class)
12141207
.limitBehavior(behavior);
12151208
// If the user has not spelled out a superclass, offer to insert
12161209
// 'NSObject'. We could also offer to replace the existing superclass,
@@ -1229,8 +1222,7 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
12291222
}
12301223

12311224
if (!ctx.LangOpts.EnableObjCInterop) {
1232-
ctx.Diags.diagnose(attrLoc, diag::objc_interop_disabled)
1233-
.fixItRemove(attr->getRangeWithAt())
1225+
swift::diagnoseAndRemoveAttr(CD, attr, diag::objc_interop_disabled)
12341226
.limitBehavior(behavior);
12351227
}
12361228
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2992,46 +2992,37 @@ static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage,
29922992
}
29932993
}
29942994

2995-
/// This emits a diagnostic with a fixit to remove the attribute.
2996-
template<typename ...ArgTypes>
2997-
void diagnoseAndRemoveAttr(Decl *D, DeclAttribute *attr,
2998-
ArgTypes &&...Args) {
2999-
auto &ctx = D->getASTContext();
3000-
ctx.Diags.diagnose(attr->getLocation(), std::forward<ArgTypes>(Args)...)
3001-
.fixItRemove(attr->getRangeWithAt());
3002-
}
3003-
30042995
static void finishLazyVariableImplInfo(VarDecl *var,
30052996
StorageImplInfo &info) {
30062997
auto *attr = var->getAttrs().getAttribute<LazyAttr>();
30072998

30082999
// It cannot currently be used on let's since we don't have a mutability model
30093000
// that supports it.
30103001
if (var->isLet())
3011-
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_let);
3002+
diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_not_on_let);
30123003

30133004
// lazy must have an initializer.
30143005
if (!var->getParentInitializer())
3015-
diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_initializer);
3006+
diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_requires_initializer);
30163007

30173008
bool invalid = false;
30183009

30193010
if (isa<ProtocolDecl>(var->getDeclContext())) {
3020-
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_in_protocol);
3011+
diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_not_in_protocol);
30213012
invalid = true;
30223013
}
30233014

30243015
// Lazy properties must be written as stored properties in the source.
30253016
if (info.getReadImpl() != ReadImplKind::Stored &&
30263017
(info.getWriteImpl() != WriteImplKind::Stored &&
30273018
info.getWriteImpl() != WriteImplKind::StoredWithObservers)) {
3028-
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_computed);
3019+
diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_not_on_computed);
30293020
invalid = true;
30303021
}
30313022

30323023
// The pattern binding must only bind a single variable.
30333024
if (!var->getParentPatternBinding()->getSingleVar())
3034-
diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_single_var);
3025+
diagnoseAttrWithRemovalFixIt(var, attr, diag::lazy_requires_single_var);
30353026

30363027
if (!invalid)
30373028
info = StorageImplInfo::getMutableComputed();
@@ -3083,7 +3074,7 @@ static void finishNSManagedImplInfo(VarDecl *var,
30833074
auto *attr = var->getAttrs().getAttribute<NSManagedAttr>();
30843075

30853076
if (var->isLet())
3086-
diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_let_property);
3077+
diagnoseAttrWithRemovalFixIt(var, attr, diag::attr_NSManaged_let_property);
30873078

30883079
SourceFile *parentFile = var->getDeclContext()->getParentSourceFile();
30893080

@@ -3095,7 +3086,7 @@ static void finishNSManagedImplInfo(VarDecl *var,
30953086
if (parentFile && parentFile->Kind == SourceFileKind::Interface)
30963087
return;
30973088

3098-
diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_not_stored, kind);
3089+
diagnoseAttrWithRemovalFixIt(var, attr, diag::attr_NSManaged_not_stored, kind);
30993090
};
31003091

31013092
// @NSManaged properties must be written as stored.

lib/Sema/TypeChecker.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838

3939
namespace swift {
4040

41+
class Decl;
42+
class DeclAttribute;
43+
class DiagnosticEngine;
4144
class ExportContext;
4245
class GenericSignatureBuilder;
4346
class NominalTypeDecl;
@@ -1317,6 +1320,45 @@ void bindSwitchCasePatternVars(DeclContext *dc, CaseStmt *stmt);
13171320
AnyFunctionType *applyGlobalActorType(
13181321
AnyFunctionType *fnType, ValueDecl *funcOrEnum, DeclContext *dc);
13191322

1323+
/// Diagnose an error concerning an incorrect attribute and emit a fix-it
1324+
/// offering to remove it.
1325+
template<typename ...ArgTypes>
1326+
InFlightDiagnostic
1327+
diagnoseAttrWithRemovalFixIt(const Decl *D, const DeclAttribute *attr,
1328+
ArgTypes &&...Args) {
1329+
assert(D);
1330+
1331+
if (D->hasClangNode() && (!attr || !attr->getAddedByAccessNote())) {
1332+
assert(false && "Clang importer propagated a bogus attribute");
1333+
return InFlightDiagnostic();
1334+
}
1335+
1336+
auto &Diags = D->getASTContext().Diags;
1337+
1338+
if (!attr || !attr->getLocation().isValid())
1339+
return Diags.diagnose(D, std::forward<ArgTypes>(Args)...);
1340+
1341+
return std::move(Diags.diagnose(attr->getLocation(),
1342+
std::forward<ArgTypes>(Args)...)
1343+
.fixItRemove(attr->getRangeWithAt()));
1344+
}
1345+
1346+
/// Diagnose an error concerning an incorrect attribute, emit a fix-it offering
1347+
/// to remove it, and mark it invalid so that it will be ignored by other parts
1348+
/// of the compiler.
1349+
template<typename ...ArgTypes>
1350+
InFlightDiagnostic
1351+
diagnoseAndRemoveAttr(const Decl *D, const DeclAttribute *attr,
1352+
ArgTypes &&...Args) {
1353+
if (attr)
1354+
// FIXME: Due to problems with the design of DeclAttributes::getAttribute(),
1355+
// many callers try to pass us const DeclAttributes. This is a hacky
1356+
// workaround.
1357+
const_cast<DeclAttribute *>(attr)->setInvalid();
1358+
1359+
return diagnoseAttrWithRemovalFixIt(D, attr, std::forward<ArgTypes>(Args)...);
1360+
}
1361+
13201362
} // end namespace swift
13211363

13221364
#endif

test/Index/kinds_objc.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// RUN: %target-swift-ide-test -print-indexed-symbols -source-filename %s | %FileCheck %s
22
// REQUIRES: objc_interop
33

4+
import Foundation
5+
46
@objc class TargetForIBAction {}
57
// CHECK: [[@LINE-1]]:13 | class/Swift | TargetForIBAction | [[TargetForIBAction_USR:.*]] | Def |
68
@objc class TargetForIBSegueAction {}

0 commit comments

Comments
 (0)