1f4a2713aSLionel Sambuc //===---- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ------===//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This is a concrete diagnostic client, which buffers the diagnostic messages.
11f4a2713aSLionel Sambuc //
12f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
13f4a2713aSLionel Sambuc
14f4a2713aSLionel Sambuc #include "clang/Frontend/VerifyDiagnosticConsumer.h"
15f4a2713aSLionel Sambuc #include "clang/Basic/CharInfo.h"
16f4a2713aSLionel Sambuc #include "clang/Basic/FileManager.h"
17f4a2713aSLionel Sambuc #include "clang/Frontend/FrontendDiagnostic.h"
18f4a2713aSLionel Sambuc #include "clang/Frontend/TextDiagnosticBuffer.h"
19f4a2713aSLionel Sambuc #include "clang/Lex/HeaderSearch.h"
20f4a2713aSLionel Sambuc #include "clang/Lex/Preprocessor.h"
21f4a2713aSLionel Sambuc #include "llvm/ADT/SmallString.h"
22f4a2713aSLionel Sambuc #include "llvm/Support/Regex.h"
23f4a2713aSLionel Sambuc #include "llvm/Support/raw_ostream.h"
24f4a2713aSLionel Sambuc
25f4a2713aSLionel Sambuc using namespace clang;
26f4a2713aSLionel Sambuc typedef VerifyDiagnosticConsumer::Directive Directive;
27f4a2713aSLionel Sambuc typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList;
28f4a2713aSLionel Sambuc typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData;
29f4a2713aSLionel Sambuc
VerifyDiagnosticConsumer(DiagnosticsEngine & Diags_)30*0a6a1f1dSLionel Sambuc VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
31*0a6a1f1dSLionel Sambuc : Diags(Diags_),
32*0a6a1f1dSLionel Sambuc PrimaryClient(Diags.getClient()), PrimaryClientOwner(Diags.takeClient()),
33*0a6a1f1dSLionel Sambuc Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(nullptr),
34*0a6a1f1dSLionel Sambuc LangOpts(nullptr), SrcManager(nullptr), ActiveSourceFiles(0),
35*0a6a1f1dSLionel Sambuc Status(HasNoDirectives)
36f4a2713aSLionel Sambuc {
37f4a2713aSLionel Sambuc if (Diags.hasSourceManager())
38f4a2713aSLionel Sambuc setSourceManager(Diags.getSourceManager());
39f4a2713aSLionel Sambuc }
40f4a2713aSLionel Sambuc
~VerifyDiagnosticConsumer()41f4a2713aSLionel Sambuc VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
42f4a2713aSLionel Sambuc assert(!ActiveSourceFiles && "Incomplete parsing of source files!");
43f4a2713aSLionel Sambuc assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!");
44*0a6a1f1dSLionel Sambuc SrcManager = nullptr;
45f4a2713aSLionel Sambuc CheckDiagnostics();
46*0a6a1f1dSLionel Sambuc Diags.takeClient().release();
47f4a2713aSLionel Sambuc }
48f4a2713aSLionel Sambuc
49f4a2713aSLionel Sambuc #ifndef NDEBUG
50f4a2713aSLionel Sambuc namespace {
51f4a2713aSLionel Sambuc class VerifyFileTracker : public PPCallbacks {
52f4a2713aSLionel Sambuc VerifyDiagnosticConsumer &Verify;
53f4a2713aSLionel Sambuc SourceManager &SM;
54f4a2713aSLionel Sambuc
55f4a2713aSLionel Sambuc public:
VerifyFileTracker(VerifyDiagnosticConsumer & Verify,SourceManager & SM)56f4a2713aSLionel Sambuc VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM)
57f4a2713aSLionel Sambuc : Verify(Verify), SM(SM) { }
58f4a2713aSLionel Sambuc
59f4a2713aSLionel Sambuc /// \brief Hook into the preprocessor and update the list of parsed
60f4a2713aSLionel Sambuc /// files when the preprocessor indicates a new file is entered.
FileChanged(SourceLocation Loc,FileChangeReason Reason,SrcMgr::CharacteristicKind FileType,FileID PrevFID)61f4a2713aSLionel Sambuc virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
62f4a2713aSLionel Sambuc SrcMgr::CharacteristicKind FileType,
63f4a2713aSLionel Sambuc FileID PrevFID) {
64f4a2713aSLionel Sambuc Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc),
65f4a2713aSLionel Sambuc VerifyDiagnosticConsumer::IsParsed);
66f4a2713aSLionel Sambuc }
67f4a2713aSLionel Sambuc };
68f4a2713aSLionel Sambuc } // End anonymous namespace.
69f4a2713aSLionel Sambuc #endif
70f4a2713aSLionel Sambuc
71f4a2713aSLionel Sambuc // DiagnosticConsumer interface.
72f4a2713aSLionel Sambuc
BeginSourceFile(const LangOptions & LangOpts,const Preprocessor * PP)73f4a2713aSLionel Sambuc void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts,
74f4a2713aSLionel Sambuc const Preprocessor *PP) {
75f4a2713aSLionel Sambuc // Attach comment handler on first invocation.
76f4a2713aSLionel Sambuc if (++ActiveSourceFiles == 1) {
77f4a2713aSLionel Sambuc if (PP) {
78f4a2713aSLionel Sambuc CurrentPreprocessor = PP;
79f4a2713aSLionel Sambuc this->LangOpts = &LangOpts;
80f4a2713aSLionel Sambuc setSourceManager(PP->getSourceManager());
81f4a2713aSLionel Sambuc const_cast<Preprocessor*>(PP)->addCommentHandler(this);
82f4a2713aSLionel Sambuc #ifndef NDEBUG
83f4a2713aSLionel Sambuc // Debug build tracks parsed files.
84*0a6a1f1dSLionel Sambuc const_cast<Preprocessor*>(PP)->addPPCallbacks(
85*0a6a1f1dSLionel Sambuc llvm::make_unique<VerifyFileTracker>(*this, *SrcManager));
86f4a2713aSLionel Sambuc #endif
87f4a2713aSLionel Sambuc }
88f4a2713aSLionel Sambuc }
89f4a2713aSLionel Sambuc
90f4a2713aSLionel Sambuc assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!");
91f4a2713aSLionel Sambuc PrimaryClient->BeginSourceFile(LangOpts, PP);
92f4a2713aSLionel Sambuc }
93f4a2713aSLionel Sambuc
EndSourceFile()94f4a2713aSLionel Sambuc void VerifyDiagnosticConsumer::EndSourceFile() {
95f4a2713aSLionel Sambuc assert(ActiveSourceFiles && "No active source files!");
96f4a2713aSLionel Sambuc PrimaryClient->EndSourceFile();
97f4a2713aSLionel Sambuc
98f4a2713aSLionel Sambuc // Detach comment handler once last active source file completed.
99f4a2713aSLionel Sambuc if (--ActiveSourceFiles == 0) {
100f4a2713aSLionel Sambuc if (CurrentPreprocessor)
101f4a2713aSLionel Sambuc const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this);
102f4a2713aSLionel Sambuc
103f4a2713aSLionel Sambuc // Check diagnostics once last file completed.
104f4a2713aSLionel Sambuc CheckDiagnostics();
105*0a6a1f1dSLionel Sambuc CurrentPreprocessor = nullptr;
106*0a6a1f1dSLionel Sambuc LangOpts = nullptr;
107f4a2713aSLionel Sambuc }
108f4a2713aSLionel Sambuc }
109f4a2713aSLionel Sambuc
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)110f4a2713aSLionel Sambuc void VerifyDiagnosticConsumer::HandleDiagnostic(
111f4a2713aSLionel Sambuc DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
112f4a2713aSLionel Sambuc if (Info.hasSourceManager()) {
113f4a2713aSLionel Sambuc // If this diagnostic is for a different source manager, ignore it.
114f4a2713aSLionel Sambuc if (SrcManager && &Info.getSourceManager() != SrcManager)
115f4a2713aSLionel Sambuc return;
116f4a2713aSLionel Sambuc
117f4a2713aSLionel Sambuc setSourceManager(Info.getSourceManager());
118f4a2713aSLionel Sambuc }
119f4a2713aSLionel Sambuc
120f4a2713aSLionel Sambuc #ifndef NDEBUG
121f4a2713aSLionel Sambuc // Debug build tracks unparsed files for possible
122f4a2713aSLionel Sambuc // unparsed expected-* directives.
123f4a2713aSLionel Sambuc if (SrcManager) {
124f4a2713aSLionel Sambuc SourceLocation Loc = Info.getLocation();
125f4a2713aSLionel Sambuc if (Loc.isValid()) {
126f4a2713aSLionel Sambuc ParsedStatus PS = IsUnparsed;
127f4a2713aSLionel Sambuc
128f4a2713aSLionel Sambuc Loc = SrcManager->getExpansionLoc(Loc);
129f4a2713aSLionel Sambuc FileID FID = SrcManager->getFileID(Loc);
130f4a2713aSLionel Sambuc
131f4a2713aSLionel Sambuc const FileEntry *FE = SrcManager->getFileEntryForID(FID);
132f4a2713aSLionel Sambuc if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) {
133f4a2713aSLionel Sambuc // If the file is a modules header file it shall not be parsed
134f4a2713aSLionel Sambuc // for expected-* directives.
135f4a2713aSLionel Sambuc HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo();
136f4a2713aSLionel Sambuc if (HS.findModuleForHeader(FE))
137f4a2713aSLionel Sambuc PS = IsUnparsedNoDirectives;
138f4a2713aSLionel Sambuc }
139f4a2713aSLionel Sambuc
140f4a2713aSLionel Sambuc UpdateParsedFileStatus(*SrcManager, FID, PS);
141f4a2713aSLionel Sambuc }
142f4a2713aSLionel Sambuc }
143f4a2713aSLionel Sambuc #endif
144f4a2713aSLionel Sambuc
145f4a2713aSLionel Sambuc // Send the diagnostic to the buffer, we will check it once we reach the end
146f4a2713aSLionel Sambuc // of the source file (or are destructed).
147f4a2713aSLionel Sambuc Buffer->HandleDiagnostic(DiagLevel, Info);
148f4a2713aSLionel Sambuc }
149f4a2713aSLionel Sambuc
150f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
151f4a2713aSLionel Sambuc // Checking diagnostics implementation.
152f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
153f4a2713aSLionel Sambuc
154f4a2713aSLionel Sambuc typedef TextDiagnosticBuffer::DiagList DiagList;
155f4a2713aSLionel Sambuc typedef TextDiagnosticBuffer::const_iterator const_diag_iterator;
156f4a2713aSLionel Sambuc
157f4a2713aSLionel Sambuc namespace {
158f4a2713aSLionel Sambuc
159f4a2713aSLionel Sambuc /// StandardDirective - Directive with string matching.
160f4a2713aSLionel Sambuc ///
161f4a2713aSLionel Sambuc class StandardDirective : public Directive {
162f4a2713aSLionel Sambuc public:
StandardDirective(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)163f4a2713aSLionel Sambuc StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
164*0a6a1f1dSLionel Sambuc bool MatchAnyLine, StringRef Text, unsigned Min,
165*0a6a1f1dSLionel Sambuc unsigned Max)
166*0a6a1f1dSLionel Sambuc : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) { }
167f4a2713aSLionel Sambuc
isValid(std::string & Error)168*0a6a1f1dSLionel Sambuc bool isValid(std::string &Error) override {
169f4a2713aSLionel Sambuc // all strings are considered valid; even empty ones
170f4a2713aSLionel Sambuc return true;
171f4a2713aSLionel Sambuc }
172f4a2713aSLionel Sambuc
match(StringRef S)173*0a6a1f1dSLionel Sambuc bool match(StringRef S) override {
174f4a2713aSLionel Sambuc return S.find(Text) != StringRef::npos;
175f4a2713aSLionel Sambuc }
176f4a2713aSLionel Sambuc };
177f4a2713aSLionel Sambuc
178f4a2713aSLionel Sambuc /// RegexDirective - Directive with regular-expression matching.
179f4a2713aSLionel Sambuc ///
180f4a2713aSLionel Sambuc class RegexDirective : public Directive {
181f4a2713aSLionel Sambuc public:
RegexDirective(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max,StringRef RegexStr)182f4a2713aSLionel Sambuc RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
183*0a6a1f1dSLionel Sambuc bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
184*0a6a1f1dSLionel Sambuc StringRef RegexStr)
185*0a6a1f1dSLionel Sambuc : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max),
186*0a6a1f1dSLionel Sambuc Regex(RegexStr) { }
187f4a2713aSLionel Sambuc
isValid(std::string & Error)188*0a6a1f1dSLionel Sambuc bool isValid(std::string &Error) override {
189f4a2713aSLionel Sambuc if (Regex.isValid(Error))
190f4a2713aSLionel Sambuc return true;
191f4a2713aSLionel Sambuc return false;
192f4a2713aSLionel Sambuc }
193f4a2713aSLionel Sambuc
match(StringRef S)194*0a6a1f1dSLionel Sambuc bool match(StringRef S) override {
195f4a2713aSLionel Sambuc return Regex.match(S);
196f4a2713aSLionel Sambuc }
197f4a2713aSLionel Sambuc
198f4a2713aSLionel Sambuc private:
199f4a2713aSLionel Sambuc llvm::Regex Regex;
200f4a2713aSLionel Sambuc };
201f4a2713aSLionel Sambuc
202f4a2713aSLionel Sambuc class ParseHelper
203f4a2713aSLionel Sambuc {
204f4a2713aSLionel Sambuc public:
ParseHelper(StringRef S)205f4a2713aSLionel Sambuc ParseHelper(StringRef S)
206*0a6a1f1dSLionel Sambuc : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(nullptr) {}
207f4a2713aSLionel Sambuc
208f4a2713aSLionel Sambuc // Return true if string literal is next.
Next(StringRef S)209f4a2713aSLionel Sambuc bool Next(StringRef S) {
210f4a2713aSLionel Sambuc P = C;
211f4a2713aSLionel Sambuc PEnd = C + S.size();
212f4a2713aSLionel Sambuc if (PEnd > End)
213f4a2713aSLionel Sambuc return false;
214f4a2713aSLionel Sambuc return !memcmp(P, S.data(), S.size());
215f4a2713aSLionel Sambuc }
216f4a2713aSLionel Sambuc
217f4a2713aSLionel Sambuc // Return true if number is next.
218f4a2713aSLionel Sambuc // Output N only if number is next.
Next(unsigned & N)219f4a2713aSLionel Sambuc bool Next(unsigned &N) {
220f4a2713aSLionel Sambuc unsigned TMP = 0;
221f4a2713aSLionel Sambuc P = C;
222f4a2713aSLionel Sambuc for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) {
223f4a2713aSLionel Sambuc TMP *= 10;
224f4a2713aSLionel Sambuc TMP += P[0] - '0';
225f4a2713aSLionel Sambuc }
226f4a2713aSLionel Sambuc if (P == C)
227f4a2713aSLionel Sambuc return false;
228f4a2713aSLionel Sambuc PEnd = P;
229f4a2713aSLionel Sambuc N = TMP;
230f4a2713aSLionel Sambuc return true;
231f4a2713aSLionel Sambuc }
232f4a2713aSLionel Sambuc
233f4a2713aSLionel Sambuc // Return true if string literal is found.
234f4a2713aSLionel Sambuc // When true, P marks begin-position of S in content.
Search(StringRef S,bool EnsureStartOfWord=false)235f4a2713aSLionel Sambuc bool Search(StringRef S, bool EnsureStartOfWord = false) {
236f4a2713aSLionel Sambuc do {
237f4a2713aSLionel Sambuc P = std::search(C, End, S.begin(), S.end());
238f4a2713aSLionel Sambuc PEnd = P + S.size();
239f4a2713aSLionel Sambuc if (P == End)
240f4a2713aSLionel Sambuc break;
241f4a2713aSLionel Sambuc if (!EnsureStartOfWord
242f4a2713aSLionel Sambuc // Check if string literal starts a new word.
243f4a2713aSLionel Sambuc || P == Begin || isWhitespace(P[-1])
244*0a6a1f1dSLionel Sambuc // Or it could be preceded by the start of a comment.
245f4a2713aSLionel Sambuc || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*')
246f4a2713aSLionel Sambuc && P[-2] == '/'))
247f4a2713aSLionel Sambuc return true;
248f4a2713aSLionel Sambuc // Otherwise, skip and search again.
249f4a2713aSLionel Sambuc } while (Advance());
250f4a2713aSLionel Sambuc return false;
251f4a2713aSLionel Sambuc }
252f4a2713aSLionel Sambuc
253*0a6a1f1dSLionel Sambuc // Return true if a CloseBrace that closes the OpenBrace at the current nest
254*0a6a1f1dSLionel Sambuc // level is found. When true, P marks begin-position of CloseBrace.
SearchClosingBrace(StringRef OpenBrace,StringRef CloseBrace)255*0a6a1f1dSLionel Sambuc bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) {
256*0a6a1f1dSLionel Sambuc unsigned Depth = 1;
257*0a6a1f1dSLionel Sambuc P = C;
258*0a6a1f1dSLionel Sambuc while (P < End) {
259*0a6a1f1dSLionel Sambuc StringRef S(P, End - P);
260*0a6a1f1dSLionel Sambuc if (S.startswith(OpenBrace)) {
261*0a6a1f1dSLionel Sambuc ++Depth;
262*0a6a1f1dSLionel Sambuc P += OpenBrace.size();
263*0a6a1f1dSLionel Sambuc } else if (S.startswith(CloseBrace)) {
264*0a6a1f1dSLionel Sambuc --Depth;
265*0a6a1f1dSLionel Sambuc if (Depth == 0) {
266*0a6a1f1dSLionel Sambuc PEnd = P + CloseBrace.size();
267*0a6a1f1dSLionel Sambuc return true;
268*0a6a1f1dSLionel Sambuc }
269*0a6a1f1dSLionel Sambuc P += CloseBrace.size();
270*0a6a1f1dSLionel Sambuc } else {
271*0a6a1f1dSLionel Sambuc ++P;
272*0a6a1f1dSLionel Sambuc }
273*0a6a1f1dSLionel Sambuc }
274*0a6a1f1dSLionel Sambuc return false;
275*0a6a1f1dSLionel Sambuc }
276*0a6a1f1dSLionel Sambuc
277f4a2713aSLionel Sambuc // Advance 1-past previous next/search.
278f4a2713aSLionel Sambuc // Behavior is undefined if previous next/search failed.
Advance()279f4a2713aSLionel Sambuc bool Advance() {
280f4a2713aSLionel Sambuc C = PEnd;
281f4a2713aSLionel Sambuc return C < End;
282f4a2713aSLionel Sambuc }
283f4a2713aSLionel Sambuc
284f4a2713aSLionel Sambuc // Skip zero or more whitespace.
SkipWhitespace()285f4a2713aSLionel Sambuc void SkipWhitespace() {
286f4a2713aSLionel Sambuc for (; C < End && isWhitespace(*C); ++C)
287f4a2713aSLionel Sambuc ;
288f4a2713aSLionel Sambuc }
289f4a2713aSLionel Sambuc
290f4a2713aSLionel Sambuc // Return true if EOF reached.
Done()291f4a2713aSLionel Sambuc bool Done() {
292f4a2713aSLionel Sambuc return !(C < End);
293f4a2713aSLionel Sambuc }
294f4a2713aSLionel Sambuc
295f4a2713aSLionel Sambuc const char * const Begin; // beginning of expected content
296f4a2713aSLionel Sambuc const char * const End; // end of expected content (1-past)
297f4a2713aSLionel Sambuc const char *C; // position of next char in content
298f4a2713aSLionel Sambuc const char *P;
299f4a2713aSLionel Sambuc
300f4a2713aSLionel Sambuc private:
301f4a2713aSLionel Sambuc const char *PEnd; // previous next/search subject end (1-past)
302f4a2713aSLionel Sambuc };
303f4a2713aSLionel Sambuc
304f4a2713aSLionel Sambuc } // namespace anonymous
305f4a2713aSLionel Sambuc
306f4a2713aSLionel Sambuc /// ParseDirective - Go through the comment and see if it indicates expected
307f4a2713aSLionel Sambuc /// diagnostics. If so, then put them in the appropriate directive list.
308f4a2713aSLionel Sambuc ///
309f4a2713aSLionel Sambuc /// Returns true if any valid directives were found.
ParseDirective(StringRef S,ExpectedData * ED,SourceManager & SM,Preprocessor * PP,SourceLocation Pos,VerifyDiagnosticConsumer::DirectiveStatus & Status)310f4a2713aSLionel Sambuc static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
311f4a2713aSLionel Sambuc Preprocessor *PP, SourceLocation Pos,
312f4a2713aSLionel Sambuc VerifyDiagnosticConsumer::DirectiveStatus &Status) {
313f4a2713aSLionel Sambuc DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
314f4a2713aSLionel Sambuc
315f4a2713aSLionel Sambuc // A single comment may contain multiple directives.
316f4a2713aSLionel Sambuc bool FoundDirective = false;
317f4a2713aSLionel Sambuc for (ParseHelper PH(S); !PH.Done();) {
318f4a2713aSLionel Sambuc // Search for token: expected
319f4a2713aSLionel Sambuc if (!PH.Search("expected", true))
320f4a2713aSLionel Sambuc break;
321f4a2713aSLionel Sambuc PH.Advance();
322f4a2713aSLionel Sambuc
323f4a2713aSLionel Sambuc // Next token: -
324f4a2713aSLionel Sambuc if (!PH.Next("-"))
325f4a2713aSLionel Sambuc continue;
326f4a2713aSLionel Sambuc PH.Advance();
327f4a2713aSLionel Sambuc
328f4a2713aSLionel Sambuc // Next token: { error | warning | note }
329*0a6a1f1dSLionel Sambuc DirectiveList *DL = nullptr;
330f4a2713aSLionel Sambuc if (PH.Next("error"))
331*0a6a1f1dSLionel Sambuc DL = ED ? &ED->Errors : nullptr;
332f4a2713aSLionel Sambuc else if (PH.Next("warning"))
333*0a6a1f1dSLionel Sambuc DL = ED ? &ED->Warnings : nullptr;
334*0a6a1f1dSLionel Sambuc else if (PH.Next("remark"))
335*0a6a1f1dSLionel Sambuc DL = ED ? &ED->Remarks : nullptr;
336f4a2713aSLionel Sambuc else if (PH.Next("note"))
337*0a6a1f1dSLionel Sambuc DL = ED ? &ED->Notes : nullptr;
338f4a2713aSLionel Sambuc else if (PH.Next("no-diagnostics")) {
339f4a2713aSLionel Sambuc if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
340f4a2713aSLionel Sambuc Diags.Report(Pos, diag::err_verify_invalid_no_diags)
341f4a2713aSLionel Sambuc << /*IsExpectedNoDiagnostics=*/true;
342f4a2713aSLionel Sambuc else
343f4a2713aSLionel Sambuc Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
344f4a2713aSLionel Sambuc continue;
345f4a2713aSLionel Sambuc } else
346f4a2713aSLionel Sambuc continue;
347f4a2713aSLionel Sambuc PH.Advance();
348f4a2713aSLionel Sambuc
349f4a2713aSLionel Sambuc if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
350f4a2713aSLionel Sambuc Diags.Report(Pos, diag::err_verify_invalid_no_diags)
351f4a2713aSLionel Sambuc << /*IsExpectedNoDiagnostics=*/false;
352f4a2713aSLionel Sambuc continue;
353f4a2713aSLionel Sambuc }
354f4a2713aSLionel Sambuc Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
355f4a2713aSLionel Sambuc
356f4a2713aSLionel Sambuc // If a directive has been found but we're not interested
357f4a2713aSLionel Sambuc // in storing the directive information, return now.
358f4a2713aSLionel Sambuc if (!DL)
359f4a2713aSLionel Sambuc return true;
360f4a2713aSLionel Sambuc
361f4a2713aSLionel Sambuc // Default directive kind.
362f4a2713aSLionel Sambuc bool RegexKind = false;
363f4a2713aSLionel Sambuc const char* KindStr = "string";
364f4a2713aSLionel Sambuc
365f4a2713aSLionel Sambuc // Next optional token: -
366f4a2713aSLionel Sambuc if (PH.Next("-re")) {
367f4a2713aSLionel Sambuc PH.Advance();
368f4a2713aSLionel Sambuc RegexKind = true;
369f4a2713aSLionel Sambuc KindStr = "regex";
370f4a2713aSLionel Sambuc }
371f4a2713aSLionel Sambuc
372f4a2713aSLionel Sambuc // Next optional token: @
373f4a2713aSLionel Sambuc SourceLocation ExpectedLoc;
374*0a6a1f1dSLionel Sambuc bool MatchAnyLine = false;
375f4a2713aSLionel Sambuc if (!PH.Next("@")) {
376f4a2713aSLionel Sambuc ExpectedLoc = Pos;
377f4a2713aSLionel Sambuc } else {
378f4a2713aSLionel Sambuc PH.Advance();
379f4a2713aSLionel Sambuc unsigned Line = 0;
380f4a2713aSLionel Sambuc bool FoundPlus = PH.Next("+");
381f4a2713aSLionel Sambuc if (FoundPlus || PH.Next("-")) {
382f4a2713aSLionel Sambuc // Relative to current line.
383f4a2713aSLionel Sambuc PH.Advance();
384f4a2713aSLionel Sambuc bool Invalid = false;
385f4a2713aSLionel Sambuc unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
386f4a2713aSLionel Sambuc if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
387f4a2713aSLionel Sambuc if (FoundPlus) ExpectedLine += Line;
388f4a2713aSLionel Sambuc else ExpectedLine -= Line;
389f4a2713aSLionel Sambuc ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
390f4a2713aSLionel Sambuc }
391f4a2713aSLionel Sambuc } else if (PH.Next(Line)) {
392f4a2713aSLionel Sambuc // Absolute line number.
393f4a2713aSLionel Sambuc if (Line > 0)
394f4a2713aSLionel Sambuc ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
395f4a2713aSLionel Sambuc } else if (PP && PH.Search(":")) {
396f4a2713aSLionel Sambuc // Specific source file.
397f4a2713aSLionel Sambuc StringRef Filename(PH.C, PH.P-PH.C);
398f4a2713aSLionel Sambuc PH.Advance();
399f4a2713aSLionel Sambuc
400f4a2713aSLionel Sambuc // Lookup file via Preprocessor, like a #include.
401f4a2713aSLionel Sambuc const DirectoryLookup *CurDir;
402*0a6a1f1dSLionel Sambuc const FileEntry *FE =
403*0a6a1f1dSLionel Sambuc PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir,
404*0a6a1f1dSLionel Sambuc nullptr, nullptr, nullptr);
405f4a2713aSLionel Sambuc if (!FE) {
406f4a2713aSLionel Sambuc Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
407f4a2713aSLionel Sambuc diag::err_verify_missing_file) << Filename << KindStr;
408f4a2713aSLionel Sambuc continue;
409f4a2713aSLionel Sambuc }
410f4a2713aSLionel Sambuc
411f4a2713aSLionel Sambuc if (SM.translateFile(FE).isInvalid())
412f4a2713aSLionel Sambuc SM.createFileID(FE, Pos, SrcMgr::C_User);
413f4a2713aSLionel Sambuc
414f4a2713aSLionel Sambuc if (PH.Next(Line) && Line > 0)
415f4a2713aSLionel Sambuc ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
416*0a6a1f1dSLionel Sambuc else if (PH.Next("*")) {
417*0a6a1f1dSLionel Sambuc MatchAnyLine = true;
418*0a6a1f1dSLionel Sambuc ExpectedLoc = SM.translateFileLineCol(FE, 1, 1);
419*0a6a1f1dSLionel Sambuc }
420f4a2713aSLionel Sambuc }
421f4a2713aSLionel Sambuc
422f4a2713aSLionel Sambuc if (ExpectedLoc.isInvalid()) {
423f4a2713aSLionel Sambuc Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
424f4a2713aSLionel Sambuc diag::err_verify_missing_line) << KindStr;
425f4a2713aSLionel Sambuc continue;
426f4a2713aSLionel Sambuc }
427f4a2713aSLionel Sambuc PH.Advance();
428f4a2713aSLionel Sambuc }
429f4a2713aSLionel Sambuc
430f4a2713aSLionel Sambuc // Skip optional whitespace.
431f4a2713aSLionel Sambuc PH.SkipWhitespace();
432f4a2713aSLionel Sambuc
433f4a2713aSLionel Sambuc // Next optional token: positive integer or a '+'.
434f4a2713aSLionel Sambuc unsigned Min = 1;
435f4a2713aSLionel Sambuc unsigned Max = 1;
436f4a2713aSLionel Sambuc if (PH.Next(Min)) {
437f4a2713aSLionel Sambuc PH.Advance();
438f4a2713aSLionel Sambuc // A positive integer can be followed by a '+' meaning min
439f4a2713aSLionel Sambuc // or more, or by a '-' meaning a range from min to max.
440f4a2713aSLionel Sambuc if (PH.Next("+")) {
441f4a2713aSLionel Sambuc Max = Directive::MaxCount;
442f4a2713aSLionel Sambuc PH.Advance();
443f4a2713aSLionel Sambuc } else if (PH.Next("-")) {
444f4a2713aSLionel Sambuc PH.Advance();
445f4a2713aSLionel Sambuc if (!PH.Next(Max) || Max < Min) {
446f4a2713aSLionel Sambuc Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
447f4a2713aSLionel Sambuc diag::err_verify_invalid_range) << KindStr;
448f4a2713aSLionel Sambuc continue;
449f4a2713aSLionel Sambuc }
450f4a2713aSLionel Sambuc PH.Advance();
451f4a2713aSLionel Sambuc } else {
452f4a2713aSLionel Sambuc Max = Min;
453f4a2713aSLionel Sambuc }
454f4a2713aSLionel Sambuc } else if (PH.Next("+")) {
455f4a2713aSLionel Sambuc // '+' on its own means "1 or more".
456f4a2713aSLionel Sambuc Max = Directive::MaxCount;
457f4a2713aSLionel Sambuc PH.Advance();
458f4a2713aSLionel Sambuc }
459f4a2713aSLionel Sambuc
460f4a2713aSLionel Sambuc // Skip optional whitespace.
461f4a2713aSLionel Sambuc PH.SkipWhitespace();
462f4a2713aSLionel Sambuc
463f4a2713aSLionel Sambuc // Next token: {{
464f4a2713aSLionel Sambuc if (!PH.Next("{{")) {
465f4a2713aSLionel Sambuc Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
466f4a2713aSLionel Sambuc diag::err_verify_missing_start) << KindStr;
467f4a2713aSLionel Sambuc continue;
468f4a2713aSLionel Sambuc }
469f4a2713aSLionel Sambuc PH.Advance();
470f4a2713aSLionel Sambuc const char* const ContentBegin = PH.C; // mark content begin
471f4a2713aSLionel Sambuc
472f4a2713aSLionel Sambuc // Search for token: }}
473*0a6a1f1dSLionel Sambuc if (!PH.SearchClosingBrace("{{", "}}")) {
474f4a2713aSLionel Sambuc Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
475f4a2713aSLionel Sambuc diag::err_verify_missing_end) << KindStr;
476f4a2713aSLionel Sambuc continue;
477f4a2713aSLionel Sambuc }
478f4a2713aSLionel Sambuc const char* const ContentEnd = PH.P; // mark content end
479f4a2713aSLionel Sambuc PH.Advance();
480f4a2713aSLionel Sambuc
481f4a2713aSLionel Sambuc // Build directive text; convert \n to newlines.
482f4a2713aSLionel Sambuc std::string Text;
483f4a2713aSLionel Sambuc StringRef NewlineStr = "\\n";
484f4a2713aSLionel Sambuc StringRef Content(ContentBegin, ContentEnd-ContentBegin);
485f4a2713aSLionel Sambuc size_t CPos = 0;
486f4a2713aSLionel Sambuc size_t FPos;
487f4a2713aSLionel Sambuc while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
488f4a2713aSLionel Sambuc Text += Content.substr(CPos, FPos-CPos);
489f4a2713aSLionel Sambuc Text += '\n';
490f4a2713aSLionel Sambuc CPos = FPos + NewlineStr.size();
491f4a2713aSLionel Sambuc }
492f4a2713aSLionel Sambuc if (Text.empty())
493f4a2713aSLionel Sambuc Text.assign(ContentBegin, ContentEnd);
494f4a2713aSLionel Sambuc
495*0a6a1f1dSLionel Sambuc // Check that regex directives contain at least one regex.
496*0a6a1f1dSLionel Sambuc if (RegexKind && Text.find("{{") == StringRef::npos) {
497*0a6a1f1dSLionel Sambuc Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
498*0a6a1f1dSLionel Sambuc diag::err_verify_missing_regex) << Text;
499*0a6a1f1dSLionel Sambuc return false;
500*0a6a1f1dSLionel Sambuc }
501*0a6a1f1dSLionel Sambuc
502f4a2713aSLionel Sambuc // Construct new directive.
503*0a6a1f1dSLionel Sambuc std::unique_ptr<Directive> D = Directive::create(
504*0a6a1f1dSLionel Sambuc RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);
505*0a6a1f1dSLionel Sambuc
506f4a2713aSLionel Sambuc std::string Error;
507f4a2713aSLionel Sambuc if (D->isValid(Error)) {
508*0a6a1f1dSLionel Sambuc DL->push_back(std::move(D));
509f4a2713aSLionel Sambuc FoundDirective = true;
510f4a2713aSLionel Sambuc } else {
511f4a2713aSLionel Sambuc Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
512f4a2713aSLionel Sambuc diag::err_verify_invalid_content)
513f4a2713aSLionel Sambuc << KindStr << Error;
514f4a2713aSLionel Sambuc }
515f4a2713aSLionel Sambuc }
516f4a2713aSLionel Sambuc
517f4a2713aSLionel Sambuc return FoundDirective;
518f4a2713aSLionel Sambuc }
519f4a2713aSLionel Sambuc
520f4a2713aSLionel Sambuc /// HandleComment - Hook into the preprocessor and extract comments containing
521f4a2713aSLionel Sambuc /// expected errors and warnings.
HandleComment(Preprocessor & PP,SourceRange Comment)522f4a2713aSLionel Sambuc bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
523f4a2713aSLionel Sambuc SourceRange Comment) {
524f4a2713aSLionel Sambuc SourceManager &SM = PP.getSourceManager();
525f4a2713aSLionel Sambuc
526f4a2713aSLionel Sambuc // If this comment is for a different source manager, ignore it.
527f4a2713aSLionel Sambuc if (SrcManager && &SM != SrcManager)
528f4a2713aSLionel Sambuc return false;
529f4a2713aSLionel Sambuc
530f4a2713aSLionel Sambuc SourceLocation CommentBegin = Comment.getBegin();
531f4a2713aSLionel Sambuc
532f4a2713aSLionel Sambuc const char *CommentRaw = SM.getCharacterData(CommentBegin);
533f4a2713aSLionel Sambuc StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw);
534f4a2713aSLionel Sambuc
535f4a2713aSLionel Sambuc if (C.empty())
536f4a2713aSLionel Sambuc return false;
537f4a2713aSLionel Sambuc
538f4a2713aSLionel Sambuc // Fold any "\<EOL>" sequences
539f4a2713aSLionel Sambuc size_t loc = C.find('\\');
540f4a2713aSLionel Sambuc if (loc == StringRef::npos) {
541f4a2713aSLionel Sambuc ParseDirective(C, &ED, SM, &PP, CommentBegin, Status);
542f4a2713aSLionel Sambuc return false;
543f4a2713aSLionel Sambuc }
544f4a2713aSLionel Sambuc
545f4a2713aSLionel Sambuc std::string C2;
546f4a2713aSLionel Sambuc C2.reserve(C.size());
547f4a2713aSLionel Sambuc
548f4a2713aSLionel Sambuc for (size_t last = 0;; loc = C.find('\\', last)) {
549f4a2713aSLionel Sambuc if (loc == StringRef::npos || loc == C.size()) {
550f4a2713aSLionel Sambuc C2 += C.substr(last);
551f4a2713aSLionel Sambuc break;
552f4a2713aSLionel Sambuc }
553f4a2713aSLionel Sambuc C2 += C.substr(last, loc-last);
554f4a2713aSLionel Sambuc last = loc + 1;
555f4a2713aSLionel Sambuc
556f4a2713aSLionel Sambuc if (C[last] == '\n' || C[last] == '\r') {
557f4a2713aSLionel Sambuc ++last;
558f4a2713aSLionel Sambuc
559f4a2713aSLionel Sambuc // Escape \r\n or \n\r, but not \n\n.
560f4a2713aSLionel Sambuc if (last < C.size())
561f4a2713aSLionel Sambuc if (C[last] == '\n' || C[last] == '\r')
562f4a2713aSLionel Sambuc if (C[last] != C[last-1])
563f4a2713aSLionel Sambuc ++last;
564f4a2713aSLionel Sambuc } else {
565f4a2713aSLionel Sambuc // This was just a normal backslash.
566f4a2713aSLionel Sambuc C2 += '\\';
567f4a2713aSLionel Sambuc }
568f4a2713aSLionel Sambuc }
569f4a2713aSLionel Sambuc
570f4a2713aSLionel Sambuc if (!C2.empty())
571f4a2713aSLionel Sambuc ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status);
572f4a2713aSLionel Sambuc return false;
573f4a2713aSLionel Sambuc }
574f4a2713aSLionel Sambuc
575f4a2713aSLionel Sambuc #ifndef NDEBUG
576f4a2713aSLionel Sambuc /// \brief Lex the specified source file to determine whether it contains
577f4a2713aSLionel Sambuc /// any expected-* directives. As a Lexer is used rather than a full-blown
578f4a2713aSLionel Sambuc /// Preprocessor, directives inside skipped #if blocks will still be found.
579f4a2713aSLionel Sambuc ///
580f4a2713aSLionel Sambuc /// \return true if any directives were found.
findDirectives(SourceManager & SM,FileID FID,const LangOptions & LangOpts)581f4a2713aSLionel Sambuc static bool findDirectives(SourceManager &SM, FileID FID,
582f4a2713aSLionel Sambuc const LangOptions &LangOpts) {
583f4a2713aSLionel Sambuc // Create a raw lexer to pull all the comments out of FID.
584f4a2713aSLionel Sambuc if (FID.isInvalid())
585f4a2713aSLionel Sambuc return false;
586f4a2713aSLionel Sambuc
587f4a2713aSLionel Sambuc // Create a lexer to lex all the tokens of the main file in raw mode.
588f4a2713aSLionel Sambuc const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID);
589f4a2713aSLionel Sambuc Lexer RawLex(FID, FromFile, SM, LangOpts);
590f4a2713aSLionel Sambuc
591f4a2713aSLionel Sambuc // Return comments as tokens, this is how we find expected diagnostics.
592f4a2713aSLionel Sambuc RawLex.SetCommentRetentionState(true);
593f4a2713aSLionel Sambuc
594f4a2713aSLionel Sambuc Token Tok;
595f4a2713aSLionel Sambuc Tok.setKind(tok::comment);
596f4a2713aSLionel Sambuc VerifyDiagnosticConsumer::DirectiveStatus Status =
597f4a2713aSLionel Sambuc VerifyDiagnosticConsumer::HasNoDirectives;
598f4a2713aSLionel Sambuc while (Tok.isNot(tok::eof)) {
599f4a2713aSLionel Sambuc RawLex.LexFromRawLexer(Tok);
600f4a2713aSLionel Sambuc if (!Tok.is(tok::comment)) continue;
601f4a2713aSLionel Sambuc
602f4a2713aSLionel Sambuc std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts);
603f4a2713aSLionel Sambuc if (Comment.empty()) continue;
604f4a2713aSLionel Sambuc
605f4a2713aSLionel Sambuc // Find first directive.
606*0a6a1f1dSLionel Sambuc if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(),
607*0a6a1f1dSLionel Sambuc Status))
608f4a2713aSLionel Sambuc return true;
609f4a2713aSLionel Sambuc }
610f4a2713aSLionel Sambuc return false;
611f4a2713aSLionel Sambuc }
612f4a2713aSLionel Sambuc #endif // !NDEBUG
613f4a2713aSLionel Sambuc
614f4a2713aSLionel Sambuc /// \brief Takes a list of diagnostics that have been generated but not matched
615f4a2713aSLionel Sambuc /// by an expected-* directive and produces a diagnostic to the user from this.
PrintUnexpected(DiagnosticsEngine & Diags,SourceManager * SourceMgr,const_diag_iterator diag_begin,const_diag_iterator diag_end,const char * Kind)616f4a2713aSLionel Sambuc static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr,
617f4a2713aSLionel Sambuc const_diag_iterator diag_begin,
618f4a2713aSLionel Sambuc const_diag_iterator diag_end,
619f4a2713aSLionel Sambuc const char *Kind) {
620f4a2713aSLionel Sambuc if (diag_begin == diag_end) return 0;
621f4a2713aSLionel Sambuc
622f4a2713aSLionel Sambuc SmallString<256> Fmt;
623f4a2713aSLionel Sambuc llvm::raw_svector_ostream OS(Fmt);
624f4a2713aSLionel Sambuc for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) {
625f4a2713aSLionel Sambuc if (I->first.isInvalid() || !SourceMgr)
626f4a2713aSLionel Sambuc OS << "\n (frontend)";
627f4a2713aSLionel Sambuc else {
628f4a2713aSLionel Sambuc OS << "\n ";
629f4a2713aSLionel Sambuc if (const FileEntry *File = SourceMgr->getFileEntryForID(
630f4a2713aSLionel Sambuc SourceMgr->getFileID(I->first)))
631f4a2713aSLionel Sambuc OS << " File " << File->getName();
632f4a2713aSLionel Sambuc OS << " Line " << SourceMgr->getPresumedLineNumber(I->first);
633f4a2713aSLionel Sambuc }
634f4a2713aSLionel Sambuc OS << ": " << I->second;
635f4a2713aSLionel Sambuc }
636f4a2713aSLionel Sambuc
637f4a2713aSLionel Sambuc Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
638f4a2713aSLionel Sambuc << Kind << /*Unexpected=*/true << OS.str();
639f4a2713aSLionel Sambuc return std::distance(diag_begin, diag_end);
640f4a2713aSLionel Sambuc }
641f4a2713aSLionel Sambuc
642f4a2713aSLionel Sambuc /// \brief Takes a list of diagnostics that were expected to have been generated
643f4a2713aSLionel Sambuc /// but were not and produces a diagnostic to the user from this.
PrintExpected(DiagnosticsEngine & Diags,SourceManager & SourceMgr,std::vector<Directive * > & DL,const char * Kind)644*0a6a1f1dSLionel Sambuc static unsigned PrintExpected(DiagnosticsEngine &Diags,
645*0a6a1f1dSLionel Sambuc SourceManager &SourceMgr,
646*0a6a1f1dSLionel Sambuc std::vector<Directive *> &DL, const char *Kind) {
647f4a2713aSLionel Sambuc if (DL.empty())
648f4a2713aSLionel Sambuc return 0;
649f4a2713aSLionel Sambuc
650f4a2713aSLionel Sambuc SmallString<256> Fmt;
651f4a2713aSLionel Sambuc llvm::raw_svector_ostream OS(Fmt);
652*0a6a1f1dSLionel Sambuc for (auto *DirPtr : DL) {
653*0a6a1f1dSLionel Sambuc Directive &D = *DirPtr;
654*0a6a1f1dSLionel Sambuc OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc);
655*0a6a1f1dSLionel Sambuc if (D.MatchAnyLine)
656*0a6a1f1dSLionel Sambuc OS << " Line *";
657*0a6a1f1dSLionel Sambuc else
658*0a6a1f1dSLionel Sambuc OS << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
659f4a2713aSLionel Sambuc if (D.DirectiveLoc != D.DiagnosticLoc)
660f4a2713aSLionel Sambuc OS << " (directive at "
661f4a2713aSLionel Sambuc << SourceMgr.getFilename(D.DirectiveLoc) << ':'
662f4a2713aSLionel Sambuc << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')';
663f4a2713aSLionel Sambuc OS << ": " << D.Text;
664f4a2713aSLionel Sambuc }
665f4a2713aSLionel Sambuc
666f4a2713aSLionel Sambuc Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
667f4a2713aSLionel Sambuc << Kind << /*Unexpected=*/false << OS.str();
668f4a2713aSLionel Sambuc return DL.size();
669f4a2713aSLionel Sambuc }
670f4a2713aSLionel Sambuc
671f4a2713aSLionel Sambuc /// \brief Determine whether two source locations come from the same file.
IsFromSameFile(SourceManager & SM,SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc)672f4a2713aSLionel Sambuc static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
673f4a2713aSLionel Sambuc SourceLocation DiagnosticLoc) {
674f4a2713aSLionel Sambuc while (DiagnosticLoc.isMacroID())
675f4a2713aSLionel Sambuc DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc);
676f4a2713aSLionel Sambuc
677f4a2713aSLionel Sambuc if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc))
678f4a2713aSLionel Sambuc return true;
679f4a2713aSLionel Sambuc
680f4a2713aSLionel Sambuc const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc));
681f4a2713aSLionel Sambuc if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc))
682f4a2713aSLionel Sambuc return true;
683f4a2713aSLionel Sambuc
684f4a2713aSLionel Sambuc return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
685f4a2713aSLionel Sambuc }
686f4a2713aSLionel Sambuc
687f4a2713aSLionel Sambuc /// CheckLists - Compare expected to seen diagnostic lists and return the
688f4a2713aSLionel Sambuc /// the difference between them.
689f4a2713aSLionel Sambuc ///
CheckLists(DiagnosticsEngine & Diags,SourceManager & SourceMgr,const char * Label,DirectiveList & Left,const_diag_iterator d2_begin,const_diag_iterator d2_end)690f4a2713aSLionel Sambuc static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
691f4a2713aSLionel Sambuc const char *Label,
692f4a2713aSLionel Sambuc DirectiveList &Left,
693f4a2713aSLionel Sambuc const_diag_iterator d2_begin,
694f4a2713aSLionel Sambuc const_diag_iterator d2_end) {
695*0a6a1f1dSLionel Sambuc std::vector<Directive *> LeftOnly;
696f4a2713aSLionel Sambuc DiagList Right(d2_begin, d2_end);
697f4a2713aSLionel Sambuc
698*0a6a1f1dSLionel Sambuc for (auto &Owner : Left) {
699*0a6a1f1dSLionel Sambuc Directive &D = *Owner;
700f4a2713aSLionel Sambuc unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
701f4a2713aSLionel Sambuc
702f4a2713aSLionel Sambuc for (unsigned i = 0; i < D.Max; ++i) {
703f4a2713aSLionel Sambuc DiagList::iterator II, IE;
704f4a2713aSLionel Sambuc for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
705*0a6a1f1dSLionel Sambuc if (!D.MatchAnyLine) {
706f4a2713aSLionel Sambuc unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
707f4a2713aSLionel Sambuc if (LineNo1 != LineNo2)
708f4a2713aSLionel Sambuc continue;
709*0a6a1f1dSLionel Sambuc }
710f4a2713aSLionel Sambuc
711f4a2713aSLionel Sambuc if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
712f4a2713aSLionel Sambuc continue;
713f4a2713aSLionel Sambuc
714f4a2713aSLionel Sambuc const std::string &RightText = II->second;
715f4a2713aSLionel Sambuc if (D.match(RightText))
716f4a2713aSLionel Sambuc break;
717f4a2713aSLionel Sambuc }
718f4a2713aSLionel Sambuc if (II == IE) {
719f4a2713aSLionel Sambuc // Not found.
720f4a2713aSLionel Sambuc if (i >= D.Min) break;
721*0a6a1f1dSLionel Sambuc LeftOnly.push_back(&D);
722f4a2713aSLionel Sambuc } else {
723f4a2713aSLionel Sambuc // Found. The same cannot be found twice.
724f4a2713aSLionel Sambuc Right.erase(II);
725f4a2713aSLionel Sambuc }
726f4a2713aSLionel Sambuc }
727f4a2713aSLionel Sambuc }
728f4a2713aSLionel Sambuc // Now all that's left in Right are those that were not matched.
729f4a2713aSLionel Sambuc unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
730f4a2713aSLionel Sambuc num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
731f4a2713aSLionel Sambuc return num;
732f4a2713aSLionel Sambuc }
733f4a2713aSLionel Sambuc
734f4a2713aSLionel Sambuc /// CheckResults - This compares the expected results to those that
735f4a2713aSLionel Sambuc /// were actually reported. It emits any discrepencies. Return "true" if there
736f4a2713aSLionel Sambuc /// were problems. Return "false" otherwise.
737f4a2713aSLionel Sambuc ///
CheckResults(DiagnosticsEngine & Diags,SourceManager & SourceMgr,const TextDiagnosticBuffer & Buffer,ExpectedData & ED)738f4a2713aSLionel Sambuc static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
739f4a2713aSLionel Sambuc const TextDiagnosticBuffer &Buffer,
740f4a2713aSLionel Sambuc ExpectedData &ED) {
741f4a2713aSLionel Sambuc // We want to capture the delta between what was expected and what was
742f4a2713aSLionel Sambuc // seen.
743f4a2713aSLionel Sambuc //
744f4a2713aSLionel Sambuc // Expected \ Seen - set expected but not seen
745f4a2713aSLionel Sambuc // Seen \ Expected - set seen but not expected
746f4a2713aSLionel Sambuc unsigned NumProblems = 0;
747f4a2713aSLionel Sambuc
748f4a2713aSLionel Sambuc // See if there are error mismatches.
749f4a2713aSLionel Sambuc NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors,
750f4a2713aSLionel Sambuc Buffer.err_begin(), Buffer.err_end());
751f4a2713aSLionel Sambuc
752f4a2713aSLionel Sambuc // See if there are warning mismatches.
753f4a2713aSLionel Sambuc NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings,
754f4a2713aSLionel Sambuc Buffer.warn_begin(), Buffer.warn_end());
755f4a2713aSLionel Sambuc
756*0a6a1f1dSLionel Sambuc // See if there are remark mismatches.
757*0a6a1f1dSLionel Sambuc NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks,
758*0a6a1f1dSLionel Sambuc Buffer.remark_begin(), Buffer.remark_end());
759*0a6a1f1dSLionel Sambuc
760f4a2713aSLionel Sambuc // See if there are note mismatches.
761f4a2713aSLionel Sambuc NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes,
762f4a2713aSLionel Sambuc Buffer.note_begin(), Buffer.note_end());
763f4a2713aSLionel Sambuc
764f4a2713aSLionel Sambuc return NumProblems;
765f4a2713aSLionel Sambuc }
766f4a2713aSLionel Sambuc
UpdateParsedFileStatus(SourceManager & SM,FileID FID,ParsedStatus PS)767f4a2713aSLionel Sambuc void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
768f4a2713aSLionel Sambuc FileID FID,
769f4a2713aSLionel Sambuc ParsedStatus PS) {
770f4a2713aSLionel Sambuc // Check SourceManager hasn't changed.
771f4a2713aSLionel Sambuc setSourceManager(SM);
772f4a2713aSLionel Sambuc
773f4a2713aSLionel Sambuc #ifndef NDEBUG
774f4a2713aSLionel Sambuc if (FID.isInvalid())
775f4a2713aSLionel Sambuc return;
776f4a2713aSLionel Sambuc
777f4a2713aSLionel Sambuc const FileEntry *FE = SM.getFileEntryForID(FID);
778f4a2713aSLionel Sambuc
779f4a2713aSLionel Sambuc if (PS == IsParsed) {
780f4a2713aSLionel Sambuc // Move the FileID from the unparsed set to the parsed set.
781f4a2713aSLionel Sambuc UnparsedFiles.erase(FID);
782f4a2713aSLionel Sambuc ParsedFiles.insert(std::make_pair(FID, FE));
783f4a2713aSLionel Sambuc } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) {
784f4a2713aSLionel Sambuc // Add the FileID to the unparsed set if we haven't seen it before.
785f4a2713aSLionel Sambuc
786f4a2713aSLionel Sambuc // Check for directives.
787f4a2713aSLionel Sambuc bool FoundDirectives;
788f4a2713aSLionel Sambuc if (PS == IsUnparsedNoDirectives)
789f4a2713aSLionel Sambuc FoundDirectives = false;
790f4a2713aSLionel Sambuc else
791f4a2713aSLionel Sambuc FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts);
792f4a2713aSLionel Sambuc
793f4a2713aSLionel Sambuc // Add the FileID to the unparsed set.
794f4a2713aSLionel Sambuc UnparsedFiles.insert(std::make_pair(FID,
795f4a2713aSLionel Sambuc UnparsedFileStatus(FE, FoundDirectives)));
796f4a2713aSLionel Sambuc }
797f4a2713aSLionel Sambuc #endif
798f4a2713aSLionel Sambuc }
799f4a2713aSLionel Sambuc
CheckDiagnostics()800f4a2713aSLionel Sambuc void VerifyDiagnosticConsumer::CheckDiagnostics() {
801f4a2713aSLionel Sambuc // Ensure any diagnostics go to the primary client.
802*0a6a1f1dSLionel Sambuc DiagnosticConsumer *CurClient = Diags.getClient();
803*0a6a1f1dSLionel Sambuc std::unique_ptr<DiagnosticConsumer> Owner = Diags.takeClient();
804f4a2713aSLionel Sambuc Diags.setClient(PrimaryClient, false);
805f4a2713aSLionel Sambuc
806f4a2713aSLionel Sambuc #ifndef NDEBUG
807f4a2713aSLionel Sambuc // In a debug build, scan through any files that may have been missed
808f4a2713aSLionel Sambuc // during parsing and issue a fatal error if directives are contained
809f4a2713aSLionel Sambuc // within these files. If a fatal error occurs, this suggests that
810f4a2713aSLionel Sambuc // this file is being parsed separately from the main file, in which
811f4a2713aSLionel Sambuc // case consider moving the directives to the correct place, if this
812f4a2713aSLionel Sambuc // is applicable.
813f4a2713aSLionel Sambuc if (UnparsedFiles.size() > 0) {
814f4a2713aSLionel Sambuc // Generate a cache of parsed FileEntry pointers for alias lookups.
815f4a2713aSLionel Sambuc llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache;
816f4a2713aSLionel Sambuc for (ParsedFilesMap::iterator I = ParsedFiles.begin(),
817f4a2713aSLionel Sambuc End = ParsedFiles.end(); I != End; ++I) {
818f4a2713aSLionel Sambuc if (const FileEntry *FE = I->second)
819f4a2713aSLionel Sambuc ParsedFileCache.insert(FE);
820f4a2713aSLionel Sambuc }
821f4a2713aSLionel Sambuc
822f4a2713aSLionel Sambuc // Iterate through list of unparsed files.
823f4a2713aSLionel Sambuc for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(),
824f4a2713aSLionel Sambuc End = UnparsedFiles.end(); I != End; ++I) {
825f4a2713aSLionel Sambuc const UnparsedFileStatus &Status = I->second;
826f4a2713aSLionel Sambuc const FileEntry *FE = Status.getFile();
827f4a2713aSLionel Sambuc
828f4a2713aSLionel Sambuc // Skip files that have been parsed via an alias.
829f4a2713aSLionel Sambuc if (FE && ParsedFileCache.count(FE))
830f4a2713aSLionel Sambuc continue;
831f4a2713aSLionel Sambuc
832f4a2713aSLionel Sambuc // Report a fatal error if this file contained directives.
833f4a2713aSLionel Sambuc if (Status.foundDirectives()) {
834f4a2713aSLionel Sambuc llvm::report_fatal_error(Twine("-verify directives found after rather"
835f4a2713aSLionel Sambuc " than during normal parsing of ",
836f4a2713aSLionel Sambuc StringRef(FE ? FE->getName() : "(unknown)")));
837f4a2713aSLionel Sambuc }
838f4a2713aSLionel Sambuc }
839f4a2713aSLionel Sambuc
840f4a2713aSLionel Sambuc // UnparsedFiles has been processed now, so clear it.
841f4a2713aSLionel Sambuc UnparsedFiles.clear();
842f4a2713aSLionel Sambuc }
843f4a2713aSLionel Sambuc #endif // !NDEBUG
844f4a2713aSLionel Sambuc
845f4a2713aSLionel Sambuc if (SrcManager) {
846f4a2713aSLionel Sambuc // Produce an error if no expected-* directives could be found in the
847f4a2713aSLionel Sambuc // source file(s) processed.
848f4a2713aSLionel Sambuc if (Status == HasNoDirectives) {
849f4a2713aSLionel Sambuc Diags.Report(diag::err_verify_no_directives).setForceEmit();
850f4a2713aSLionel Sambuc ++NumErrors;
851f4a2713aSLionel Sambuc Status = HasNoDirectivesReported;
852f4a2713aSLionel Sambuc }
853f4a2713aSLionel Sambuc
854f4a2713aSLionel Sambuc // Check that the expected diagnostics occurred.
855f4a2713aSLionel Sambuc NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
856f4a2713aSLionel Sambuc } else {
857*0a6a1f1dSLionel Sambuc NumErrors += (PrintUnexpected(Diags, nullptr, Buffer->err_begin(),
858f4a2713aSLionel Sambuc Buffer->err_end(), "error") +
859*0a6a1f1dSLionel Sambuc PrintUnexpected(Diags, nullptr, Buffer->warn_begin(),
860f4a2713aSLionel Sambuc Buffer->warn_end(), "warn") +
861*0a6a1f1dSLionel Sambuc PrintUnexpected(Diags, nullptr, Buffer->note_begin(),
862f4a2713aSLionel Sambuc Buffer->note_end(), "note"));
863f4a2713aSLionel Sambuc }
864f4a2713aSLionel Sambuc
865*0a6a1f1dSLionel Sambuc Diags.setClient(CurClient, Owner.release() != nullptr);
866f4a2713aSLionel Sambuc
867f4a2713aSLionel Sambuc // Reset the buffer, we have processed all the diagnostics in it.
868f4a2713aSLionel Sambuc Buffer.reset(new TextDiagnosticBuffer());
869*0a6a1f1dSLionel Sambuc ED.Reset();
870f4a2713aSLionel Sambuc }
871f4a2713aSLionel Sambuc
create(bool RegexKind,SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)872*0a6a1f1dSLionel Sambuc std::unique_ptr<Directive> Directive::create(bool RegexKind,
873*0a6a1f1dSLionel Sambuc SourceLocation DirectiveLoc,
874*0a6a1f1dSLionel Sambuc SourceLocation DiagnosticLoc,
875*0a6a1f1dSLionel Sambuc bool MatchAnyLine, StringRef Text,
876f4a2713aSLionel Sambuc unsigned Min, unsigned Max) {
877*0a6a1f1dSLionel Sambuc if (!RegexKind)
878*0a6a1f1dSLionel Sambuc return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
879*0a6a1f1dSLionel Sambuc MatchAnyLine, Text, Min, Max);
880*0a6a1f1dSLionel Sambuc
881*0a6a1f1dSLionel Sambuc // Parse the directive into a regular expression.
882*0a6a1f1dSLionel Sambuc std::string RegexStr;
883*0a6a1f1dSLionel Sambuc StringRef S = Text;
884*0a6a1f1dSLionel Sambuc while (!S.empty()) {
885*0a6a1f1dSLionel Sambuc if (S.startswith("{{")) {
886*0a6a1f1dSLionel Sambuc S = S.drop_front(2);
887*0a6a1f1dSLionel Sambuc size_t RegexMatchLength = S.find("}}");
888*0a6a1f1dSLionel Sambuc assert(RegexMatchLength != StringRef::npos);
889*0a6a1f1dSLionel Sambuc // Append the regex, enclosed in parentheses.
890*0a6a1f1dSLionel Sambuc RegexStr += "(";
891*0a6a1f1dSLionel Sambuc RegexStr.append(S.data(), RegexMatchLength);
892*0a6a1f1dSLionel Sambuc RegexStr += ")";
893*0a6a1f1dSLionel Sambuc S = S.drop_front(RegexMatchLength + 2);
894*0a6a1f1dSLionel Sambuc } else {
895*0a6a1f1dSLionel Sambuc size_t VerbatimMatchLength = S.find("{{");
896*0a6a1f1dSLionel Sambuc if (VerbatimMatchLength == StringRef::npos)
897*0a6a1f1dSLionel Sambuc VerbatimMatchLength = S.size();
898*0a6a1f1dSLionel Sambuc // Escape and append the fixed string.
899*0a6a1f1dSLionel Sambuc RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength));
900*0a6a1f1dSLionel Sambuc S = S.drop_front(VerbatimMatchLength);
901*0a6a1f1dSLionel Sambuc }
902*0a6a1f1dSLionel Sambuc }
903*0a6a1f1dSLionel Sambuc
904*0a6a1f1dSLionel Sambuc return llvm::make_unique<RegexDirective>(
905*0a6a1f1dSLionel Sambuc DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr);
906f4a2713aSLionel Sambuc }
907