1259701Sdim//===--- Diagnostics.cpp - Helper class for error diagnostics -----*- C++ -*-===//
2259701Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6259701Sdim//
7259701Sdim//===----------------------------------------------------------------------===//
8259701Sdim
9259701Sdim#include "clang/ASTMatchers/Dynamic/Diagnostics.h"
10259701Sdim
11259701Sdimnamespace clang {
12259701Sdimnamespace ast_matchers {
13259701Sdimnamespace dynamic {
14259701SdimDiagnostics::ArgStream Diagnostics::pushContextFrame(ContextType Type,
15259701Sdim                                                     SourceRange Range) {
16288943Sdim  ContextStack.emplace_back();
17259701Sdim  ContextFrame& data = ContextStack.back();
18259701Sdim  data.Type = Type;
19259701Sdim  data.Range = Range;
20259701Sdim  return ArgStream(&data.Args);
21259701Sdim}
22259701Sdim
23259701SdimDiagnostics::Context::Context(ConstructMatcherEnum, Diagnostics *Error,
24259701Sdim                              StringRef MatcherName,
25296417Sdim                              SourceRange MatcherRange)
26259701Sdim    : Error(Error) {
27259701Sdim  Error->pushContextFrame(CT_MatcherConstruct, MatcherRange) << MatcherName;
28259701Sdim}
29259701Sdim
30259701SdimDiagnostics::Context::Context(MatcherArgEnum, Diagnostics *Error,
31259701Sdim                              StringRef MatcherName,
32296417Sdim                              SourceRange MatcherRange,
33259701Sdim                              unsigned ArgNumber)
34259701Sdim    : Error(Error) {
35259701Sdim  Error->pushContextFrame(CT_MatcherArg, MatcherRange) << ArgNumber
36259701Sdim                                                       << MatcherName;
37259701Sdim}
38259701Sdim
39259701SdimDiagnostics::Context::~Context() { Error->ContextStack.pop_back(); }
40259701Sdim
41259701SdimDiagnostics::OverloadContext::OverloadContext(Diagnostics *Error)
42259701Sdim    : Error(Error), BeginIndex(Error->Errors.size()) {}
43259701Sdim
44259701SdimDiagnostics::OverloadContext::~OverloadContext() {
45259701Sdim  // Merge all errors that happened while in this context.
46259701Sdim  if (BeginIndex < Error->Errors.size()) {
47259701Sdim    Diagnostics::ErrorContent &Dest = Error->Errors[BeginIndex];
48259701Sdim    for (size_t i = BeginIndex + 1, e = Error->Errors.size(); i < e; ++i) {
49259701Sdim      Dest.Messages.push_back(Error->Errors[i].Messages[0]);
50259701Sdim    }
51259701Sdim    Error->Errors.resize(BeginIndex + 1);
52259701Sdim  }
53259701Sdim}
54259701Sdim
55259701Sdimvoid Diagnostics::OverloadContext::revertErrors() {
56259701Sdim  // Revert the errors.
57259701Sdim  Error->Errors.resize(BeginIndex);
58259701Sdim}
59259701Sdim
60259701SdimDiagnostics::ArgStream &Diagnostics::ArgStream::operator<<(const Twine &Arg) {
61259701Sdim  Out->push_back(Arg.str());
62259701Sdim  return *this;
63259701Sdim}
64259701Sdim
65296417SdimDiagnostics::ArgStream Diagnostics::addError(SourceRange Range,
66259701Sdim                                             ErrorType Error) {
67288943Sdim  Errors.emplace_back();
68259701Sdim  ErrorContent &Last = Errors.back();
69259701Sdim  Last.ContextStack = ContextStack;
70288943Sdim  Last.Messages.emplace_back();
71259701Sdim  Last.Messages.back().Range = Range;
72259701Sdim  Last.Messages.back().Type = Error;
73259701Sdim  return ArgStream(&Last.Messages.back().Args);
74259701Sdim}
75259701Sdim
76288943Sdimstatic StringRef contextTypeToFormatString(Diagnostics::ContextType Type) {
77259701Sdim  switch (Type) {
78259701Sdim    case Diagnostics::CT_MatcherConstruct:
79259701Sdim      return "Error building matcher $0.";
80259701Sdim    case Diagnostics::CT_MatcherArg:
81259701Sdim      return "Error parsing argument $0 for matcher $1.";
82259701Sdim  }
83259701Sdim  llvm_unreachable("Unknown ContextType value.");
84259701Sdim}
85259701Sdim
86288943Sdimstatic StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) {
87259701Sdim  switch (Type) {
88276479Sdim  case Diagnostics::ET_RegistryMatcherNotFound:
89259701Sdim    return "Matcher not found: $0";
90259701Sdim  case Diagnostics::ET_RegistryWrongArgCount:
91259701Sdim    return "Incorrect argument count. (Expected = $0) != (Actual = $1)";
92259701Sdim  case Diagnostics::ET_RegistryWrongArgType:
93259701Sdim    return "Incorrect type for arg $0. (Expected = $1) != (Actual = $2)";
94259701Sdim  case Diagnostics::ET_RegistryNotBindable:
95259701Sdim    return "Matcher does not support binding.";
96259701Sdim  case Diagnostics::ET_RegistryAmbiguousOverload:
97259701Sdim    // TODO: Add type info about the overload error.
98259701Sdim    return "Ambiguous matcher overload.";
99276479Sdim  case Diagnostics::ET_RegistryValueNotFound:
100276479Sdim    return "Value not found: $0";
101259701Sdim
102259701Sdim  case Diagnostics::ET_ParserStringError:
103259701Sdim    return "Error parsing string token: <$0>";
104259701Sdim  case Diagnostics::ET_ParserNoOpenParen:
105259701Sdim    return "Error parsing matcher. Found token <$0> while looking for '('.";
106259701Sdim  case Diagnostics::ET_ParserNoCloseParen:
107259701Sdim    return "Error parsing matcher. Found end-of-code while looking for ')'.";
108259701Sdim  case Diagnostics::ET_ParserNoComma:
109259701Sdim    return "Error parsing matcher. Found token <$0> while looking for ','.";
110259701Sdim  case Diagnostics::ET_ParserNoCode:
111259701Sdim    return "End of code found while looking for token.";
112259701Sdim  case Diagnostics::ET_ParserNotAMatcher:
113259701Sdim    return "Input value is not a matcher expression.";
114259701Sdim  case Diagnostics::ET_ParserInvalidToken:
115259701Sdim    return "Invalid token <$0> found when looking for a value.";
116259701Sdim  case Diagnostics::ET_ParserMalformedBindExpr:
117259701Sdim    return "Malformed bind() expression.";
118259701Sdim  case Diagnostics::ET_ParserTrailingCode:
119259701Sdim    return "Expected end of code.";
120321369Sdim  case Diagnostics::ET_ParserNumberError:
121321369Sdim    return "Error parsing numeric literal: <$0>";
122259701Sdim  case Diagnostics::ET_ParserOverloadedType:
123259701Sdim    return "Input value has unresolved overloaded type: $0";
124259701Sdim
125259701Sdim  case Diagnostics::ET_None:
126259701Sdim    return "<N/A>";
127259701Sdim  }
128259701Sdim  llvm_unreachable("Unknown ErrorType value.");
129259701Sdim}
130259701Sdim
131288943Sdimstatic void formatErrorString(StringRef FormatString,
132288943Sdim                              ArrayRef<std::string> Args,
133288943Sdim                              llvm::raw_ostream &OS) {
134259701Sdim  while (!FormatString.empty()) {
135259701Sdim    std::pair<StringRef, StringRef> Pieces = FormatString.split("$");
136259701Sdim    OS << Pieces.first.str();
137259701Sdim    if (Pieces.second.empty()) break;
138259701Sdim
139259701Sdim    const char Next = Pieces.second.front();
140259701Sdim    FormatString = Pieces.second.drop_front();
141259701Sdim    if (Next >= '0' && Next <= '9') {
142259701Sdim      const unsigned Index = Next - '0';
143259701Sdim      if (Index < Args.size()) {
144259701Sdim        OS << Args[Index];
145259701Sdim      } else {
146259701Sdim        OS << "<Argument_Not_Provided>";
147259701Sdim      }
148259701Sdim    }
149259701Sdim  }
150259701Sdim}
151259701Sdim
152296417Sdimstatic void maybeAddLineAndColumn(SourceRange Range,
153259701Sdim                                  llvm::raw_ostream &OS) {
154259701Sdim  if (Range.Start.Line > 0 && Range.Start.Column > 0) {
155259701Sdim    OS << Range.Start.Line << ":" << Range.Start.Column << ": ";
156259701Sdim  }
157259701Sdim}
158259701Sdim
159259701Sdimstatic void printContextFrameToStream(const Diagnostics::ContextFrame &Frame,
160259701Sdim                                      llvm::raw_ostream &OS) {
161259701Sdim  maybeAddLineAndColumn(Frame.Range, OS);
162259701Sdim  formatErrorString(contextTypeToFormatString(Frame.Type), Frame.Args, OS);
163259701Sdim}
164259701Sdim
165259701Sdimstatic void
166259701SdimprintMessageToStream(const Diagnostics::ErrorContent::Message &Message,
167259701Sdim                     const Twine Prefix, llvm::raw_ostream &OS) {
168259701Sdim  maybeAddLineAndColumn(Message.Range, OS);
169259701Sdim  OS << Prefix;
170259701Sdim  formatErrorString(errorTypeToFormatString(Message.Type), Message.Args, OS);
171259701Sdim}
172259701Sdim
173259701Sdimstatic void printErrorContentToStream(const Diagnostics::ErrorContent &Content,
174259701Sdim                                      llvm::raw_ostream &OS) {
175259701Sdim  if (Content.Messages.size() == 1) {
176259701Sdim    printMessageToStream(Content.Messages[0], "", OS);
177259701Sdim  } else {
178259701Sdim    for (size_t i = 0, e = Content.Messages.size(); i != e; ++i) {
179259701Sdim      if (i != 0) OS << "\n";
180259701Sdim      printMessageToStream(Content.Messages[i],
181259701Sdim                           "Candidate " + Twine(i + 1) + ": ", OS);
182259701Sdim    }
183259701Sdim  }
184259701Sdim}
185259701Sdim
186259701Sdimvoid Diagnostics::printToStream(llvm::raw_ostream &OS) const {
187259701Sdim  for (size_t i = 0, e = Errors.size(); i != e; ++i) {
188259701Sdim    if (i != 0) OS << "\n";
189259701Sdim    printErrorContentToStream(Errors[i], OS);
190259701Sdim  }
191259701Sdim}
192259701Sdim
193259701Sdimstd::string Diagnostics::toString() const {
194259701Sdim  std::string S;
195259701Sdim  llvm::raw_string_ostream OS(S);
196259701Sdim  printToStream(OS);
197259701Sdim  return OS.str();
198259701Sdim}
199259701Sdim
200259701Sdimvoid Diagnostics::printToStreamFull(llvm::raw_ostream &OS) const {
201259701Sdim  for (size_t i = 0, e = Errors.size(); i != e; ++i) {
202259701Sdim    if (i != 0) OS << "\n";
203259701Sdim    const ErrorContent &Error = Errors[i];
204259701Sdim    for (size_t i = 0, e = Error.ContextStack.size(); i != e; ++i) {
205259701Sdim      printContextFrameToStream(Error.ContextStack[i], OS);
206259701Sdim      OS << "\n";
207259701Sdim    }
208259701Sdim    printErrorContentToStream(Error, OS);
209259701Sdim  }
210259701Sdim}
211259701Sdim
212259701Sdimstd::string Diagnostics::toStringFull() const {
213259701Sdim  std::string S;
214259701Sdim  llvm::raw_string_ostream OS(S);
215259701Sdim  printToStreamFull(OS);
216259701Sdim  return OS.str();
217259701Sdim}
218259701Sdim
219259701Sdim}  // namespace dynamic
220259701Sdim}  // namespace ast_matchers
221259701Sdim}  // namespace clang
222