1 //===- VerifyDiagnosticConsumer.h - Verifying Diagnostic Client -*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H 10 #define LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H 11 12 #include "clang/Basic/Diagnostic.h" 13 #include "clang/Basic/FileManager.h" 14 #include "clang/Basic/LLVM.h" 15 #include "clang/Basic/SourceLocation.h" 16 #include "clang/Lex/Preprocessor.h" 17 #include "llvm/ADT/DenseMap.h" 18 #include "llvm/ADT/PointerIntPair.h" 19 #include "llvm/ADT/StringRef.h" 20 #include <cassert> 21 #include <limits> 22 #include <memory> 23 #include <string> 24 #include <vector> 25 26 namespace clang { 27 28 class FileEntry; 29 class LangOptions; 30 class SourceManager; 31 class TextDiagnosticBuffer; 32 33 /// VerifyDiagnosticConsumer - Create a diagnostic client which will use 34 /// markers in the input source to check that all the emitted diagnostics match 35 /// those expected. See clang/docs/InternalsManual.rst for details about how to 36 /// write tests to verify diagnostics. 37 /// 38 class VerifyDiagnosticConsumer: public DiagnosticConsumer, 39 public CommentHandler { 40 public: 41 /// Directive - Abstract class representing a parsed verify directive. 42 /// 43 class Directive { 44 public: 45 static std::unique_ptr<Directive> 46 create(bool RegexKind, SourceLocation DirectiveLoc, 47 SourceLocation DiagnosticLoc, bool MatchAnyFileAndLine, 48 bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max); 49 50 public: 51 /// Constant representing n or more matches. 52 static const unsigned MaxCount = std::numeric_limits<unsigned>::max(); 53 54 SourceLocation DirectiveLoc; 55 SourceLocation DiagnosticLoc; 56 const std::string Text; 57 unsigned Min, Max; 58 bool MatchAnyLine; 59 bool MatchAnyFileAndLine; // `MatchAnyFileAndLine` implies `MatchAnyLine`. 60 61 Directive(const Directive &) = delete; 62 Directive &operator=(const Directive &) = delete; 63 virtual ~Directive() = default; 64 65 // Returns true if directive text is valid. 66 // Otherwise returns false and populates E. 67 virtual bool isValid(std::string &Error) = 0; 68 69 // Returns true on match. 70 virtual bool match(StringRef S) = 0; 71 72 protected: Directive(SourceLocation DirectiveLoc,SourceLocation DiagnosticLoc,bool MatchAnyFileAndLine,bool MatchAnyLine,StringRef Text,unsigned Min,unsigned Max)73 Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, 74 bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, 75 unsigned Min, unsigned Max) 76 : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), Text(Text), 77 Min(Min), Max(Max), MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine), 78 MatchAnyFileAndLine(MatchAnyFileAndLine) { 79 assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); 80 assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) && 81 "DiagnosticLoc is invalid!"); 82 } 83 }; 84 85 using DirectiveList = std::vector<std::unique_ptr<Directive>>; 86 87 /// ExpectedData - owns directive objects and deletes on destructor. 88 struct ExpectedData { 89 DirectiveList Errors; 90 DirectiveList Warnings; 91 DirectiveList Remarks; 92 DirectiveList Notes; 93 ResetExpectedData94 void Reset() { 95 Errors.clear(); 96 Warnings.clear(); 97 Remarks.clear(); 98 Notes.clear(); 99 } 100 }; 101 102 enum DirectiveStatus { 103 HasNoDirectives, 104 HasNoDirectivesReported, 105 HasExpectedNoDiagnostics, 106 HasOtherExpectedDirectives 107 }; 108 109 class MarkerTracker; 110 111 private: 112 DiagnosticsEngine &Diags; 113 DiagnosticConsumer *PrimaryClient; 114 std::unique_ptr<DiagnosticConsumer> PrimaryClientOwner; 115 std::unique_ptr<TextDiagnosticBuffer> Buffer; 116 std::unique_ptr<MarkerTracker> Markers; 117 const Preprocessor *CurrentPreprocessor = nullptr; 118 const LangOptions *LangOpts = nullptr; 119 SourceManager *SrcManager = nullptr; 120 unsigned ActiveSourceFiles = 0; 121 DirectiveStatus Status; 122 ExpectedData ED; 123 124 void CheckDiagnostics(); 125 setSourceManager(SourceManager & SM)126 void setSourceManager(SourceManager &SM) { 127 assert((!SrcManager || SrcManager == &SM) && "SourceManager changed!"); 128 SrcManager = &SM; 129 } 130 131 // These facilities are used for validation in debug builds. 132 class UnparsedFileStatus { 133 OptionalFileEntryRef File; 134 bool FoundDirectives; 135 136 public: UnparsedFileStatus(OptionalFileEntryRef File,bool FoundDirectives)137 UnparsedFileStatus(OptionalFileEntryRef File, bool FoundDirectives) 138 : File(File), FoundDirectives(FoundDirectives) {} 139 getFile()140 OptionalFileEntryRef getFile() const { return File; } foundDirectives()141 bool foundDirectives() const { return FoundDirectives; } 142 }; 143 144 using ParsedFilesMap = llvm::DenseMap<FileID, const FileEntry *>; 145 using UnparsedFilesMap = llvm::DenseMap<FileID, UnparsedFileStatus>; 146 147 ParsedFilesMap ParsedFiles; 148 UnparsedFilesMap UnparsedFiles; 149 150 public: 151 /// Create a new verifying diagnostic client, which will issue errors to 152 /// the currently-attached diagnostic client when a diagnostic does not match 153 /// what is expected (as indicated in the source file). 154 VerifyDiagnosticConsumer(DiagnosticsEngine &Diags); 155 ~VerifyDiagnosticConsumer() override; 156 157 void BeginSourceFile(const LangOptions &LangOpts, 158 const Preprocessor *PP) override; 159 160 void EndSourceFile() override; 161 162 enum ParsedStatus { 163 /// File has been processed via HandleComment. 164 IsParsed, 165 166 /// File has diagnostics and may have directives. 167 IsUnparsed, 168 169 /// File has diagnostics but guaranteed no directives. 170 IsUnparsedNoDirectives 171 }; 172 173 /// Update lists of parsed and unparsed files. 174 void UpdateParsedFileStatus(SourceManager &SM, FileID FID, ParsedStatus PS); 175 176 bool HandleComment(Preprocessor &PP, SourceRange Comment) override; 177 178 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 179 const Diagnostic &Info) override; 180 }; 181 182 } // namespace clang 183 184 #endif // LLVM_CLANG_FRONTEND_VERIFYDIAGNOSTICCONSUMER_H 185