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