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