blob: 9524232d5b20e4a29d3c6fee4433c9a3a9158123 [file] [log] [blame]
Chris Lattnera092b142007-06-27 17:24:55 +00001//===--- DiagChecker.cpp - Diagnostic Checking Functions ------------------===//
Bill Wendling469211a2007-06-27 03:19:45 +00002//
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
Chris Lattner23e63532007-06-28 04:54:17 +000014#include "clang.h"
Bill Wendling469211a2007-06-27 03:19:45 +000015#include "ASTStreamers.h"
Chris Lattner23e63532007-06-28 04:54:17 +000016#include "TextDiagnosticBuffer.h"
Bill Wendling469211a2007-06-27 03:19:45 +000017#include "clang/Basic/SourceManager.h"
18#include "clang/Lex/Preprocessor.h"
19using namespace clang;
20
Bill Wendling52b0a4e2007-06-27 07:24:11 +000021typedef TextDiagnosticBuffer::DiagList DiagList;
22
Bill Wendling469211a2007-06-27 03:19:45 +000023// USING THE DIAGNOSTIC CHECKER:
24//
25// Indicating that a line expects an error or a warning is simple. Put a comment
26// on the line that has the diagnostic, use "expected-{error,warning}" to tag
27// if it's an expected error or warning, and place the expected text between {{
28// and }} markers. The full text doesn't have to be included, only enough to
29// ensure that the correct diagnostic was emitted.
30//
31// Here's an example:
32//
33// int A = B; // expected-error {{use of undeclared identifier 'B'}}
34//
35// You can place as many diagnostics on one line as you wish. To make the code
36// more readable, you can use slash-newline to separate out the diagnostics.
37
38static const char * const ExpectedErrStr = "expected-error";
39static const char * const ExpectedWarnStr = "expected-warning";
40
41/// FindDiagnostics - Go through the comment and see if it indicates expected
42/// diagnostics. If so, then put them in a diagnostic list.
43///
44static void FindDiagnostics(const std::string &Comment,
Chris Lattner23e63532007-06-28 04:54:17 +000045 const TextDiagnosticBuffer &DiagClient,
Bill Wendling469211a2007-06-27 03:19:45 +000046 DiagList &ExpectedDiags,
47 SourceManager &SourceMgr,
48 SourceLocation Pos,
49 const char * const ExpectedStr) {
50 // Find all expected diagnostics
51 typedef std::string::size_type size_type;
52 size_type ColNo = std::string::npos;
53
54 for (;;) {
55 ColNo = Comment.find(ExpectedStr, ColNo);
56 if (ColNo == std::string::npos) break;
57
58 size_type OpenDiag = Comment.find_first_of("{{", ColNo);
59
60 if (OpenDiag == std::string::npos) {
61 fprintf(stderr,
62 "oops:%d: Cannot find beginning of expected error string\n",
63 SourceMgr.getLineNumber(Pos));
64 break;
65 }
66
67 OpenDiag += 2;
68 size_type CloseDiag = Comment.find_first_of("}}", OpenDiag);
69
70 if (CloseDiag == std::string::npos) {
71 fprintf(stderr,
72 "oops:%d: Cannot find end of expected error string\n",
73 SourceMgr.getLineNumber(Pos));
74 break;
75 }
76
77 std::string Msg(Comment.substr(OpenDiag, CloseDiag - OpenDiag));
78 ExpectedDiags.push_back(std::make_pair(Pos, Msg));
79 ColNo = CloseDiag + 2;
80 }
81}
82
83/// ProcessFileDiagnosticChecking - This lexes the file and finds all of the
84/// expected errors and warnings. It then does the actual parsing of the
85/// program. The parsing will report its diagnostics, and a function can be
86/// called later to report any discrepencies between the diagnostics expected
87/// and those actually seen.
88///
Chris Lattner23e63532007-06-28 04:54:17 +000089static void ProcessFileDiagnosticChecking(const TextDiagnosticBuffer&DiagClient,
Bill Wendling469211a2007-06-27 03:19:45 +000090 Preprocessor &PP,
Bill Wendling469211a2007-06-27 03:19:45 +000091 unsigned MainFileID,
92 DiagList &ExpectedErrors,
93 DiagList &ExpectedWarnings) {
Chris Lattner23e63532007-06-28 04:54:17 +000094 // Return comments as tokens, this is how we find expected diagnostics.
Bill Wendling469211a2007-06-27 03:19:45 +000095 PP.SetCommentRetentionState(true, true);
96
97 // Enter the cave.
98 PP.EnterSourceFile(MainFileID, 0, true);
99
Chris Lattner23e63532007-06-28 04:54:17 +0000100 LexerToken Tok;
Bill Wendling469211a2007-06-27 03:19:45 +0000101 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
Chris Lattner23e63532007-06-28 04:54:17 +0000108 FindDiagnostics(Comment, DiagClient, ExpectedErrors,PP.getSourceManager(),
Bill Wendling469211a2007-06-27 03:19:45 +0000109 Tok.getLocation(), ExpectedErrStr);
110
111 // Find all expected warnings
Chris Lattner23e63532007-06-28 04:54:17 +0000112 FindDiagnostics(Comment, DiagClient, ExpectedWarnings,
113 PP.getSourceManager(),Tok.getLocation(), ExpectedWarnStr);
Bill Wendling469211a2007-06-27 03:19:45 +0000114 }
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)
Chris Lattnerc2d81632007-06-27 17:26:23 +0000138 fprintf(stderr, " Line %d: %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///
Chris Lattner23e63532007-06-28 04:54:17 +0000184static bool ReportCheckingResults(const TextDiagnosticBuffer &DiagClient,
Bill Wendling469211a2007-06-27 03:19:45 +0000185 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}
Chris Lattner23e63532007-06-28 04:54:17 +0000223
224/// CheckDiagnostics - Implement the -parse-ast-check diagnostic verifier.
225bool clang::CheckDiagnostics(Preprocessor &PP, unsigned MainFileID) {
226 const TextDiagnosticBuffer &Diags =
227 static_cast<const TextDiagnosticBuffer&>(PP.getDiagnostics().getClient());
228
229 // Gather the set of expected diagnostics.
230 DiagList ExpectedErrors, ExpectedWarnings;
231 ProcessFileDiagnosticChecking(Diags, PP, MainFileID, ExpectedErrors,
232 ExpectedWarnings);
233
234
235 return ReportCheckingResults(Diags, ExpectedErrors,
236 ExpectedWarnings, PP.getSourceManager());
237}
238
239