1 //===--- Diagnostics.cpp - Helper class for error diagnostics ---*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/ASTMatchers/Dynamic/Diagnostics.h"
12 namespace ast_matchers
{
14 Diagnostics::ArgStream
Diagnostics::pushContextFrame(ContextType Type
,
16 ContextStack
.emplace_back();
17 ContextFrame
& data
= ContextStack
.back();
20 return ArgStream(&data
.Args
);
23 Diagnostics::Context::Context(ConstructMatcherEnum
, Diagnostics
*Error
,
24 StringRef MatcherName
,
25 SourceRange MatcherRange
)
27 Error
->pushContextFrame(CT_MatcherConstruct
, MatcherRange
) << MatcherName
;
30 Diagnostics::Context::Context(MatcherArgEnum
, Diagnostics
*Error
,
31 StringRef MatcherName
,
32 SourceRange MatcherRange
,
35 Error
->pushContextFrame(CT_MatcherArg
, MatcherRange
) << ArgNumber
39 Diagnostics::Context::~Context() { Error
->ContextStack
.pop_back(); }
41 Diagnostics::OverloadContext::OverloadContext(Diagnostics
*Error
)
42 : Error(Error
), BeginIndex(Error
->Errors
.size()) {}
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]);
51 Error
->Errors
.resize(BeginIndex
+ 1);
55 void Diagnostics::OverloadContext::revertErrors() {
57 Error
->Errors
.resize(BeginIndex
);
60 Diagnostics::ArgStream
&Diagnostics::ArgStream::operator<<(const Twine
&Arg
) {
61 Out
->push_back(Arg
.str());
65 Diagnostics::ArgStream
Diagnostics::addError(SourceRange Range
,
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
);
76 static StringRef
contextTypeToFormatString(Diagnostics::ContextType 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.";
83 llvm_unreachable("Unknown ContextType value.");
86 static StringRef
errorTypeToFormatString(Diagnostics::ErrorType 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.";
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.";
135 case Diagnostics::ET_None
:
138 llvm_unreachable("Unknown ErrorType value.");
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;
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()) {
156 OS
<< "<Argument_Not_Provided>";
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
<< ": ";
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
);
176 printMessageToStream(const Diagnostics::ErrorContent::Message
&Message
,
177 const Twine Prefix
, llvm::raw_ostream
&OS
) {
178 maybeAddLineAndColumn(Message
.Range
, OS
);
180 formatErrorString(errorTypeToFormatString(Message
.Type
), Message
.Args
, OS
);
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
);
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
);
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
);
203 std::string
Diagnostics::toString() const {
205 llvm::raw_string_ostream
OS(S
);
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
);
218 printErrorContentToStream(Error
, OS
);
222 std::string
Diagnostics::toStringFull() const {
224 llvm::raw_string_ostream
OS(S
);
225 printToStreamFull(OS
);
229 } // namespace dynamic
230 } // namespace ast_matchers