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