blob: b8333c1fbfb5c3c8f43793d8d5c6b865e17bcdea [file] [log] [blame]
Bill Wendling469211a2007-06-27 03:19:45 +00001//===--- LLVMDiagChecker.cpp - Diagnostic Checking Functions --------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file was developed by Bill Wendling and is distributed under the
6// University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Process the input files and check that the diagnostic messages are expected.
11//
12//===----------------------------------------------------------------------===//
13
14#include "LLVMDiagChecker.h"
15#include "ASTStreamers.h"
Bill Wendling469211a2007-06-27 03:19:45 +000016#include "clang/Basic/SourceManager.h"
17#include "clang/Lex/Preprocessor.h"
18using namespace clang;
19
Bill Wendling52b0a4e2007-06-27 07:24:11 +000020typedef TextDiagnosticBuffer::DiagList DiagList;
21
Bill Wendling469211a2007-06-27 03:19:45 +000022// USING THE DIAGNOSTIC CHECKER:
23//
24// Indicating that a line expects an error or a warning is simple. Put a comment
25// on the line that has the diagnostic, use "expected-{error,warning}" to tag
26// if it's an expected error or warning, and place the expected text between {{
27// and }} markers. The full text doesn't have to be included, only enough to
28// ensure that the correct diagnostic was emitted.
29//
30// Here's an example:
31//
32// int A = B; // expected-error {{use of undeclared identifier 'B'}}
33//
34// You can place as many diagnostics on one line as you wish. To make the code
35// more readable, you can use slash-newline to separate out the diagnostics.
36
37static const char * const ExpectedErrStr = "expected-error";
38static const char * const ExpectedWarnStr = "expected-warning";
39
40/// FindDiagnostics - Go through the comment and see if it indicates expected
41/// diagnostics. If so, then put them in a diagnostic list.
42///
43static void FindDiagnostics(const std::string &Comment,
44 TextDiagnosticBuffer &DiagClient,
45 DiagList &ExpectedDiags,
46 SourceManager &SourceMgr,
47 SourceLocation Pos,
48 const char * const ExpectedStr) {
49 // Find all expected diagnostics
50 typedef std::string::size_type size_type;
51 size_type ColNo = std::string::npos;
52
53 for (;;) {
54 ColNo = Comment.find(ExpectedStr, ColNo);
55 if (ColNo == std::string::npos) break;
56
57 size_type OpenDiag = Comment.find_first_of("{{", ColNo);
58
59 if (OpenDiag == std::string::npos) {
60 fprintf(stderr,
61 "oops:%d: Cannot find beginning of expected error string\n",
62 SourceMgr.getLineNumber(Pos));
63 break;
64 }
65
66 OpenDiag += 2;
67 size_type CloseDiag = Comment.find_first_of("}}", OpenDiag);
68
69 if (CloseDiag == std::string::npos) {
70 fprintf(stderr,
71 "oops:%d: Cannot find end of expected error string\n",
72 SourceMgr.getLineNumber(Pos));
73 break;
74 }
75
76 std::string Msg(Comment.substr(OpenDiag, CloseDiag - OpenDiag));
77 ExpectedDiags.push_back(std::make_pair(Pos, Msg));
78 ColNo = CloseDiag + 2;
79 }
80}
81
82/// ProcessFileDiagnosticChecking - This lexes the file and finds all of the
83/// expected errors and warnings. It then does the actual parsing of the
84/// program. The parsing will report its diagnostics, and a function can be
85/// called later to report any discrepencies between the diagnostics expected
86/// and those actually seen.
87///
88void clang::ProcessFileDiagnosticChecking(TextDiagnosticBuffer &DiagClient,
89 Preprocessor &PP,
90 const std::string &InFile,
91 SourceManager &SourceMgr,
92 unsigned MainFileID,
93 DiagList &ExpectedErrors,
94 DiagList &ExpectedWarnings) {
95 LexerToken Tok;
96 PP.SetCommentRetentionState(true, true);
97
98 // Enter the cave.
99 PP.EnterSourceFile(MainFileID, 0, true);
100
101 do {
102 PP.Lex(Tok);
103
104 if (Tok.getKind() == tok::comment) {
105 std::string Comment = PP.getSpelling(Tok);
106
107 // Find all expected errors
108 FindDiagnostics(Comment, DiagClient, ExpectedErrors, SourceMgr,
109 Tok.getLocation(), ExpectedErrStr);
110
111 // Find all expected warnings
112 FindDiagnostics(Comment, DiagClient, ExpectedWarnings, SourceMgr,
113 Tok.getLocation(), ExpectedWarnStr);
114 }
115 } while (Tok.getKind() != tok::eof);
116
117 // Parsing the specified input file.
118 PP.SetCommentRetentionState(false, false);
119 BuildASTs(PP, MainFileID, false);
120}
121
122typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
123
124/// PrintProblem - This takes a diagnostic map of the delta between expected and
125/// seen diagnostics. If there's anything in it, then something unexpected
126/// happened. Print the map out in a nice format and return "true". If the map
127/// is empty and we're not going to print things, then return "false".
128///
129static bool PrintProblem(SourceManager &SourceMgr,
130 const_diag_iterator diag_begin,
131 const_diag_iterator diag_end,
132 const char *Msg) {
133 if (diag_begin == diag_end) return false;
134
135 fprintf(stderr, "%s\n", Msg);
136
Bill Wendling97d41462007-06-27 04:06:59 +0000137 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I)
Bill Wendling469211a2007-06-27 03:19:45 +0000138 fprintf(stderr, " LineNo %d:\n %s\n",
Bill Wendling97d41462007-06-27 04:06:59 +0000139 SourceMgr.getLineNumber(I->first),
140 I->second.c_str());
Bill Wendling469211a2007-06-27 03:19:45 +0000141
142 return true;
143}
144
145/// CompareDiagLists - Compare two diangnostic lists and return the difference
146/// between them.
147///
148static bool CompareDiagLists(SourceManager &SourceMgr,
149 const_diag_iterator d1_begin,
150 const_diag_iterator d1_end,
151 const_diag_iterator d2_begin,
152 const_diag_iterator d2_end,
153 const char *Msg) {
Bill Wendling52b0a4e2007-06-27 07:24:11 +0000154 DiagList DiffList;
Bill Wendling469211a2007-06-27 03:19:45 +0000155
Bill Wendling97d41462007-06-27 04:06:59 +0000156 for (const_diag_iterator I = d1_begin, E = d1_end; I != E; ++I) {
Bill Wendlinga5b3bb12007-06-27 07:43:27 +0000157 unsigned LineNo1 = SourceMgr.getLineNumber(I->first);
Bill Wendling97d41462007-06-27 04:06:59 +0000158 const std::string &Diag1 = I->second;
Bill Wendling469211a2007-06-27 03:19:45 +0000159 bool Found = false;
160
Bill Wendling97d41462007-06-27 04:06:59 +0000161 for (const_diag_iterator II = d2_begin, IE = d2_end; II != IE; ++II) {
Bill Wendlinga5b3bb12007-06-27 07:43:27 +0000162 unsigned LineNo2 = SourceMgr.getLineNumber(II->first);
163 if (LineNo1 != LineNo2) continue;
Bill Wendling97d41462007-06-27 04:06:59 +0000164
Bill Wendlinga5b3bb12007-06-27 07:43:27 +0000165 const std::string &Diag2 = II->second;
Bill Wendling97d41462007-06-27 04:06:59 +0000166 if (Diag2.find(Diag1) != std::string::npos ||
167 Diag1.find(Diag2) != std::string::npos) {
Bill Wendling469211a2007-06-27 03:19:45 +0000168 Found = true;
169 break;
170 }
171 }
172
173 if (!Found)
Bill Wendlinga5b3bb12007-06-27 07:43:27 +0000174 DiffList.push_back(std::make_pair(I->first, Diag1));
Bill Wendling469211a2007-06-27 03:19:45 +0000175 }
176
177 return PrintProblem(SourceMgr, DiffList.begin(), DiffList.end(), Msg);
178}
179
180/// ReportCheckingResults - This compares the expected results to those that
181/// were actually reported. It emits any discrepencies. Return "true" if there
182/// were problems. Return "false" otherwise.
183///
184bool clang::ReportCheckingResults(TextDiagnosticBuffer &DiagClient,
185 const DiagList &ExpectedErrors,
186 const DiagList &ExpectedWarnings,
187 SourceManager &SourceMgr) {
188 // We want to capture the delta between what was expected and what was
189 // seen.
190 //
191 // Expected \ Seen - set expected but not seen
192 // Seen \ Expected - set seen but not expected
193 bool HadProblem = false;
194
195 // See if there were errors that were expected but not seen.
196 HadProblem |= CompareDiagLists(SourceMgr,
197 ExpectedErrors.begin(), ExpectedErrors.end(),
198 DiagClient.err_begin(), DiagClient.err_end(),
199 "Errors expected but not seen:");
200
201 // See if there were errors that were seen but not expected.
202 HadProblem |= CompareDiagLists(SourceMgr,
203 DiagClient.err_begin(), DiagClient.err_end(),
204 ExpectedErrors.begin(), ExpectedErrors.end(),
205 "Errors seen but not expected:");
206
207 // See if there were warnings that were expected but not seen.
208 HadProblem |= CompareDiagLists(SourceMgr,
209 ExpectedWarnings.begin(),
210 ExpectedWarnings.end(),
211 DiagClient.warn_begin(), DiagClient.warn_end(),
212 "Warnings expected but not seen:");
213
214 // See if there were warnings that were seen but not expected.
215 HadProblem |= CompareDiagLists(SourceMgr,
216 DiagClient.warn_begin(), DiagClient.warn_end(),
217 ExpectedWarnings.begin(),
218 ExpectedWarnings.end(),
219 "Warnings seen but not expected:");
220
221 return HadProblem;
222}