xref: /minix3/external/bsd/llvm/dist/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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