1 //===- YAMLRemarkParser.cpp -----------------------------------------------===//
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 // This file provides utility methods used by clients that want to use the
10 // parser for remark diagnostics in LLVM.
12 //===----------------------------------------------------------------------===//
14 #include "YAMLRemarkParser.h"
15 #include "llvm/ADT/StringSwitch.h"
16 #include "llvm/Remarks/RemarkParser.h"
19 using namespace llvm::remarks
;
21 char YAMLParseError::ID
= 0;
23 Error
YAMLRemarkParser::parseKey(StringRef
&Result
, yaml::KeyValueNode
&Node
) {
24 if (auto *Key
= dyn_cast
<yaml::ScalarNode
>(Node
.getKey())) {
25 Result
= Key
->getRawValue();
26 return Error::success();
29 return make_error
<YAMLParseError
>("key is not a string.", Node
);
33 Error
YAMLRemarkParser::parseStr(T
&Result
, yaml::KeyValueNode
&Node
) {
34 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
36 return make_error
<YAMLParseError
>("expected a value of scalar type.", Node
);
37 StringRef Tmp
= Value
->getRawValue();
39 if (Tmp
.front() == '\'')
40 Tmp
= Tmp
.drop_front();
42 if (Tmp
.back() == '\'')
43 Tmp
= Tmp
.drop_back();
47 return Error::success();
51 Error
YAMLRemarkParser::parseUnsigned(T
&Result
, yaml::KeyValueNode
&Node
) {
52 SmallVector
<char, 4> Tmp
;
53 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
55 return make_error
<YAMLParseError
>("expected a value of scalar type.", Node
);
56 unsigned UnsignedValue
= 0;
57 if (Value
->getValue(Tmp
).getAsInteger(10, UnsignedValue
))
58 return make_error
<YAMLParseError
>("expected a value of integer type.",
60 Result
= UnsignedValue
;
61 return Error::success();
64 Error
YAMLRemarkParser::parseType(Type
&Result
, yaml::MappingNode
&Node
) {
65 auto Type
= StringSwitch
<remarks::Type
>(Node
.getRawTag())
66 .Case("!Passed", remarks::Type::Passed
)
67 .Case("!Missed", remarks::Type::Missed
)
68 .Case("!Analysis", remarks::Type::Analysis
)
69 .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute
)
70 .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing
)
71 .Case("!Failure", remarks::Type::Failure
)
72 .Default(remarks::Type::Unknown
);
73 if (Type
== remarks::Type::Unknown
)
74 return make_error
<YAMLParseError
>("expected a remark tag.", Node
);
76 return Error::success();
79 Error
YAMLRemarkParser::parseDebugLoc(Optional
<RemarkLocation
> &Result
,
80 yaml::KeyValueNode
&Node
) {
81 auto *DebugLoc
= dyn_cast
<yaml::MappingNode
>(Node
.getValue());
83 return make_error
<YAMLParseError
>("expected a value of mapping type.",
86 Optional
<StringRef
> File
;
87 Optional
<unsigned> Line
;
88 Optional
<unsigned> Column
;
90 for (yaml::KeyValueNode
&DLNode
: *DebugLoc
) {
92 if (Error E
= parseKey(KeyName
, DLNode
))
94 if (KeyName
== "File") {
95 if (Error E
= parseStr(File
, DLNode
))
97 } else if (KeyName
== "Column") {
98 if (Error E
= parseUnsigned(Column
, DLNode
))
100 } else if (KeyName
== "Line") {
101 if (Error E
= parseUnsigned(Line
, DLNode
))
104 return make_error
<YAMLParseError
>("unknown entry in DebugLoc map.",
109 // If any of the debug loc fields is missing, return an error.
110 if (!File
|| !Line
|| !Column
)
111 return make_error
<YAMLParseError
>("DebugLoc node incomplete.", Node
);
113 Result
= RemarkLocation
{*File
, *Line
, *Column
};
115 return Error::success();
118 Error
YAMLRemarkParser::parseRemarkField(yaml::KeyValueNode
&RemarkField
) {
121 if (Error E
= parseKey(KeyName
, RemarkField
))
124 if (KeyName
== "Pass") {
125 if (Error E
= parseStr(State
->TheRemark
.PassName
, RemarkField
))
127 } else if (KeyName
== "Name") {
128 if (Error E
= parseStr(State
->TheRemark
.RemarkName
, RemarkField
))
130 } else if (KeyName
== "Function") {
131 if (Error E
= parseStr(State
->TheRemark
.FunctionName
, RemarkField
))
133 } else if (KeyName
== "Hotness") {
134 State
->TheRemark
.Hotness
= 0;
135 if (Error E
= parseUnsigned(*State
->TheRemark
.Hotness
, RemarkField
))
137 } else if (KeyName
== "DebugLoc") {
138 if (Error E
= parseDebugLoc(State
->TheRemark
.Loc
, RemarkField
))
140 } else if (KeyName
== "Args") {
141 auto *Args
= dyn_cast
<yaml::SequenceNode
>(RemarkField
.getValue());
143 return make_error
<YAMLParseError
>("wrong value type for key.",
146 for (yaml::Node
&Arg
: *Args
)
147 if (Error E
= parseArg(State
->Args
, Arg
))
150 State
->TheRemark
.Args
= State
->Args
;
152 return make_error
<YAMLParseError
>("unknown key.", RemarkField
);
155 return Error::success();
158 Error
YAMLRemarkParser::parseArg(SmallVectorImpl
<Argument
> &Args
,
160 auto *ArgMap
= dyn_cast
<yaml::MappingNode
>(&Node
);
162 return make_error
<YAMLParseError
>("expected a value of mapping type.",
167 Optional
<RemarkLocation
> Loc
;
169 for (yaml::KeyValueNode
&ArgEntry
: *ArgMap
)
170 if (Error E
= parseArgEntry(ArgEntry
, KeyStr
, ValueStr
, Loc
))
174 return make_error
<YAMLParseError
>("argument key is missing.", *ArgMap
);
175 if (ValueStr
.empty())
176 return make_error
<YAMLParseError
>("argument value is missing.", *ArgMap
);
178 Args
.push_back(Argument
{KeyStr
, ValueStr
, Loc
});
180 return Error::success();
183 Error
YAMLRemarkParser::parseArgEntry(yaml::KeyValueNode
&ArgEntry
,
184 StringRef
&KeyStr
, StringRef
&ValueStr
,
185 Optional
<RemarkLocation
> &Loc
) {
187 if (Error E
= parseKey(KeyName
, ArgEntry
))
190 // Try to parse debug locs.
191 if (KeyName
== "DebugLoc") {
192 // Can't have multiple DebugLoc entries per argument.
194 return make_error
<YAMLParseError
>(
195 "only one DebugLoc entry is allowed per argument.", ArgEntry
);
197 if (Error E
= parseDebugLoc(Loc
, ArgEntry
))
199 return Error::success();
202 // If we already have a string, error out.
203 if (!ValueStr
.empty())
204 return make_error
<YAMLParseError
>(
205 "only one string entry is allowed per argument.", ArgEntry
);
207 // Try to parse a string.
208 if (Error E
= parseStr(ValueStr
, ArgEntry
))
211 // Keep the key from the string.
213 return Error::success();
216 Error
YAMLRemarkParser::parseYAMLElement(yaml::Document
&Remark
) {
217 // Parsing a new remark, clear the previous one by re-constructing the state
218 // in-place in the Optional.
219 State
.emplace(TmpArgs
);
221 yaml::Node
*YAMLRoot
= Remark
.getRoot();
223 return createStringError(std::make_error_code(std::errc::invalid_argument
),
224 "not a valid YAML file.");
226 auto *Root
= dyn_cast
<yaml::MappingNode
>(YAMLRoot
);
228 return make_error
<YAMLParseError
>("document root is not of mapping type.",
231 if (Error E
= parseType(State
->TheRemark
.RemarkType
, *Root
))
234 for (yaml::KeyValueNode
&RemarkField
: *Root
)
235 if (Error E
= parseRemarkField(RemarkField
))
238 // If the YAML parsing failed, don't even continue parsing. We might
239 // encounter malformed YAML.
241 return make_error
<YAMLParseError
>("YAML parsing failed.",
244 // Check if any of the mandatory fields are missing.
245 if (State
->TheRemark
.RemarkType
== Type::Unknown
||
246 State
->TheRemark
.PassName
.empty() ||
247 State
->TheRemark
.RemarkName
.empty() ||
248 State
->TheRemark
.FunctionName
.empty())
249 return make_error
<YAMLParseError
>("Type, Pass, Name or Function missing.",
252 return Error::success();
255 /// Handle a diagnostic from the YAML stream. Records the error in the
256 /// YAMLRemarkParser class.
257 void YAMLRemarkParser::HandleDiagnostic(const SMDiagnostic
&Diag
, void *Ctx
) {
258 assert(Ctx
&& "Expected non-null Ctx in diagnostic handler.");
259 auto *Parser
= static_cast<YAMLRemarkParser
*>(Ctx
);
260 Diag
.print(/*ProgName=*/nullptr, Parser
->ErrorStream
, /*ShowColors*/ false,
261 /*ShowKindLabels*/ true);