1 //===--- Diagnostics.cpp - Helper class for error diagnostics -----*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/ASTMatchers/Dynamic/Diagnostics.h" 11 12 namespace clang { 13 namespace ast_matchers { 14 namespace dynamic { 15 16 Diagnostics::ArgStream Diagnostics::pushContextFrame(ContextType Type, 17 SourceRange Range) { 18 ContextStack.push_back(ContextFrame()); 19 ContextFrame& data = ContextStack.back(); 20 data.Type = Type; 21 data.Range = Range; 22 return ArgStream(&data.Args); 23 } 24 25 Diagnostics::Context::Context(ConstructMatcherEnum, Diagnostics *Error, 26 StringRef MatcherName, 27 const SourceRange &MatcherRange) 28 : Error(Error) { 29 Error->pushContextFrame(CT_MatcherConstruct, MatcherRange) << MatcherName; 30 } 31 32 Diagnostics::Context::Context(MatcherArgEnum, Diagnostics *Error, 33 StringRef MatcherName, 34 const SourceRange &MatcherRange, 35 unsigned ArgNumber) 36 : Error(Error) { 37 Error->pushContextFrame(CT_MatcherArg, MatcherRange) << ArgNumber 38 << MatcherName; 39 } 40 41 Diagnostics::Context::~Context() { Error->ContextStack.pop_back(); } 42 43 Diagnostics::OverloadContext::OverloadContext(Diagnostics *Error) 44 : Error(Error), BeginIndex(Error->Errors.size()) {} 45 46 Diagnostics::OverloadContext::~OverloadContext() { 47 // Merge all errors that happened while in this context. 48 if (BeginIndex < Error->Errors.size()) { 49 Diagnostics::ErrorContent &Dest = Error->Errors[BeginIndex]; 50 for (size_t i = BeginIndex + 1, e = Error->Errors.size(); i < e; ++i) { 51 Dest.Messages.push_back(Error->Errors[i].Messages[0]); 52 } 53 Error->Errors.resize(BeginIndex + 1); 54 } 55 } 56 57 void Diagnostics::OverloadContext::revertErrors() { 58 // Revert the errors. 59 Error->Errors.resize(BeginIndex); 60 } 61 62 Diagnostics::ArgStream &Diagnostics::ArgStream::operator<<(const Twine &Arg) { 63 Out->push_back(Arg.str()); 64 return *this; 65 } 66 67 Diagnostics::ArgStream Diagnostics::addError(const SourceRange &Range, 68 ErrorType Error) { 69 Errors.push_back(ErrorContent()); 70 ErrorContent &Last = Errors.back(); 71 Last.ContextStack = ContextStack; 72 Last.Messages.push_back(ErrorContent::Message()); 73 Last.Messages.back().Range = Range; 74 Last.Messages.back().Type = Error; 75 return ArgStream(&Last.Messages.back().Args); 76 } 77 78 StringRef contextTypeToFormatString(Diagnostics::ContextType Type) { 79 switch (Type) { 80 case Diagnostics::CT_MatcherConstruct: 81 return "Error building matcher $0."; 82 case Diagnostics::CT_MatcherArg: 83 return "Error parsing argument $0 for matcher $1."; 84 } 85 llvm_unreachable("Unknown ContextType value."); 86 } 87 88 StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) { 89 switch (Type) { 90 case Diagnostics::ET_RegistryNotFound: 91 return "Matcher not found: $0"; 92 case Diagnostics::ET_RegistryWrongArgCount: 93 return "Incorrect argument count. (Expected = $0) != (Actual = $1)"; 94 case Diagnostics::ET_RegistryWrongArgType: 95 return "Incorrect type for arg $0. (Expected = $1) != (Actual = $2)"; 96 case Diagnostics::ET_RegistryNotBindable: 97 return "Matcher does not support binding."; 98 case Diagnostics::ET_RegistryAmbiguousOverload: 99 // TODO: Add type info about the overload error. 100 return "Ambiguous matcher overload."; 101 102 case Diagnostics::ET_ParserStringError: 103 return "Error parsing string token: <$0>"; 104 case Diagnostics::ET_ParserNoOpenParen: 105 return "Error parsing matcher. Found token <$0> while looking for '('."; 106 case Diagnostics::ET_ParserNoCloseParen: 107 return "Error parsing matcher. Found end-of-code while looking for ')'."; 108 case Diagnostics::ET_ParserNoComma: 109 return "Error parsing matcher. Found token <$0> while looking for ','."; 110 case Diagnostics::ET_ParserNoCode: 111 return "End of code found while looking for token."; 112 case Diagnostics::ET_ParserNotAMatcher: 113 return "Input value is not a matcher expression."; 114 case Diagnostics::ET_ParserInvalidToken: 115 return "Invalid token <$0> found when looking for a value."; 116 case Diagnostics::ET_ParserMalformedBindExpr: 117 return "Malformed bind() expression."; 118 case Diagnostics::ET_ParserTrailingCode: 119 return "Expected end of code."; 120 case Diagnostics::ET_ParserUnsignedError: 121 return "Error parsing unsigned token: <$0>"; 122 case Diagnostics::ET_ParserOverloadedType: 123 return "Input value has unresolved overloaded type: $0"; 124 125 case Diagnostics::ET_None: 126 return "<N/A>"; 127 } 128 llvm_unreachable("Unknown ErrorType value."); 129 } 130 131 void formatErrorString(StringRef FormatString, ArrayRef<std::string> Args, 132 llvm::raw_ostream &OS) { 133 while (!FormatString.empty()) { 134 std::pair<StringRef, StringRef> Pieces = FormatString.split("$"); 135 OS << Pieces.first.str(); 136 if (Pieces.second.empty()) break; 137 138 const char Next = Pieces.second.front(); 139 FormatString = Pieces.second.drop_front(); 140 if (Next >= '0' && Next <= '9') { 141 const unsigned Index = Next - '0'; 142 if (Index < Args.size()) { 143 OS << Args[Index]; 144 } else { 145 OS << "<Argument_Not_Provided>"; 146 } 147 } 148 } 149 } 150 151 static void maybeAddLineAndColumn(const SourceRange &Range, 152 llvm::raw_ostream &OS) { 153 if (Range.Start.Line > 0 && Range.Start.Column > 0) { 154 OS << Range.Start.Line << ":" << Range.Start.Column << ": "; 155 } 156 } 157 158 static void printContextFrameToStream(const Diagnostics::ContextFrame &Frame, 159 llvm::raw_ostream &OS) { 160 maybeAddLineAndColumn(Frame.Range, OS); 161 formatErrorString(contextTypeToFormatString(Frame.Type), Frame.Args, OS); 162 } 163 164 static void 165 printMessageToStream(const Diagnostics::ErrorContent::Message &Message, 166 const Twine Prefix, llvm::raw_ostream &OS) { 167 maybeAddLineAndColumn(Message.Range, OS); 168 OS << Prefix; 169 formatErrorString(errorTypeToFormatString(Message.Type), Message.Args, OS); 170 } 171 172 static void printErrorContentToStream(const Diagnostics::ErrorContent &Content, 173 llvm::raw_ostream &OS) { 174 if (Content.Messages.size() == 1) { 175 printMessageToStream(Content.Messages[0], "", OS); 176 } else { 177 for (size_t i = 0, e = Content.Messages.size(); i != e; ++i) { 178 if (i != 0) OS << "\n"; 179 printMessageToStream(Content.Messages[i], 180 "Candidate " + Twine(i + 1) + ": ", OS); 181 } 182 } 183 } 184 185 void Diagnostics::printToStream(llvm::raw_ostream &OS) const { 186 for (size_t i = 0, e = Errors.size(); i != e; ++i) { 187 if (i != 0) OS << "\n"; 188 printErrorContentToStream(Errors[i], OS); 189 } 190 } 191 192 std::string Diagnostics::toString() const { 193 std::string S; 194 llvm::raw_string_ostream OS(S); 195 printToStream(OS); 196 return OS.str(); 197 } 198 199 void Diagnostics::printToStreamFull(llvm::raw_ostream &OS) const { 200 for (size_t i = 0, e = Errors.size(); i != e; ++i) { 201 if (i != 0) OS << "\n"; 202 const ErrorContent &Error = Errors[i]; 203 for (size_t i = 0, e = Error.ContextStack.size(); i != e; ++i) { 204 printContextFrameToStream(Error.ContextStack[i], OS); 205 OS << "\n"; 206 } 207 printErrorContentToStream(Error, OS); 208 } 209 } 210 211 std::string Diagnostics::toStringFull() const { 212 std::string S; 213 llvm::raw_string_ostream OS(S); 214 printToStreamFull(OS); 215 return OS.str(); 216 } 217 218 } // namespace dynamic 219 } // namespace ast_matchers 220 } // namespace clang 221