blob: 1e327ce363d920ba74d735f05431aea5cbd0adf4 [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 Lattnerd2177732007-07-20 16:59:19 +000094 Token Tok;
Reid Spencer5f016e22007-07-11 17:01:13 +000095 do {
96 PP.Lex(Tok);
97
98 if (Tok.getKind() == tok::comment) {
99 std::string Comment = PP.getSpelling(Tok);
100
101 // Find all expected errors
102 FindDiagnostics(Comment, ExpectedErrors,PP.getSourceManager(),
103 Tok.getLocation(), ExpectedErrStr);
104
105 // Find all expected warnings
106 FindDiagnostics(Comment, ExpectedWarnings, PP.getSourceManager(),
107 Tok.getLocation(), ExpectedWarnStr);
108 }
109 } while (Tok.getKind() != tok::eof);
110
111 PP.SetCommentRetentionState(false, false);
112}
113
114/// PrintProblem - This takes a diagnostic map of the delta between expected and
115/// seen diagnostics. If there's anything in it, then something unexpected
116/// happened. Print the map out in a nice format and return "true". If the map
117/// is empty and we're not going to print things, then return "false".
118///
119static bool PrintProblem(SourceManager &SourceMgr,
120 const_diag_iterator diag_begin,
121 const_diag_iterator diag_end,
122 const char *Msg) {
123 if (diag_begin == diag_end) return false;
124
125 fprintf(stderr, "%s\n", Msg);
126
127 for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I)
128 fprintf(stderr, " Line %d: %s\n",
Chris Lattner9dc1f532007-07-20 16:37:10 +0000129 SourceMgr.getLogicalLineNumber(I->first),
Reid Spencer5f016e22007-07-11 17:01:13 +0000130 I->second.c_str());
131
132 return true;
133}
134
135/// CompareDiagLists - Compare two diangnostic lists and return the difference
136/// between them.
137///
138static bool CompareDiagLists(SourceManager &SourceMgr,
139 const_diag_iterator d1_begin,
140 const_diag_iterator d1_end,
141 const_diag_iterator d2_begin,
142 const_diag_iterator d2_end,
143 const char *Msg) {
144 DiagList DiffList;
145
146 for (const_diag_iterator I = d1_begin, E = d1_end; I != E; ++I) {
Chris Lattner9dc1f532007-07-20 16:37:10 +0000147 unsigned LineNo1 = SourceMgr.getLogicalLineNumber(I->first);
Reid Spencer5f016e22007-07-11 17:01:13 +0000148 const std::string &Diag1 = I->second;
149 bool Found = false;
150
151 for (const_diag_iterator II = d2_begin, IE = d2_end; II != IE; ++II) {
Chris Lattner9dc1f532007-07-20 16:37:10 +0000152 unsigned LineNo2 = SourceMgr.getLogicalLineNumber(II->first);
Reid Spencer5f016e22007-07-11 17:01:13 +0000153 if (LineNo1 != LineNo2) continue;
154
155 const std::string &Diag2 = II->second;
156 if (Diag2.find(Diag1) != std::string::npos ||
157 Diag1.find(Diag2) != std::string::npos) {
158 Found = true;
159 break;
160 }
161 }
162
163 if (!Found)
164 DiffList.push_back(std::make_pair(I->first, Diag1));
165 }
166
167 return PrintProblem(SourceMgr, DiffList.begin(), DiffList.end(), Msg);
168}
169
170/// CheckResults - This compares the expected results to those that
171/// were actually reported. It emits any discrepencies. Return "true" if there
172/// were problems. Return "false" otherwise.
173///
174static bool CheckResults(Preprocessor &PP,
175 const DiagList &ExpectedErrors,
176 const DiagList &ExpectedWarnings) {
177 const TextDiagnosticBuffer &Diags =
178 static_cast<const TextDiagnosticBuffer&>(PP.getDiagnostics().getClient());
179 SourceManager &SourceMgr = PP.getSourceManager();
180
181 // We want to capture the delta between what was expected and what was
182 // seen.
183 //
184 // Expected \ Seen - set expected but not seen
185 // Seen \ Expected - set seen but not expected
186 bool HadProblem = false;
187
188 // See if there were errors that were expected but not seen.
189 HadProblem |= CompareDiagLists(SourceMgr,
190 ExpectedErrors.begin(), ExpectedErrors.end(),
191 Diags.err_begin(), Diags.err_end(),
192 "Errors expected but not seen:");
193
194 // See if there were errors that were seen but not expected.
195 HadProblem |= CompareDiagLists(SourceMgr,
196 Diags.err_begin(), Diags.err_end(),
197 ExpectedErrors.begin(), ExpectedErrors.end(),
198 "Errors seen but not expected:");
199
200 // See if there were warnings that were expected but not seen.
201 HadProblem |= CompareDiagLists(SourceMgr,
202 ExpectedWarnings.begin(),
203 ExpectedWarnings.end(),
204 Diags.warn_begin(), Diags.warn_end(),
205 "Warnings expected but not seen:");
206
207 // See if there were warnings that were seen but not expected.
208 HadProblem |= CompareDiagLists(SourceMgr,
209 Diags.warn_begin(), Diags.warn_end(),
210 ExpectedWarnings.begin(),
211 ExpectedWarnings.end(),
212 "Warnings seen but not expected:");
213
214 return HadProblem;
215}
216
217/// CheckDiagnostics - Implement the -parse-ast-check diagnostic verifier.
218bool clang::CheckDiagnostics(Preprocessor &PP, unsigned MainFileID) {
Chris Lattner009e9f72007-08-10 18:27:41 +0000219 // Parse the specified input file.
220 BuildASTs(PP, MainFileID, false);
221
Reid Spencer5f016e22007-07-11 17:01:13 +0000222 // Gather the set of expected diagnostics.
223 DiagList ExpectedErrors, ExpectedWarnings;
224 FindExpectedDiags(PP, MainFileID, ExpectedErrors, ExpectedWarnings);
225
Reid Spencer5f016e22007-07-11 17:01:13 +0000226 // Check that the expected diagnostics occurred.
227 return CheckResults(PP, ExpectedErrors, ExpectedWarnings);
228}
229
230