blob: 25acfe85a8f892bf663efb7cd36254522297c9b6 [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"
16#include "TextDiagnosticBuffer.h"
17#include "clang/Basic/SourceManager.h"
18#include "clang/Lex/Preprocessor.h"
19using namespace clang;
20
21// USING THE DIAGNOSTIC CHECKER:
22//
23// Indicating that a line expects an error or a warning is simple. Put a comment
24// on the line that has the diagnostic, use "expected-{error,warning}" to tag
25// if it's an expected error or warning, and place the expected text between {{
26// and }} markers. The full text doesn't have to be included, only enough to
27// ensure that the correct diagnostic was emitted.
28//
29// Here's an example:
30//
31// int A = B; // expected-error {{use of undeclared identifier 'B'}}
32//
33// You can place as many diagnostics on one line as you wish. To make the code
34// more readable, you can use slash-newline to separate out the diagnostics.
35
36static const char * const ExpectedErrStr = "expected-error";
37static const char * const ExpectedWarnStr = "expected-warning";
38
39/// FindDiagnostics - Go through the comment and see if it indicates expected
40/// diagnostics. If so, then put them in a diagnostic list.
41///
42static void FindDiagnostics(const std::string &Comment,
43 TextDiagnosticBuffer &DiagClient,
44 DiagList &ExpectedDiags,
45 SourceManager &SourceMgr,
46 SourceLocation Pos,
47 const char * const ExpectedStr) {
48 // Find all expected diagnostics
49 typedef std::string::size_type size_type;
50 size_type ColNo = std::string::npos;
51
52 for (;;) {
53 ColNo = Comment.find(ExpectedStr, ColNo);
54 if (ColNo == std::string::npos) break;
55
56 size_type OpenDiag = Comment.find_first_of("{{", ColNo);
57
58 if (OpenDiag == std::string::npos) {
59 fprintf(stderr,
60 "oops:%d: Cannot find beginning of expected error string\n",
61 SourceMgr.getLineNumber(Pos));
62 break;
63 }
64
65 OpenDiag += 2;
66 size_type CloseDiag = Comment.find_first_of("}}", OpenDiag);
67
68 if (CloseDiag == std::string::npos) {
69 fprintf(stderr,
70 "oops:%d: Cannot find end of expected error string\n",
71 SourceMgr.getLineNumber(Pos));
72 break;
73 }
74
75 std::string Msg(Comment.substr(OpenDiag, CloseDiag - OpenDiag));
76 ExpectedDiags.push_back(std::make_pair(Pos, Msg));
77 ColNo = CloseDiag + 2;
78 }
79}
80
81/// ProcessFileDiagnosticChecking - This lexes the file and finds all of the
82/// expected errors and warnings. It then does the actual parsing of the
83/// program. The parsing will report its diagnostics, and a function can be
84/// called later to report any discrepencies between the diagnostics expected
85/// and those actually seen.
86///
87void clang::ProcessFileDiagnosticChecking(TextDiagnosticBuffer &DiagClient,
88 Preprocessor &PP,
89 const std::string &InFile,
90 SourceManager &SourceMgr,
91 unsigned MainFileID,
92 DiagList &ExpectedErrors,
93 DiagList &ExpectedWarnings) {
94 LexerToken Tok;
95 PP.SetCommentRetentionState(true, true);
96
97 // Enter the cave.
98 PP.EnterSourceFile(MainFileID, 0, true);
99
100 do {
101 PP.Lex(Tok);
102
103 if (Tok.getKind() == tok::comment) {
104 std::string Comment = PP.getSpelling(Tok);
105
106 // Find all expected errors
107 FindDiagnostics(Comment, DiagClient, ExpectedErrors, SourceMgr,
108 Tok.getLocation(), ExpectedErrStr);
109
110 // Find all expected warnings
111 FindDiagnostics(Comment, DiagClient, ExpectedWarnings, SourceMgr,
112 Tok.getLocation(), ExpectedWarnStr);
113 }
114 } while (Tok.getKind() != tok::eof);
115
116 // Parsing the specified input file.
117 PP.SetCommentRetentionState(false, false);
118 BuildASTs(PP, MainFileID, false);
119}
120
121typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
122
123/// PrintProblem - This takes a diagnostic map of the delta between expected and
124/// seen diagnostics. If there's anything in it, then something unexpected
125/// happened. Print the map out in a nice format and return "true". If the map
126/// is empty and we're not going to print things, then return "false".
127///
128static bool PrintProblem(SourceManager &SourceMgr,
129 const_diag_iterator diag_begin,
130 const_diag_iterator diag_end,
131 const char *Msg) {
132 if (diag_begin == diag_end) return false;
133
134 fprintf(stderr, "%s\n", Msg);
135
136 for (; diag_begin != diag_end; ++diag_begin)
137 fprintf(stderr, " LineNo %d:\n %s\n",
138 SourceMgr.getLineNumber(diag_begin->first),
139 diag_begin->second.c_str());
140
141 return true;
142}
143
144/// CompareDiagLists - Compare two diangnostic lists and return the difference
145/// between them.
146///
147static bool CompareDiagLists(SourceManager &SourceMgr,
148 const_diag_iterator d1_begin,
149 const_diag_iterator d1_end,
150 const_diag_iterator d2_begin,
151 const_diag_iterator d2_end,
152 const char *Msg) {
153 TextDiagnosticBuffer::DiagList DiffList;
154
155 for (; d1_begin != d1_end; ++d1_begin) {
156 const std::string &Diag = d1_begin->second;
157 bool Found = false;
158
159 for (; d2_begin != d2_end; ++d2_begin) {
160 if (d2_begin->second.find(Diag) != std::string::npos ||
161 Diag.find(d2_begin->second) != std::string::npos) {
162 Found = true;
163 break;
164 }
165 }
166
167 if (!Found)
168 DiffList.push_back(std::make_pair(d1_begin->first, Diag));
169 }
170
171 return PrintProblem(SourceMgr, DiffList.begin(), DiffList.end(), Msg);
172}
173
174/// ReportCheckingResults - This compares the expected results to those that
175/// were actually reported. It emits any discrepencies. Return "true" if there
176/// were problems. Return "false" otherwise.
177///
178bool clang::ReportCheckingResults(TextDiagnosticBuffer &DiagClient,
179 const DiagList &ExpectedErrors,
180 const DiagList &ExpectedWarnings,
181 SourceManager &SourceMgr) {
182 // We want to capture the delta between what was expected and what was
183 // seen.
184 //
185 // Expected \ Seen - set expected but not seen
186 // Seen \ Expected - set seen but not expected
187 bool HadProblem = false;
188
189 // See if there were errors that were expected but not seen.
190 HadProblem |= CompareDiagLists(SourceMgr,
191 ExpectedErrors.begin(), ExpectedErrors.end(),
192 DiagClient.err_begin(), DiagClient.err_end(),
193 "Errors expected but not seen:");
194
195 // See if there were errors that were seen but not expected.
196 HadProblem |= CompareDiagLists(SourceMgr,
197 DiagClient.err_begin(), DiagClient.err_end(),
198 ExpectedErrors.begin(), ExpectedErrors.end(),
199 "Errors seen but not expected:");
200
201 // See if there were warnings that were expected but not seen.
202 HadProblem |= CompareDiagLists(SourceMgr,
203 ExpectedWarnings.begin(),
204 ExpectedWarnings.end(),
205 DiagClient.warn_begin(), DiagClient.warn_end(),
206 "Warnings expected but not seen:");
207
208 // See if there were warnings that were seen but not expected.
209 HadProblem |= CompareDiagLists(SourceMgr,
210 DiagClient.warn_begin(), DiagClient.warn_end(),
211 ExpectedWarnings.begin(),
212 ExpectedWarnings.end(),
213 "Warnings seen but not expected:");
214
215 return HadProblem;
216}