15ffd83dbSDimitry Andric //===--- Diagnostics.cpp - Helper class for error diagnostics ---*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "clang/ASTMatchers/Dynamic/Diagnostics.h"
100b57cec5SDimitry Andric
110b57cec5SDimitry Andric namespace clang {
120b57cec5SDimitry Andric namespace ast_matchers {
130b57cec5SDimitry Andric namespace dynamic {
pushContextFrame(ContextType Type,SourceRange Range)140b57cec5SDimitry Andric Diagnostics::ArgStream Diagnostics::pushContextFrame(ContextType Type,
150b57cec5SDimitry Andric SourceRange Range) {
160b57cec5SDimitry Andric ContextStack.emplace_back();
170b57cec5SDimitry Andric ContextFrame& data = ContextStack.back();
180b57cec5SDimitry Andric data.Type = Type;
190b57cec5SDimitry Andric data.Range = Range;
200b57cec5SDimitry Andric return ArgStream(&data.Args);
210b57cec5SDimitry Andric }
220b57cec5SDimitry Andric
Context(ConstructMatcherEnum,Diagnostics * Error,StringRef MatcherName,SourceRange MatcherRange)230b57cec5SDimitry Andric Diagnostics::Context::Context(ConstructMatcherEnum, Diagnostics *Error,
240b57cec5SDimitry Andric StringRef MatcherName,
250b57cec5SDimitry Andric SourceRange MatcherRange)
260b57cec5SDimitry Andric : Error(Error) {
270b57cec5SDimitry Andric Error->pushContextFrame(CT_MatcherConstruct, MatcherRange) << MatcherName;
280b57cec5SDimitry Andric }
290b57cec5SDimitry Andric
Context(MatcherArgEnum,Diagnostics * Error,StringRef MatcherName,SourceRange MatcherRange,unsigned ArgNumber)300b57cec5SDimitry Andric Diagnostics::Context::Context(MatcherArgEnum, Diagnostics *Error,
310b57cec5SDimitry Andric StringRef MatcherName,
320b57cec5SDimitry Andric SourceRange MatcherRange,
330b57cec5SDimitry Andric unsigned ArgNumber)
340b57cec5SDimitry Andric : Error(Error) {
350b57cec5SDimitry Andric Error->pushContextFrame(CT_MatcherArg, MatcherRange) << ArgNumber
360b57cec5SDimitry Andric << MatcherName;
370b57cec5SDimitry Andric }
380b57cec5SDimitry Andric
~Context()390b57cec5SDimitry Andric Diagnostics::Context::~Context() { Error->ContextStack.pop_back(); }
400b57cec5SDimitry Andric
OverloadContext(Diagnostics * Error)410b57cec5SDimitry Andric Diagnostics::OverloadContext::OverloadContext(Diagnostics *Error)
420b57cec5SDimitry Andric : Error(Error), BeginIndex(Error->Errors.size()) {}
430b57cec5SDimitry Andric
~OverloadContext()440b57cec5SDimitry Andric Diagnostics::OverloadContext::~OverloadContext() {
450b57cec5SDimitry Andric // Merge all errors that happened while in this context.
460b57cec5SDimitry Andric if (BeginIndex < Error->Errors.size()) {
470b57cec5SDimitry Andric Diagnostics::ErrorContent &Dest = Error->Errors[BeginIndex];
480b57cec5SDimitry Andric for (size_t i = BeginIndex + 1, e = Error->Errors.size(); i < e; ++i) {
490b57cec5SDimitry Andric Dest.Messages.push_back(Error->Errors[i].Messages[0]);
500b57cec5SDimitry Andric }
510b57cec5SDimitry Andric Error->Errors.resize(BeginIndex + 1);
520b57cec5SDimitry Andric }
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric
revertErrors()550b57cec5SDimitry Andric void Diagnostics::OverloadContext::revertErrors() {
560b57cec5SDimitry Andric // Revert the errors.
570b57cec5SDimitry Andric Error->Errors.resize(BeginIndex);
580b57cec5SDimitry Andric }
590b57cec5SDimitry Andric
operator <<(const Twine & Arg)600b57cec5SDimitry Andric Diagnostics::ArgStream &Diagnostics::ArgStream::operator<<(const Twine &Arg) {
610b57cec5SDimitry Andric Out->push_back(Arg.str());
620b57cec5SDimitry Andric return *this;
630b57cec5SDimitry Andric }
640b57cec5SDimitry Andric
addError(SourceRange Range,ErrorType Error)650b57cec5SDimitry Andric Diagnostics::ArgStream Diagnostics::addError(SourceRange Range,
660b57cec5SDimitry Andric ErrorType Error) {
670b57cec5SDimitry Andric Errors.emplace_back();
680b57cec5SDimitry Andric ErrorContent &Last = Errors.back();
690b57cec5SDimitry Andric Last.ContextStack = ContextStack;
700b57cec5SDimitry Andric Last.Messages.emplace_back();
710b57cec5SDimitry Andric Last.Messages.back().Range = Range;
720b57cec5SDimitry Andric Last.Messages.back().Type = Error;
730b57cec5SDimitry Andric return ArgStream(&Last.Messages.back().Args);
740b57cec5SDimitry Andric }
750b57cec5SDimitry Andric
contextTypeToFormatString(Diagnostics::ContextType Type)760b57cec5SDimitry Andric static StringRef contextTypeToFormatString(Diagnostics::ContextType Type) {
770b57cec5SDimitry Andric switch (Type) {
780b57cec5SDimitry Andric case Diagnostics::CT_MatcherConstruct:
790b57cec5SDimitry Andric return "Error building matcher $0.";
800b57cec5SDimitry Andric case Diagnostics::CT_MatcherArg:
810b57cec5SDimitry Andric return "Error parsing argument $0 for matcher $1.";
820b57cec5SDimitry Andric }
830b57cec5SDimitry Andric llvm_unreachable("Unknown ContextType value.");
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric
errorTypeToFormatString(Diagnostics::ErrorType Type)860b57cec5SDimitry Andric static StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) {
870b57cec5SDimitry Andric switch (Type) {
880b57cec5SDimitry Andric case Diagnostics::ET_RegistryMatcherNotFound:
890b57cec5SDimitry Andric return "Matcher not found: $0";
900b57cec5SDimitry Andric case Diagnostics::ET_RegistryWrongArgCount:
910b57cec5SDimitry Andric return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
920b57cec5SDimitry Andric case Diagnostics::ET_RegistryWrongArgType:
930b57cec5SDimitry Andric return "Incorrect type for arg $0. (Expected = $1) != (Actual = $2)";
940b57cec5SDimitry Andric case Diagnostics::ET_RegistryNotBindable:
950b57cec5SDimitry Andric return "Matcher does not support binding.";
960b57cec5SDimitry Andric case Diagnostics::ET_RegistryAmbiguousOverload:
970b57cec5SDimitry Andric // TODO: Add type info about the overload error.
980b57cec5SDimitry Andric return "Ambiguous matcher overload.";
990b57cec5SDimitry Andric case Diagnostics::ET_RegistryValueNotFound:
1000b57cec5SDimitry Andric return "Value not found: $0";
1015ffd83dbSDimitry Andric case Diagnostics::ET_RegistryUnknownEnumWithReplace:
1025ffd83dbSDimitry Andric return "Unknown value '$1' for arg $0; did you mean '$2'";
103fe6060f1SDimitry Andric case Diagnostics::ET_RegistryNonNodeMatcher:
104fe6060f1SDimitry Andric return "Matcher not a node matcher: $0";
105fe6060f1SDimitry Andric case Diagnostics::ET_RegistryMatcherNoWithSupport:
106fe6060f1SDimitry Andric return "Matcher does not support with call.";
1070b57cec5SDimitry Andric
1080b57cec5SDimitry Andric case Diagnostics::ET_ParserStringError:
1090b57cec5SDimitry Andric return "Error parsing string token: <$0>";
1100b57cec5SDimitry Andric case Diagnostics::ET_ParserNoOpenParen:
1110b57cec5SDimitry Andric return "Error parsing matcher. Found token <$0> while looking for '('.";
1120b57cec5SDimitry Andric case Diagnostics::ET_ParserNoCloseParen:
1130b57cec5SDimitry Andric return "Error parsing matcher. Found end-of-code while looking for ')'.";
1140b57cec5SDimitry Andric case Diagnostics::ET_ParserNoComma:
1150b57cec5SDimitry Andric return "Error parsing matcher. Found token <$0> while looking for ','.";
1160b57cec5SDimitry Andric case Diagnostics::ET_ParserNoCode:
1170b57cec5SDimitry Andric return "End of code found while looking for token.";
1180b57cec5SDimitry Andric case Diagnostics::ET_ParserNotAMatcher:
1190b57cec5SDimitry Andric return "Input value is not a matcher expression.";
1200b57cec5SDimitry Andric case Diagnostics::ET_ParserInvalidToken:
1210b57cec5SDimitry Andric return "Invalid token <$0> found when looking for a value.";
1220b57cec5SDimitry Andric case Diagnostics::ET_ParserMalformedBindExpr:
1230b57cec5SDimitry Andric return "Malformed bind() expression.";
1240b57cec5SDimitry Andric case Diagnostics::ET_ParserTrailingCode:
1250b57cec5SDimitry Andric return "Expected end of code.";
1260b57cec5SDimitry Andric case Diagnostics::ET_ParserNumberError:
1270b57cec5SDimitry Andric return "Error parsing numeric literal: <$0>";
1280b57cec5SDimitry Andric case Diagnostics::ET_ParserOverloadedType:
1290b57cec5SDimitry Andric return "Input value has unresolved overloaded type: $0";
130fe6060f1SDimitry Andric case Diagnostics::ET_ParserMalformedChainedExpr:
131fe6060f1SDimitry Andric return "Period not followed by valid chained call.";
132fe6060f1SDimitry Andric case Diagnostics::ET_ParserFailedToBuildMatcher:
133fe6060f1SDimitry Andric return "Failed to build matcher: $0.";
1340b57cec5SDimitry Andric
1350b57cec5SDimitry Andric case Diagnostics::ET_None:
1360b57cec5SDimitry Andric return "<N/A>";
1370b57cec5SDimitry Andric }
1380b57cec5SDimitry Andric llvm_unreachable("Unknown ErrorType value.");
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric
formatErrorString(StringRef FormatString,ArrayRef<std::string> Args,llvm::raw_ostream & OS)1410b57cec5SDimitry Andric static void formatErrorString(StringRef FormatString,
1420b57cec5SDimitry Andric ArrayRef<std::string> Args,
1430b57cec5SDimitry Andric llvm::raw_ostream &OS) {
1440b57cec5SDimitry Andric while (!FormatString.empty()) {
1450b57cec5SDimitry Andric std::pair<StringRef, StringRef> Pieces = FormatString.split("$");
1460b57cec5SDimitry Andric OS << Pieces.first.str();
1470b57cec5SDimitry Andric if (Pieces.second.empty()) break;
1480b57cec5SDimitry Andric
1490b57cec5SDimitry Andric const char Next = Pieces.second.front();
1500b57cec5SDimitry Andric FormatString = Pieces.second.drop_front();
1510b57cec5SDimitry Andric if (Next >= '0' && Next <= '9') {
1520b57cec5SDimitry Andric const unsigned Index = Next - '0';
1530b57cec5SDimitry Andric if (Index < Args.size()) {
1540b57cec5SDimitry Andric OS << Args[Index];
1550b57cec5SDimitry Andric } else {
1560b57cec5SDimitry Andric OS << "<Argument_Not_Provided>";
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric }
1590b57cec5SDimitry Andric }
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric
maybeAddLineAndColumn(SourceRange Range,llvm::raw_ostream & OS)1620b57cec5SDimitry Andric static void maybeAddLineAndColumn(SourceRange Range,
1630b57cec5SDimitry Andric llvm::raw_ostream &OS) {
1640b57cec5SDimitry Andric if (Range.Start.Line > 0 && Range.Start.Column > 0) {
1650b57cec5SDimitry Andric OS << Range.Start.Line << ":" << Range.Start.Column << ": ";
1660b57cec5SDimitry Andric }
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric
printContextFrameToStream(const Diagnostics::ContextFrame & Frame,llvm::raw_ostream & OS)1690b57cec5SDimitry Andric static void printContextFrameToStream(const Diagnostics::ContextFrame &Frame,
1700b57cec5SDimitry Andric llvm::raw_ostream &OS) {
1710b57cec5SDimitry Andric maybeAddLineAndColumn(Frame.Range, OS);
1720b57cec5SDimitry Andric formatErrorString(contextTypeToFormatString(Frame.Type), Frame.Args, OS);
1730b57cec5SDimitry Andric }
1740b57cec5SDimitry Andric
1750b57cec5SDimitry Andric static void
printMessageToStream(const Diagnostics::ErrorContent::Message & Message,const Twine Prefix,llvm::raw_ostream & OS)1760b57cec5SDimitry Andric printMessageToStream(const Diagnostics::ErrorContent::Message &Message,
1770b57cec5SDimitry Andric const Twine Prefix, llvm::raw_ostream &OS) {
1780b57cec5SDimitry Andric maybeAddLineAndColumn(Message.Range, OS);
1790b57cec5SDimitry Andric OS << Prefix;
1800b57cec5SDimitry Andric formatErrorString(errorTypeToFormatString(Message.Type), Message.Args, OS);
1810b57cec5SDimitry Andric }
1820b57cec5SDimitry Andric
printErrorContentToStream(const Diagnostics::ErrorContent & Content,llvm::raw_ostream & OS)1830b57cec5SDimitry Andric static void printErrorContentToStream(const Diagnostics::ErrorContent &Content,
1840b57cec5SDimitry Andric llvm::raw_ostream &OS) {
1850b57cec5SDimitry Andric if (Content.Messages.size() == 1) {
1860b57cec5SDimitry Andric printMessageToStream(Content.Messages[0], "", OS);
1870b57cec5SDimitry Andric } else {
1880b57cec5SDimitry Andric for (size_t i = 0, e = Content.Messages.size(); i != e; ++i) {
1890b57cec5SDimitry Andric if (i != 0) OS << "\n";
1900b57cec5SDimitry Andric printMessageToStream(Content.Messages[i],
1910b57cec5SDimitry Andric "Candidate " + Twine(i + 1) + ": ", OS);
1920b57cec5SDimitry Andric }
1930b57cec5SDimitry Andric }
1940b57cec5SDimitry Andric }
1950b57cec5SDimitry Andric
printToStream(llvm::raw_ostream & OS) const1960b57cec5SDimitry Andric void Diagnostics::printToStream(llvm::raw_ostream &OS) const {
1970b57cec5SDimitry Andric for (size_t i = 0, e = Errors.size(); i != e; ++i) {
1980b57cec5SDimitry Andric if (i != 0) OS << "\n";
1990b57cec5SDimitry Andric printErrorContentToStream(Errors[i], OS);
2000b57cec5SDimitry Andric }
2010b57cec5SDimitry Andric }
2020b57cec5SDimitry Andric
toString() const2030b57cec5SDimitry Andric std::string Diagnostics::toString() const {
2040b57cec5SDimitry Andric std::string S;
2050b57cec5SDimitry Andric llvm::raw_string_ostream OS(S);
2060b57cec5SDimitry Andric printToStream(OS);
207*0eae32dcSDimitry Andric return S;
2080b57cec5SDimitry Andric }
2090b57cec5SDimitry Andric
printToStreamFull(llvm::raw_ostream & OS) const2100b57cec5SDimitry Andric void Diagnostics::printToStreamFull(llvm::raw_ostream &OS) const {
2110b57cec5SDimitry Andric for (size_t i = 0, e = Errors.size(); i != e; ++i) {
2120b57cec5SDimitry Andric if (i != 0) OS << "\n";
2130b57cec5SDimitry Andric const ErrorContent &Error = Errors[i];
2140b57cec5SDimitry Andric for (size_t i = 0, e = Error.ContextStack.size(); i != e; ++i) {
2150b57cec5SDimitry Andric printContextFrameToStream(Error.ContextStack[i], OS);
2160b57cec5SDimitry Andric OS << "\n";
2170b57cec5SDimitry Andric }
2180b57cec5SDimitry Andric printErrorContentToStream(Error, OS);
2190b57cec5SDimitry Andric }
2200b57cec5SDimitry Andric }
2210b57cec5SDimitry Andric
toStringFull() const2220b57cec5SDimitry Andric std::string Diagnostics::toStringFull() const {
2230b57cec5SDimitry Andric std::string S;
2240b57cec5SDimitry Andric llvm::raw_string_ostream OS(S);
2250b57cec5SDimitry Andric printToStreamFull(OS);
226*0eae32dcSDimitry Andric return S;
2270b57cec5SDimitry Andric }
2280b57cec5SDimitry Andric
2290b57cec5SDimitry Andric } // namespace dynamic
2300b57cec5SDimitry Andric } // namespace ast_matchers
2310b57cec5SDimitry Andric } // namespace clang
232