Skip to content

Commit baea392

Browse files
committed
[frontend] Add an option called -verify-additional-prefix to add additional check prefixes to the DiagnosticVerifier.
This enables one to use varying prefixes when checking diagnostics with the DiagnosticVerifier. So for instance, I can make a test work both with and without send-non-sendable enabled by adding additional prefixes. As an example: ```swift // RUN: %target-swift-frontend ... -verify-additional-prefix no-sns- // RUN: %target-swift-frontend ... -verify-additional-prefix sns- let x = ... // expected-error {{This is always checked no matter what prefixes I added}} let y = ... // expected-no-sns-error {{This is only checked if send non sendable is disabled}} let z = ... // expected-sns-error {{This is only checked if send non sendable is enabled}} let w = ... // expected-no-sns-error {{This is checked for a specific error when sns is disabled...}} // expected-sns-error @-1 {{and for a different error when sns is enabled}} ``` rdar://114643840
1 parent 52c732f commit baea392

File tree

8 files changed

+161
-20
lines changed

8 files changed

+161
-20
lines changed

include/swift/Basic/DiagnosticOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ class DiagnosticOptions {
8383
/// Path to a directory of diagnostic localization tables.
8484
std::string LocalizationPath = "";
8585

86+
/// A list of post-fixes to expected- that the diagnostic verifier should
87+
/// check for diagnostics.
88+
///
89+
/// For example, if one placed the phrase "name", the verifier will check for:
90+
/// expected-name-{error,note,warning,remark} as well as the normal expected-
91+
/// prefixes.
92+
///
93+
/// NOTE: These are checked in order so if a postfix a is a prefix of another
94+
/// postfix b and a is before b in the list, b will never be checked.
95+
std::vector<std::string> AdditionalDiagnosticVerifierPostfixes;
96+
8697
/// Return a hash code of any components from these options that should
8798
/// contribute to a Swift Bridging PCH hash.
8899
llvm::hash_code getPCHHashComponents() const {

include/swift/Frontend/DiagnosticVerifier.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,15 @@ class DiagnosticVerifier : public DiagnosticConsumer {
9494
SmallVector<unsigned, 4> AdditionalBufferIDs;
9595
bool AutoApplyFixes;
9696
bool IgnoreUnknown;
97+
ArrayRef<std::string> AcceptedExpectedPostfixes;
9798

9899
public:
99100
explicit DiagnosticVerifier(SourceManager &SM, ArrayRef<unsigned> BufferIDs,
100-
bool AutoApplyFixes, bool IgnoreUnknown)
101+
bool AutoApplyFixes, bool IgnoreUnknown,
102+
ArrayRef<std::string> AcceptedExpectedPostfixes)
101103
: SM(SM), BufferIDs(BufferIDs), AutoApplyFixes(AutoApplyFixes),
102-
IgnoreUnknown(IgnoreUnknown) {}
104+
IgnoreUnknown(IgnoreUnknown),
105+
AcceptedExpectedPostfixes(AcceptedExpectedPostfixes) {}
103106

104107
void appendAdditionalBufferID(unsigned bufferID) {
105108
AdditionalBufferIDs.push_back(bufferID);

include/swift/Option/FrontendOptions.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ def verify : Flag<["-"], "verify">,
111111
"annotations">;
112112
def verify_additional_file : Separate<["-"], "verify-additional-file">,
113113
HelpText<"Verify diagnostics in this file in addition to source files">;
114+
def verify_additional_prefix : Separate<["-"], "verify-additional-prefix">,
115+
HelpText<"Check for diagnostics with the prefix expected-<PREFIX> as well as expected-">;
114116
def verify_apply_fixes : Flag<["-"], "verify-apply-fixes">,
115117
HelpText<"Like -verify, but updates the original source file">;
116118
def verify_ignore_unknown: Flag<["-"], "verify-ignore-unknown">,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,8 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
17951795

17961796
for (Arg *A : Args.filtered(OPT_verify_additional_file))
17971797
Opts.AdditionalVerifierFiles.push_back(A->getValue());
1798+
for (Arg *A : Args.filtered(OPT_verify_additional_prefix))
1799+
Opts.AdditionalDiagnosticVerifierPostfixes.push_back(A->getValue());
17981800

17991801
Opts.UseColor |=
18001802
Args.hasFlag(OPT_color_diagnostics,

lib/Frontend/DiagnosticVerifier.cpp

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,81 @@
2626

2727
using namespace swift;
2828

29+
namespace {
30+
31+
struct ExpectedCheckMatchStartParser {
32+
StringRef MatchStart;
33+
const char *ClassificationStartLoc = nullptr;
34+
std::optional<DiagnosticKind> ExpectedClassification;
35+
36+
ExpectedCheckMatchStartParser(StringRef MatchStart)
37+
: MatchStart(MatchStart) {}
38+
39+
bool tryParseClassification() {
40+
if (MatchStart.startswith("note")) {
41+
ClassificationStartLoc = MatchStart.data();
42+
ExpectedClassification = DiagnosticKind::Note;
43+
MatchStart = MatchStart.substr(strlen("note"));
44+
return true;
45+
}
46+
47+
if (MatchStart.startswith("warning")) {
48+
ClassificationStartLoc = MatchStart.data();
49+
ExpectedClassification = DiagnosticKind::Warning;
50+
MatchStart = MatchStart.substr(strlen("warning"));
51+
return true;
52+
}
53+
54+
if (MatchStart.startswith("error")) {
55+
ClassificationStartLoc = MatchStart.data();
56+
ExpectedClassification = DiagnosticKind::Error;
57+
MatchStart = MatchStart.substr(strlen("error"));
58+
return true;
59+
}
60+
61+
if (MatchStart.startswith("remark")) {
62+
ClassificationStartLoc = MatchStart.data();
63+
ExpectedClassification = DiagnosticKind::Remark;
64+
MatchStart = MatchStart.substr(strlen("remark"));
65+
return true;
66+
}
67+
68+
return false;
69+
}
70+
71+
bool parse(ArrayRef<std::string> prefixes) {
72+
// First try to parse as if we did not have a prefix. We always parse at
73+
// least expected-*.
74+
if (tryParseClassification())
75+
return true;
76+
77+
// Otherwise, walk our prefixes until we find one that matches and attempt
78+
// to check for a note, warning, error, or remark.
79+
//
80+
// TODO: We could make this more flexible, but this should work in the
81+
// short term.
82+
for (auto &p : prefixes) {
83+
if (MatchStart.starts_with(p)) {
84+
MatchStart = MatchStart.substr(p.size());
85+
return tryParseClassification();
86+
}
87+
}
88+
89+
return false;
90+
}
91+
};
92+
93+
} // anonymous namespace
94+
2995
namespace swift {
96+
3097
struct ExpectedFixIt {
3198
const char *StartLoc, *EndLoc; // The loc of the {{ and }}'s.
3299
LineColumnRange Range;
33100

34101
std::string Text;
35102
};
103+
36104
} // end namespace swift
37105

38106
const LineColumnRange &
@@ -494,23 +562,21 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
494562
StringRef MatchStart = InputFile.substr(Match);
495563
const char *DiagnosticLoc = MatchStart.data();
496564
MatchStart = MatchStart.substr(strlen("expected-"));
497-
const char *ClassificationStartLoc = MatchStart.data();
498565

499-
DiagnosticKind ExpectedClassification;
500-
if (MatchStart.startswith("note")) {
501-
ExpectedClassification = DiagnosticKind::Note;
502-
MatchStart = MatchStart.substr(strlen("note"));
503-
} else if (MatchStart.startswith("warning")) {
504-
ExpectedClassification = DiagnosticKind::Warning;
505-
MatchStart = MatchStart.substr(strlen("warning"));
506-
} else if (MatchStart.startswith("error")) {
507-
ExpectedClassification = DiagnosticKind::Error;
508-
MatchStart = MatchStart.substr(strlen("error"));
509-
} else if (MatchStart.startswith("remark")) {
510-
ExpectedClassification = DiagnosticKind::Remark;
511-
MatchStart = MatchStart.substr(strlen("remark"));
512-
} else
513-
continue;
566+
const char *ClassificationStartLoc = nullptr;
567+
std::optional<DiagnosticKind> ExpectedClassification;
568+
{
569+
ExpectedCheckMatchStartParser parser(MatchStart);
570+
// If we fail to parse... continue.
571+
if (!parser.parse(AcceptedExpectedPostfixes)) {
572+
continue;
573+
}
574+
MatchStart = parser.MatchStart;
575+
ClassificationStartLoc = parser.ClassificationStartLoc;
576+
ExpectedClassification = parser.ExpectedClassification;
577+
}
578+
assert(ClassificationStartLoc);
579+
assert(bool(ExpectedClassification));
514580

515581
// Skip any whitespace before the {{.
516582
MatchStart = MatchStart.substr(MatchStart.find_first_not_of(" \t"));
@@ -525,7 +591,7 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
525591

526592
ExpectedDiagnosticInfo Expected(DiagnosticLoc, ClassificationStartLoc,
527593
/*ClassificationEndLoc=*/MatchStart.data(),
528-
ExpectedClassification);
594+
*ExpectedClassification);
529595
int LineOffset = 0;
530596

531597
if (TextStartIdx > 0 && MatchStart[0] == '@') {

lib/Frontend/Frontend.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,8 @@ bool CompilerInstance::setupDiagnosticVerifierIfNeeded() {
363363
DiagVerifier = std::make_unique<DiagnosticVerifier>(
364364
SourceMgr, InputSourceCodeBufferIDs,
365365
diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes,
366-
diagOpts.VerifyIgnoreUnknown);
366+
diagOpts.VerifyIgnoreUnknown,
367+
diagOpts.AdditionalDiagnosticVerifierPostfixes);
367368
for (const auto &filename : diagOpts.AdditionalVerifierFiles) {
368369
auto result = getFileSystem().getBufferForFile(filename);
369370
if (!result) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: not %target-swift-frontend %s -verify -emit-sil -o /dev/null 2>&1 | %FileCheck %s
2+
// RUN: not %target-swift-frontend %s -verify -DFIRST -verify-additional-prefix first- -DFIRST -emit-sil -o /dev/null 2>&1 | %FileCheck -check-prefix=FIRST %s
3+
// RUN: not %target-swift-frontend %s -verify -DSECOND -verify-additional-prefix second- -DSECOND -emit-sil -o /dev/null 2>&1 | %FileCheck -check-prefix=SECOND %s
4+
// RUN: not %target-swift-frontend %s -verify -DBOTH -verify-additional-prefix first- -verify-additional-prefix second- -DFIRST -DSECOND -emit-sil -o /dev/null 2>&1 | %FileCheck -check-prefix=BOTH %s
5+
6+
func test() {
7+
// CHECK-NOT: initialization of immutable value 'normal'
8+
// FIRST-NOT: initialization of immutable value 'normal'
9+
// SECOND-NOT: initialization of immutable value 'normal'
10+
// BOTH-NOT: initialization of immutable value 'normal'
11+
let normal = 5 // expected-warning {{initialization of immutable value 'normal'}}
12+
13+
// CHECK: initialization of immutable value 'first'
14+
// FIRST-NOT: initialization of immutable value 'first'
15+
// SECOND: initialization of immutable value 'first'
16+
// BOTH-NOT: initialization of immutable value 'first'
17+
let first = 5 // expected-first-warning {{initialization of immutable value 'first'}}
18+
19+
// CHECK: initialization of immutable value 'second'
20+
// FIRST: initialization of immutable value 'second'
21+
// SECOND-NOT: initialization of immutable value 'second'
22+
// BOTH-NOT: initialization of immutable value 'second'
23+
let second = 5 // expected-second-warning {{initialization of immutable value 'second'}}
24+
25+
// CHECK: initialization of immutable value 'third'
26+
// FIRST: initialization of immutable value 'third'
27+
// SECOND: initialization of immutable value 'third'
28+
// BOTH: initialization of immutable value 'third'
29+
let third = 5
30+
31+
// In this case, BOTH is accepting both first and second... so because we have
32+
// a first/second warning on the next line, we fail since we consume the
33+
// warning for the first diagnostic.
34+
#if BOTH
35+
// BOTH: expected warning not produced
36+
let fourth = 5
37+
// expected-second-warning @-1 {{initialization of immutable value 'fourth'}}
38+
// expected-first-warning @-2 {{initialization of immutable value 'fourth'}}
39+
#endif
40+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-frontend %s -verify -emit-sil -o /dev/null
2+
// RUN: %target-swift-frontend %s -verify -verify-additional-prefix first- -DFIRST -emit-sil -o /dev/null
3+
// RUN: %target-swift-frontend %s -verify -verify-additional-prefix second- -DSECOND -emit-sil -o /dev/null
4+
// RUN: %target-swift-frontend %s -verify -verify-additional-prefix first- -verify-additional-prefix second- -DFIRST -DSECOND -emit-sil -o /dev/null
5+
6+
func test() {
7+
let x = 5 // expected-warning {{}}
8+
9+
#if FIRST
10+
let y = 5 // expected-first-warning {{}}
11+
#endif
12+
13+
#if SECOND
14+
let z = 5 // expected-second-warning {{}}
15+
#endif
16+
}

0 commit comments

Comments
 (0)