Skip to content

Commit d91e5c3

Browse files
authored
[verify] Improve the error messages with multiple active prefixes (#126068)
Multiple improvements to make the messages more concrete, actionable and less confusing when multiple prefixes are used in `-verify=`. The common theme among these was that prior to the patch all error messages would use the alphabetically first prefix, even if the error was associated with a different one. - Mention the actual expected but unseen directive: Prior to this change when reporting expected but unseen directive, the alphabetically first one would be used to report the error even if that's not the one present in the source. Reword the diagnostic if multiple prefixes are active and include the real spelling of the expected directive for each expected but not seen line in the output. - Reword the seen but not expected error message if multiple directives are active to avoid having to pick an arbitrary (the first) prefix for it. - Include the full spelling of the directive when reporting a directive following the no-diagnostics directive. For example "'foo-error' directive cannot follow 'foo-no-diagnostics' directive" - Use the first appearing `-no-diagnostics` directive, in the above message instead of the first one alphabetically. The new wording > diagnostics with '(error|warning|remark|note)' severity seen but not expected instead of > '<prefix>-(error|warning|remark|note)' diagnostics seen but not expected is only used when multiple prefixes are present, the error messages stay the same with a single prefix only.
1 parent 326638b commit d91e5c3

File tree

6 files changed

+120
-51
lines changed

6 files changed

+120
-51
lines changed

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,11 @@ def err_verify_invalid_content : Error<
175175
def err_verify_missing_regex : Error<
176176
"cannot find start of regex ('{{') in %0">;
177177
def err_verify_inconsistent_diags : Error<
178-
"'%0' diagnostics %select{expected|seen}1 but not %select{seen|expected}1: "
179-
"%2">;
178+
"%select{|'%1-%2' }0diagnostics %select{with '%2' severity |}0"
179+
"%select{expected|seen}3 but not %select{seen|expected}3: "
180+
"%4">;
180181
def err_verify_invalid_no_diags : Error<
181-
"%select{expected|'%0-no-diagnostics'}1 directive cannot follow "
182-
"%select{'%0-no-diagnostics' directive|other expected directives}1">;
182+
"'%0' directive cannot follow %select{'%2' directive|other expected directives}1">;
183183
def err_verify_no_directives : Error<
184184
"no expected directives found: consider use of '%0-no-diagnostics'">;
185185
def err_verify_nonconst_addrspace : Error<

clang/include/clang/Frontend/VerifyDiagnosticConsumer.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,17 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
4444
public:
4545
static std::unique_ptr<Directive>
4646
create(bool RegexKind, SourceLocation DirectiveLoc,
47-
SourceLocation DiagnosticLoc, bool MatchAnyFileAndLine,
48-
bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max);
47+
SourceLocation DiagnosticLoc, StringRef Spelling,
48+
bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
49+
unsigned Min, unsigned Max);
4950

5051
public:
5152
/// Constant representing n or more matches.
5253
static const unsigned MaxCount = std::numeric_limits<unsigned>::max();
5354

5455
SourceLocation DirectiveLoc;
5556
SourceLocation DiagnosticLoc;
57+
const std::string Spelling;
5658
const std::string Text;
5759
unsigned Min, Max;
5860
bool MatchAnyLine;
@@ -71,10 +73,11 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
7173

7274
protected:
7375
Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
74-
bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
75-
unsigned Min, unsigned Max)
76-
: DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), Text(Text),
77-
Min(Min), Max(Max), MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine),
76+
StringRef Spelling, bool MatchAnyFileAndLine, bool MatchAnyLine,
77+
StringRef Text, unsigned Min, unsigned Max)
78+
: DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
79+
Spelling(Spelling), Text(Text), Min(Min), Max(Max),
80+
MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine),
7881
MatchAnyFileAndLine(MatchAnyFileAndLine) {
7982
assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
8083
assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) &&
@@ -106,6 +109,11 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
106109
HasOtherExpectedDirectives
107110
};
108111

112+
struct ParsingState {
113+
DirectiveStatus Status;
114+
std::string FirstNoDiagnosticsDirective;
115+
};
116+
109117
class MarkerTracker;
110118

111119
private:
@@ -118,7 +126,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
118126
const LangOptions *LangOpts = nullptr;
119127
SourceManager *SrcManager = nullptr;
120128
unsigned ActiveSourceFiles = 0;
121-
DirectiveStatus Status;
129+
ParsingState State;
122130
ExpectedData ED;
123131

124132
void CheckDiagnostics();

clang/lib/Frontend/VerifyDiagnosticConsumer.cpp

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@ namespace {
8989
class StandardDirective : public Directive {
9090
public:
9191
StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
92-
bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
93-
unsigned Min, unsigned Max)
94-
: Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine,
92+
StringRef Spelling, bool MatchAnyFileAndLine,
93+
bool MatchAnyLine, StringRef Text, unsigned Min,
94+
unsigned Max)
95+
: Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
9596
MatchAnyLine, Text, Min, Max) {}
9697

9798
bool isValid(std::string &Error) override {
@@ -106,9 +107,10 @@ class StandardDirective : public Directive {
106107
class RegexDirective : public Directive {
107108
public:
108109
RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
109-
bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
110-
unsigned Min, unsigned Max, StringRef RegexStr)
111-
: Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine,
110+
StringRef Spelling, bool MatchAnyFileAndLine,
111+
bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
112+
StringRef RegexStr)
113+
: Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
112114
MatchAnyLine, Text, Min, Max),
113115
Regex(RegexStr) {}
114116

@@ -285,6 +287,7 @@ class ParseHelper
285287
// The information necessary to create a directive.
286288
struct UnattachedDirective {
287289
DirectiveList *DL = nullptr;
290+
std::string Spelling;
288291
bool RegexKind = false;
289292
SourceLocation DirectivePos, ContentBegin;
290293
std::string Text;
@@ -299,8 +302,8 @@ void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
299302
bool MatchAnyLine = false) {
300303
// Construct new directive.
301304
std::unique_ptr<Directive> D = Directive::create(
302-
UD.RegexKind, UD.DirectivePos, ExpectedLoc, MatchAnyFileAndLine,
303-
MatchAnyLine, UD.Text, UD.Min, UD.Max);
305+
UD.RegexKind, UD.DirectivePos, ExpectedLoc, UD.Spelling,
306+
MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min, UD.Max);
304307

305308
std::string Error;
306309
if (!D->isValid(Error)) {
@@ -408,7 +411,7 @@ static std::string DetailedErrorString(const DiagnosticsEngine &Diags) {
408411
/// Returns true if any valid directives were found.
409412
static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
410413
Preprocessor *PP, SourceLocation Pos,
411-
VerifyDiagnosticConsumer::DirectiveStatus &Status,
414+
VerifyDiagnosticConsumer::ParsingState &State,
412415
VerifyDiagnosticConsumer::MarkerTracker &Markers) {
413416
DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
414417

@@ -440,8 +443,9 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
440443
StringRef DToken = PH.Match();
441444
PH.Advance();
442445

443-
// Default directive kind.
444446
UnattachedDirective D;
447+
D.Spelling = DToken;
448+
// Default directive kind.
445449
const char *KindStr = "string";
446450

447451
// Parse the initial directive token in reverse so we can easily determine
@@ -482,19 +486,24 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
482486
continue;
483487

484488
if (NoDiag) {
485-
if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
489+
if (State.Status ==
490+
VerifyDiagnosticConsumer::HasOtherExpectedDirectives) {
486491
Diags.Report(Pos, diag::err_verify_invalid_no_diags)
487-
<< DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/true;
488-
else
489-
Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
492+
<< D.Spelling << /*IsExpectedNoDiagnostics=*/true;
493+
} else if (State.Status !=
494+
VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
495+
State.Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
496+
State.FirstNoDiagnosticsDirective = D.Spelling;
497+
}
490498
continue;
491499
}
492-
if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
500+
if (State.Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
493501
Diags.Report(Pos, diag::err_verify_invalid_no_diags)
494-
<< DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/false;
502+
<< D.Spelling << /*IsExpectedNoDiagnostics=*/false
503+
<< State.FirstNoDiagnosticsDirective;
495504
continue;
496505
}
497-
Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
506+
State.Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
498507

499508
// If a directive has been found but we're not interested
500509
// in storing the directive information, return now.
@@ -670,7 +679,7 @@ VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
670679
: Diags(Diags_), PrimaryClient(Diags.getClient()),
671680
PrimaryClientOwner(Diags.takeClient()),
672681
Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)),
673-
Status(HasNoDirectives) {
682+
State{HasNoDirectives, {}} {
674683
if (Diags.hasSourceManager())
675684
setSourceManager(Diags.getSourceManager());
676685
}
@@ -788,7 +797,7 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
788797
// Fold any "\<EOL>" sequences
789798
size_t loc = C.find('\\');
790799
if (loc == StringRef::npos) {
791-
ParseDirective(C, &ED, SM, &PP, CommentBegin, Status, *Markers);
800+
ParseDirective(C, &ED, SM, &PP, CommentBegin, State, *Markers);
792801
return false;
793802
}
794803

@@ -818,7 +827,7 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
818827
}
819828

820829
if (!C2.empty())
821-
ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status, *Markers);
830+
ParseDirective(C2, &ED, SM, &PP, CommentBegin, State, *Markers);
822831
return false;
823832
}
824833

@@ -843,8 +852,8 @@ static bool findDirectives(SourceManager &SM, FileID FID,
843852

844853
Token Tok;
845854
Tok.setKind(tok::comment);
846-
VerifyDiagnosticConsumer::DirectiveStatus Status =
847-
VerifyDiagnosticConsumer::HasNoDirectives;
855+
VerifyDiagnosticConsumer::ParsingState State = {
856+
VerifyDiagnosticConsumer::HasNoDirectives, {}};
848857
while (Tok.isNot(tok::eof)) {
849858
RawLex.LexFromRawLexer(Tok);
850859
if (!Tok.is(tok::comment)) continue;
@@ -856,8 +865,8 @@ static bool findDirectives(SourceManager &SM, FileID FID,
856865
VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics());
857866

858867
// Find first directive.
859-
if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(),
860-
Status, Markers))
868+
if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), State,
869+
Markers))
861870
return true;
862871
}
863872
return false;
@@ -887,10 +896,11 @@ static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceM
887896
OS << ": " << I->second;
888897
}
889898

899+
const bool IsSinglePrefix =
900+
Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
890901
std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
891-
std::string KindStr = Prefix + "-" + Kind;
892902
Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
893-
<< KindStr << /*Unexpected=*/true << OS.str();
903+
<< IsSinglePrefix << Prefix << Kind << /*Unexpected=*/true << OS.str();
894904
return std::distance(diag_begin, diag_end);
895905
}
896906

@@ -902,6 +912,9 @@ static unsigned PrintExpected(DiagnosticsEngine &Diags,
902912
if (DL.empty())
903913
return 0;
904914

915+
const bool IsSinglePrefix =
916+
Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
917+
905918
SmallString<256> Fmt;
906919
llvm::raw_svector_ostream OS(Fmt);
907920
for (const auto *D : DL) {
@@ -917,13 +930,14 @@ static unsigned PrintExpected(DiagnosticsEngine &Diags,
917930
OS << " (directive at "
918931
<< SourceMgr.getFilename(D->DirectiveLoc) << ':'
919932
<< SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
933+
if (!IsSinglePrefix)
934+
OS << " \'" << D->Spelling << '\'';
920935
OS << ": " << D->Text;
921936
}
922937

923938
std::string Prefix = *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
924-
std::string KindStr = Prefix + "-" + Kind;
925939
Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
926-
<< KindStr << /*Unexpected=*/false << OS.str();
940+
<< IsSinglePrefix << Prefix << Kind << /*Unexpected=*/false << OS.str();
927941
return DL.size();
928942
}
929943

@@ -1109,11 +1123,11 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
11091123
if (SrcManager) {
11101124
// Produce an error if no expected-* directives could be found in the
11111125
// source file(s) processed.
1112-
if (Status == HasNoDirectives) {
1126+
if (State.Status == HasNoDirectives) {
11131127
Diags.Report(diag::err_verify_no_directives).setForceEmit()
11141128
<< DetailedErrorString(Diags);
11151129
++NumErrors;
1116-
Status = HasNoDirectivesReported;
1130+
State.Status = HasNoDirectivesReported;
11171131
}
11181132

11191133
// Check that the expected diagnostics occurred.
@@ -1142,15 +1156,14 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
11421156
ED.Reset();
11431157
}
11441158

1145-
std::unique_ptr<Directive> Directive::create(bool RegexKind,
1146-
SourceLocation DirectiveLoc,
1147-
SourceLocation DiagnosticLoc,
1148-
bool MatchAnyFileAndLine,
1149-
bool MatchAnyLine, StringRef Text,
1150-
unsigned Min, unsigned Max) {
1159+
std::unique_ptr<Directive>
1160+
Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
1161+
SourceLocation DiagnosticLoc, StringRef Spelling,
1162+
bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
1163+
unsigned Min, unsigned Max) {
11511164
if (!RegexKind)
11521165
return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
1153-
MatchAnyFileAndLine,
1166+
Spelling, MatchAnyFileAndLine,
11541167
MatchAnyLine, Text, Min, Max);
11551168

11561169
// Parse the directive into a regular expression.
@@ -1175,7 +1188,7 @@ std::unique_ptr<Directive> Directive::create(bool RegexKind,
11751188
}
11761189
}
11771190

1178-
return std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc,
1191+
return std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc, Spelling,
11791192
MatchAnyFileAndLine, MatchAnyLine,
11801193
Text, Min, Max, RegexStr);
11811194
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Test the diagnostic messages of -verify with multiple prefixes.
2+
// - Expected but not seen errors should contain the prefix of the directive
3+
// - Seen but not expected errors should not choose an arbitrary prefix
4+
// - "expected directive cannot follow '<prefix>-no-diagnostics'" should report an actual
5+
// expected-no-diagnostics prefix present in the source.
6+
7+
// RUN: not %clang_cc1 -verify=foo,bar %s 2>&1 | FileCheck %s --check-prefix=CHECK1
8+
// RUN: not %clang_cc1 -verify=bar,foo %s 2>&1 | FileCheck %s --check-prefix=CHECK1
9+
10+
undefined_type x; // #1
11+
12+
// foo-error{{there is no error here}}
13+
// bar-error{{error not seen}}
14+
// bar-note{{declared here}}
15+
// bar-error{{another error not seen}}
16+
// bar-error-re{{regex error{{}} not present}}
17+
18+
// CHECK1: error: diagnostics with 'error' severity expected but not seen:
19+
// CHECK1: Line 12 'foo-error': there is no error here
20+
// CHECK1: Line 13 'bar-error': error not seen
21+
// CHECK1: Line 15 'bar-error': another error not seen
22+
// CHECK1: Line 16 'bar-error-re': regex error{{{{[}][}]}} not present
23+
// CHECK1: error: diagnostics with 'error' severity seen but not expected:
24+
// CHECK1: Line 10: unknown type name 'undefined_type'
25+
// CHECK1: error: diagnostics with 'note' severity expected but not seen:
26+
// CHECK1: Line 14 'bar-note': declared here
27+
// CHECK1: 6 errors generated.
28+
29+
// RUN: not %clang_cc1 -verify=baz,qux,quux %s 2>&1 | FileCheck %s --check-prefix=CHECK2
30+
31+
// qux-no-diagnostics
32+
// baz-error@#1{{unknown type name 'undefined_type'}}
33+
// quux-no-diagnostics
34+
// qux-error-re@#1{{unknown type name 'undefined_type'}}
35+
36+
// CHECK2: error: diagnostics with 'error' severity seen but not expected:
37+
// CHECK2: Line 10: unknown type name 'undefined_type'
38+
// CHECK2: Line 32: 'baz-error' directive cannot follow 'qux-no-diagnostics' directive
39+
// CHECK2: Line 34: 'qux-error-re' directive cannot follow 'qux-no-diagnostics' directive
40+
41+
// RUN: not %clang_cc1 -verify=spam,eggs %s 2>&1 | FileCheck %s --check-prefix=CHECK3
42+
43+
// eggs-error@#1{{unknown type name 'undefined_type'}}
44+
// spam-no-diagnostics
45+
46+
// CHECK3: error: diagnostics with 'error' severity seen but not expected:
47+
// CHECK3: Line 44: 'spam-no-diagnostics' directive cannot follow other expected directives
48+
// CHECK3: 1 error generated.

clang/test/Frontend/verify.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ unexpected b; // expected-error@33 1-1 {{unknown type}}
201201
// foo-note {{}}
202202

203203
// CHECK11: error: 'foo-error' diagnostics seen but not expected:
204-
// CHECK11-NEXT: Line 201: expected directive cannot follow 'foo-no-diagnostics' directive
204+
// CHECK11-NEXT: Line 201: 'foo-note' directive cannot follow 'foo-no-diagnostics' directive
205205
// CHECK11-NEXT: 1 error generated.
206206
#endif
207207

clang/test/Frontend/verify3.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// expected-note {{}}
99

1010
// CHECK1: error: 'expected-error' diagnostics seen but not expected:
11-
// CHECK1-NEXT: Line 8: expected directive cannot follow 'expected-no-diagnostics' directive
11+
// CHECK1-NEXT: Line 8: 'expected-note' directive cannot follow 'expected-no-diagnostics' directive
1212
// CHECK1-NEXT: 1 error generated.
1313
#endif
1414

0 commit comments

Comments
 (0)