Skip to content

Commit a6a9dfe

Browse files
committed
Add option to control access note diagnostics
This will allow teams writing access notes to use -Raccess-note=all-validate to check that their access notes are correct, or teams working around problems to use -Raccess-note=failures or -Raccess-note=none to suppress diagnostics.
1 parent a414729 commit a6a9dfe

File tree

9 files changed

+117
-16
lines changed

9 files changed

+117
-16
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5873,16 +5873,18 @@ REMARK(attr_objc_name_changed_by_access_note, none,
58735873
NOTE(fixit_attr_objc_name_changed_by_access_note, none,
58745874
"change '@objc' name in source code explicitly to silence this warning",
58755875
())
5876-
REMARK(attr_objc_name_conflicts_with_access_note, none,
5877-
"access note for %0 changes the '@objc' name of this %1 to %2, but "
5878-
"source code specifies %3; the access note will be ignored",
5879-
(StringRef, DescriptiveDeclKind, ObjCSelector, ObjCSelector))
5880-
5881-
// Used on invalid @objc diagnostics
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))
5876+
5877+
// Bad access note diagnostics. These are usually emitted as remarks, but
5878+
// '-Raccess-note=all-validate' emits them as errors.
5879+
5880+
ERROR(attr_objc_name_conflicts_with_access_note, none,
5881+
"access note for %0 changes the '@objc' name of this %1 to %2, but "
5882+
"source code specifies %3; the access note will be ignored",
5883+
(StringRef, DescriptiveDeclKind, ObjCSelector, ObjCSelector))
5884+
ERROR(wrap_invalid_attr_added_by_access_note, none,
5885+
"access note for %1 failed to add invalid "
5886+
"%select{attribute|modifier}2 '%3': %0",
5887+
(DiagnosticInfo *, StringRef, bool, StringRef))
58865888

58875889
#define UNDEFINE_DIAGNOSTIC_MACROS
58885890
#include "DefineDiagnosticMacros.h"

include/swift/Basic/LangOptions.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636

3737
namespace swift {
3838

39+
enum class DiagnosticBehavior : uint8_t;
40+
3941
/// Kind of implicit platform conditions.
4042
enum class PlatformConditionKind {
4143
#define PLATFORM_CONDITION(LABEL, IDENTIFIER) LABEL,
@@ -69,6 +71,13 @@ namespace swift {
6971
Other
7072
};
7173

74+
enum class AccessNoteDiagnosticBehavior : uint8_t {
75+
Ignore,
76+
RemarkOnFailure,
77+
RemarkOnFailureOrSuccess,
78+
ErrorOnFailureRemarkOnSuccess
79+
};
80+
7281
/// A collection of options that affect the language dialect and
7382
/// provide compiler debugging facilities.
7483
class LangOptions final {
@@ -339,6 +348,17 @@ namespace swift {
339348
std::shared_ptr<llvm::Regex> OptimizationRemarkPassedPattern;
340349
std::shared_ptr<llvm::Regex> OptimizationRemarkMissedPattern;
341350

351+
/// How should we emit diagnostics about access notes?
352+
AccessNoteDiagnosticBehavior AccessNoteBehavior =
353+
AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess;
354+
355+
DiagnosticBehavior getAccessNoteFailureLimit() const;
356+
357+
bool shouldRemarkOnAccessNoteSuccess() const {
358+
return AccessNoteBehavior >=
359+
AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess;
360+
}
361+
342362
/// Whether collect tokens during parsing for syntax coloring.
343363
bool CollectParsedToken = false;
344364

include/swift/Option/FrontendOptions.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ def enable_infer_public_concurrent_value : Flag<["-"], "enable-infer-public-send
196196
def disable_infer_public_concurrent_value : Flag<["-"], "disable-infer-public-sendable">,
197197
HelpText<"Disable inference of Sendable conformances for public structs and enums">;
198198

199+
def Raccess_note : Separate<["-"], "Raccess-note">,
200+
MetaVarName<"none|failures|all|all-validate">,
201+
HelpText<"Control access note remarks (default: all)">;
202+
def Raccess_note_EQ : Joined<["-"], "Raccess-note=">,
203+
Alias<Raccess_note>;
204+
199205
} // end let Flags = [FrontendOption, NoDriverOption]
200206

201207
def debug_crash_Group : OptionGroup<"<automatic crashing options>">;

lib/Basic/LangOptions.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "swift/Basic/LangOptions.h"
19+
#include "swift/AST/DiagnosticEngine.h"
1920
#include "swift/Basic/Feature.h"
2021
#include "swift/Basic/Platform.h"
2122
#include "swift/Basic/Range.h"
@@ -391,3 +392,17 @@ llvm::StringRef swift::getFeatureName(Feature feature) {
391392
#include "swift/Basic/Features.def"
392393
}
393394
}
395+
396+
DiagnosticBehavior LangOptions::getAccessNoteFailureLimit() const {
397+
switch (AccessNoteBehavior) {
398+
case AccessNoteDiagnosticBehavior::Ignore:
399+
return DiagnosticBehavior::Ignore;
400+
401+
case AccessNoteDiagnosticBehavior::RemarkOnFailure:
402+
case AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess:
403+
return DiagnosticBehavior::Remark;
404+
405+
case AccessNoteDiagnosticBehavior::ErrorOnFailureRemarkOnSuccess:
406+
return DiagnosticBehavior::Error;
407+
}
408+
}

lib/Frontend/CompilerInvocation.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,21 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
609609
Opts.OptimizationRemarkMissedPattern =
610610
generateOptimizationRemarkRegex(Diags, Args, A);
611611

612+
if (Arg *A = Args.getLastArg(OPT_Raccess_note)) {
613+
auto value = llvm::StringSwitch<Optional<AccessNoteDiagnosticBehavior>>(A->getValue())
614+
.Case("none", AccessNoteDiagnosticBehavior::Ignore)
615+
.Case("failures", AccessNoteDiagnosticBehavior::RemarkOnFailure)
616+
.Case("all", AccessNoteDiagnosticBehavior::RemarkOnFailureOrSuccess)
617+
.Case("all-validate", AccessNoteDiagnosticBehavior::ErrorOnFailureRemarkOnSuccess)
618+
.Default(None);
619+
620+
if (value)
621+
Opts.AccessNoteBehavior = *value;
622+
else
623+
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
624+
A->getAsString(Args), A->getValue());
625+
}
626+
612627
Opts.EnableConcisePoundFile =
613628
Args.hasArg(OPT_enable_experimental_concise_pound_file);
614629
Opts.EnableFuzzyForwardScanTrailingClosureMatching =

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ swift::behaviorLimitForObjCReason(ObjCReason reason, ASTContext &ctx) {
5555
return DiagnosticBehavior::Ignore;
5656

5757
case ObjCReason::ExplicitlyObjCByAccessNote:
58-
return DiagnosticBehavior::Remark;
58+
return ctx.LangOpts.getAccessNoteFailureLimit();
5959

6060
case ObjCReason::MemberOfObjCSubclass:
6161
case ObjCReason::MemberOfObjCMembersClass:

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,9 +1456,10 @@ static void addOrRemoveAttr(ValueDecl *VD, const AccessNotesFile &notes,
14561456
SF->AttrsAddedByAccessNotes.emplace_back(VD, attr);
14571457
} else {
14581458
VD->getAttrs().removeAttribute(attr);
1459-
diagnoseChangeByAccessNote(diag::attr_removed_by_access_note,
1460-
diag::fixit_attr_removed_by_access_note)
1461-
.fixItRemove(attr->getRangeWithAt());
1459+
if (VD->getASTContext().LangOpts.shouldRemarkOnAccessNoteSuccess())
1460+
diagnoseChangeByAccessNote(diag::attr_removed_by_access_note,
1461+
diag::fixit_attr_removed_by_access_note)
1462+
.fixItRemove(attr->getRangeWithAt());
14621463
}
14631464
}
14641465

@@ -1468,9 +1469,11 @@ swift::softenIfAccessNote(const Decl *D, const DeclAttribute *attr,
14681469
if (!attr || !attr->getAddedByAccessNote())
14691470
return std::move(diag);
14701471

1472+
auto behavior = D->getASTContext().LangOpts.getAccessNoteFailureLimit();
14711473
return std::move(diag.wrapIn(diag::wrap_invalid_attr_added_by_access_note,
14721474
D->getModuleContext()->getAccessNotes().Reason,
1473-
attr->isDeclModifier(), attr->getAttrName()));
1475+
attr->isDeclModifier(), attr->getAttrName())
1476+
.limitBehavior(behavior));
14741477
}
14751478

14761479
static void applyAccessNote(ValueDecl *VD, const AccessNote &note,
@@ -1491,6 +1494,10 @@ static void applyAccessNote(ValueDecl *VD, const AccessNote &note,
14911494

14921495
if (!attr->hasName()) {
14931496
attr->setName(*note.ObjCName, true);
1497+
1498+
if (!ctx.LangOpts.shouldRemarkOnAccessNoteSuccess())
1499+
return;
1500+
14941501
diagnoseAtAttrOrDecl(attr, VD,
14951502
diag::attr_objc_name_changed_by_access_note,
14961503
notes.Reason, VD->getDescriptiveKind(),
@@ -1512,10 +1519,12 @@ static void applyAccessNote(ValueDecl *VD, const AccessNote &note,
15121519
note.fixItInsertAfter(attr->getLocation(), newNameString);
15131520
}
15141521
else if (attr->getName() != *note.ObjCName) {
1522+
auto behavior = ctx.LangOpts.getAccessNoteFailureLimit();
15151523
diagnoseAtAttrOrDecl(attr, VD,
15161524
diag::attr_objc_name_conflicts_with_access_note,
15171525
notes.Reason, VD->getDescriptiveKind(),
1518-
*note.ObjCName, *attr->getName());
1526+
*note.ObjCName, *attr->getName())
1527+
.limitBehavior(behavior);
15191528
}
15201529
}
15211530
}
@@ -1526,6 +1535,9 @@ void TypeChecker::applyAccessNote(ValueDecl *VD) {
15261535
}
15271536

15281537
void swift::diagnoseAttrsAddedByAccessNote(SourceFile &SF) {
1538+
if (!SF.getASTContext().LangOpts.shouldRemarkOnAccessNoteSuccess())
1539+
return;
1540+
15291541
for (auto declAndAttr : SF.AttrsAddedByAccessNotes) {
15301542
auto VD = std::get<0>(declAndAttr);
15311543
auto attr = std::get<1>(declAndAttr);

test/Sema/Inputs/extra.accessnotes

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ Notes:
55
- Name: 'fn()'
66
CorinthianLeather: 'rich'
77
# expected-remark@-1 {{ignored invalid content in access notes file: unknown key 'CorinthianLeather'}}
8+
9+
# These notes should apply, or attempt to apply, despite the other problems.
10+
- Name: 'Extant.good(_:)'
11+
ObjC: true
12+
- Name: 'Extant.bad(_:)'
13+
ObjC: true

test/Sema/access-notes-invalid.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,31 @@
55

66
// RUN: %target-typecheck-verify-swift -access-notes-path %S/Inputs/extra.accessnotes -verify-additional-file %S/Inputs/extra.accessnotes
77

8+
// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=none 2>&1 | %FileCheck --check-prefixes=GOOD-IGNORE,BAD-IGNORE %s
9+
// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=failures 2>&1 | %FileCheck --check-prefixes=GOOD-IGNORE,BAD-REMARK %s
10+
// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=all 2>&1 | %FileCheck --check-prefixes=GOOD-REMARK,BAD-REMARK %s
11+
// RUN: not %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes -Raccess-note=all-validate 2>&1 | %FileCheck --check-prefixes=GOOD-REMARK,BAD-ERROR %s
12+
13+
// Default should be 'all'.
14+
// RUN: %target-swift-frontend -typecheck -primary-file %/s -access-notes-path %/S/Inputs/extra.accessnotes 2>&1 | %FileCheck --check-prefixes=GOOD-REMARK,BAD-REMARK %s
15+
16+
class Extant {
17+
func good(_: Int) {} // expected-remark * {{}} expected-note * {{}}
18+
// GOOD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-1]]:{{[0-9]+}}: remark: access note for Access notes containing future, unknown syntax adds attribute 'objc' to this instance method
19+
// GOOD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-2]]:{{[0-9]+}}: note: add attribute explicitly to silence this warning
20+
// GOOD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-3]]:{{[0-9]+}}: remark: access note for Access notes containing future, unknown syntax adds attribute 'objc' to this instance method
21+
// GOOD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-4]]:{{[0-9]+}}: note: add attribute explicitly to silence this warning
22+
23+
func bad(_: Int?) {} // expected-remark * {{}}
24+
// BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-1]]:{{[0-9]+}}: remark: access note for Access notes containing future, unknown syntax failed to add invalid attribute 'objc': method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C
25+
// BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-2]]:{{[0-9]+}}: error: access note for Access notes containing future, unknown syntax failed to add invalid attribute 'objc': method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C
26+
// BAD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-3]]:{{[0-9]+}}: remark: access note for Access notes containing future, unknown syntax failed to add invalid attribute 'objc': method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C
27+
// BAD-ERROR-DAG: access-notes-invalid.swift:[[@LINE-4]]:{{[0-9]+}}: error: access note for Access notes containing future, unknown syntax failed to add invalid attribute 'objc': method cannot be marked @objc by an access note because the type of the parameter cannot be represented in Objective-C
28+
}
29+
830
// FIXME: Should diagnose multiple access notes for the same decl
931

1032
// FIXME: Should diagnose access notes that don't match a decl in the source code
33+
34+
// Only valid on platforms where @objc is valid.
35+
// REQUIRES: objc_interop

0 commit comments

Comments
 (0)