blob: e2efd96cce995922ed1056f51d2fee0cb62f2f25 [file] [log] [blame]
Reid Spencer5f016e22007-07-11 17:01:13 +00001//===--- DiagChecker.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 "clang.h"
15#include "ASTStreamers.h"
16#include "TextDiagnosticBuffer.h"
17#include "clang/Basic/SourceManager.h"
18#include "clang/Lex/Preprocessor.h"
19using namespace clang;
20
21typedef TextDiagnosticBuffer::DiagList DiagList;
22typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
23
24// USING THE DIAGNOSTIC CHECKER:
25//
26// Indicating that a line expects an error or a warning is simple. Put a comment
27// on the line that has the diagnostic, use "expected-{error,warning}" to tag
28// if it's an expected error or warning, and place the expected text between {{
29// and }} markers. The full text doesn't have to be included, only enough to
30// ensure that the correct diagnostic was emitted.
31//
32// Here's an example:
33//
34// int A = B; // expected-error {{use of undeclared identifier 'B'}}
35//
36// You can place as many diagnostics on one line as you wish. To make the code
37// more readable, you can use slash-newline to separate out the diagnostics.
38
39static const char * const ExpectedErrStr = "expected-error";
40static const char * const ExpectedWarnStr = "expected-warning";
41
42/// FindDiagnostics - Go through the comment and see if it indicates expected
43/// diagnostics. If so, then put them in a diagnostic list.
44///
45static void FindDiagnostics(const std::string &Comment,
46 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",
Chris Lattner9dc1f532007-07-20 16:37:10 +000063 SourceMgr.getLogicalLineNumber(Pos));
Reid Spencer5f016e22007-07-11 17:01:13 +000064 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",
Chris Lattner9dc1f532007-07-20 16:37:10 +000073 SourceMgr.getLogicalLineNumber(Pos));
Reid Spencer5f016e22007-07-11 17:01:13 +000074 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/// FindExpectedDiags - Lex the file to finds all of the expected errors and
84/// warnings.
85static void FindExpectedDiags(Preprocessor &PP, unsigned MainFileID,
86 DiagList &ExpectedErrors,
87 DiagList &ExpectedWarnings) {
88 // Return comments as tokens, this is how we find expected diagnostics.
89 PP.SetCommentRetentionState(true, true);
90
91 // Enter the cave.
92 PP.EnterSourceFile(MainFileID, 0, true);
93
Chris Lattnerf01654f2007-08-30 06:34:23 +000094 // Turn off all warnings from relexing or preprocessing.
95 PP.getDiagnostics().setWarnOnExtensions(false);
96 PP.getDiagnostics().setErrorOnExtensions(false);
97 for (unsigned i = 0; i != diag::NUM_DIAGNOSTICS; ++i)
98 if (PP.getDiagnostics().isNoteWarningOrExtension((diag::kind)i))
99 PP.getDiagnostics().setDiagnosticMapping((diag::kind)i, diag::MAP_IGNORE);
100
Chris Lattnerd2177732007-07-20 16:59:19 +0000101 Token Tok;
Reid Spencer5f016e22007-07-11 17:01:13 +0000102 do {
103 PP.Lex(Tok);
104
105 if (Tok.getKind() == tok::comment) {
106 std::string Comment = PP.getSpelling(Tok);
107
108 // Find all expected errors
109 FindDiagnostics(Comment, ExpectedErrors,PP.getSourceManager(),
110 Tok.getLocation(), ExpectedErrStr);
111
112 // Find all expected warnings
113 FindDiagnostics(Comment, ExpectedWarnings, PP.getSourceManager(),
114 Tok.getLocation(), ExpectedWarnStr);
115 }
116 } while (Tok.getKind() != tok::eof);
117
118 PP.SetCommentRetentionState(false, false);
119}
120
121/// PrintProblem - This takes a diagnostic map of the delta between expected and
122/// seen diagnostics. If there's anything in it, then something unexpected
123/// happened. Print the map out in a nice format and return "true". If the map
124/// is empty and we're not going to print things, then return "false".
125///
126static bool PrintProblem(SourceManager &SourceMgr,
127 const_diag_iterator diag_begin,
128 const_diag_iterator diag_end,
129 const char *Msg) {
130 if (diag_begin == diag_end) return false;
131
132 fprintf(stderr, "%s\n", Msg);
133
134 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I)
135 fprintf(stderr, " Line %d: %s\n",
Chris Lattner9dc1f532007-07-20 16:37:10 +0000136 SourceMgr.getLogicalLineNumber(I->first),
Reid Spencer5f016e22007-07-11 17:01:13 +0000137 I->second.c_str());
138
139 return true;
140}
141
142/// CompareDiagLists - Compare two diangnostic lists and return the difference
143/// between them.
144///
145static bool CompareDiagLists(SourceManager &SourceMgr,
146 const_diag_iterator d1_begin,
147 const_diag_iterator d1_end,
148 const_diag_iterator d2_begin,
149 const_diag_iterator d2_end,
150 const char *Msg) {
151 DiagList DiffList;
152
153 for (const_diag_iterator I = d1_begin, E = d1_end; I != E; ++I) {
Chris Lattner9dc1f532007-07-20 16:37:10 +0000154 unsigned LineNo1 = SourceMgr.getLogicalLineNumber(I->first);
Reid Spencer5f016e22007-07-11 17:01:13 +0000155 const std::string &Diag1 = I->second;
156 bool Found = false;
157
158 for (const_diag_iterator II = d2_begin, IE = d2_end; II != IE; ++II) {
Chris Lattner9dc1f532007-07-20 16:37:10 +0000159 unsigned LineNo2 = SourceMgr.getLogicalLineNumber(II->first);
Reid Spencer5f016e22007-07-11 17:01:13 +0000160 if (LineNo1 != LineNo2) continue;
161
162 const std::string &Diag2 = II->second;
163 if (Diag2.find(Diag1) != std::string::npos ||
164 Diag1.find(Diag2) != std::string::npos) {
165 Found = true;
166 break;
167 }
168 }
169
170 if (!Found)
171 DiffList.push_back(std::make_pair(I->first, Diag1));
172 }
173
174 return PrintProblem(SourceMgr, DiffList.begin(), DiffList.end(), Msg);
175}
176
177/// CheckResults - This compares the expected results to those that
178/// were actually reported. It emits any discrepencies. Return "true" if there
179/// were problems. Return "false" otherwise.
180///
181static bool CheckResults(Preprocessor &PP,
182 const DiagList &ExpectedErrors,
183 const DiagList &ExpectedWarnings) {
184 const TextDiagnosticBuffer &Diags =
185 static_cast<const TextDiagnosticBuffer&>(PP.getDiagnostics().getClient());
186 SourceManager &SourceMgr = PP.getSourceManager();
187
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 Diags.err_begin(), Diags.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 Diags.err_begin(), Diags.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 Diags.warn_begin(), Diags.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 Diags.warn_begin(), Diags.warn_end(),
217 ExpectedWarnings.begin(),
218 ExpectedWarnings.end(),
219 "Warnings seen but not expected:");
220
221 return HadProblem;
222}
223
224/// CheckDiagnostics - Implement the -parse-ast-check diagnostic verifier.
225bool clang::CheckDiagnostics(Preprocessor &PP, unsigned MainFileID) {
Chris Lattner009e9f72007-08-10 18:27:41 +0000226 // Parse the specified input file.
227 BuildASTs(PP, MainFileID, false);
228
Reid Spencer5f016e22007-07-11 17:01:13 +0000229 // Gather the set of expected diagnostics.
230 DiagList ExpectedErrors, ExpectedWarnings;
231 FindExpectedDiags(PP, MainFileID, ExpectedErrors, ExpectedWarnings);
232
Reid Spencer5f016e22007-07-11 17:01:13 +0000233 // Check that the expected diagnostics occurred.
234 return CheckResults(PP, ExpectedErrors, ExpectedWarnings);
235}
236
237