232 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/ASTMatchers/Dynamic/Diagnostics.h"10 11namespace clang {12namespace ast_matchers {13namespace dynamic {14Diagnostics::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 23Diagnostics::Context::Context(ConstructMatcherEnum, Diagnostics *Error,24 StringRef MatcherName,25 SourceRange MatcherRange)26 : Error(Error) {27 Error->pushContextFrame(CT_MatcherConstruct, MatcherRange) << MatcherName;28}29 30Diagnostics::Context::Context(MatcherArgEnum, Diagnostics *Error,31 StringRef MatcherName,32 SourceRange MatcherRange,33 unsigned ArgNumber)34 : Error(Error) {35 Error->pushContextFrame(CT_MatcherArg, MatcherRange) << ArgNumber36 << MatcherName;37}38 39Diagnostics::Context::~Context() { Error->ContextStack.pop_back(); }40 41Diagnostics::OverloadContext::OverloadContext(Diagnostics *Error)42 : Error(Error), BeginIndex(Error->Errors.size()) {}43 44Diagnostics::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 55void Diagnostics::OverloadContext::revertErrors() {56 // Revert the errors.57 Error->Errors.resize(BeginIndex);58}59 60Diagnostics::ArgStream &Diagnostics::ArgStream::operator<<(const Twine &Arg) {61 Out->push_back(Arg.str());62 return *this;63}64 65Diagnostics::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 76static 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 86static 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 141static 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 162static 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 169static 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 175static void176printMessageToStream(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 183static 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 196void 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 203std::string Diagnostics::toString() const {204 std::string S;205 llvm::raw_string_ostream OS(S);206 printToStream(OS);207 return S;208}209 210void 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 222std::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 dynamic230} // namespace ast_matchers231} // namespace clang232