1 //===- YAMLRemarkParser.cpp -----------------------------------------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file provides utility methods used by clients that want to use the 10 // parser for remark diagnostics in LLVM. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "YAMLRemarkParser.h" 15 #include "llvm/ADT/StringSwitch.h" 16 #include "llvm/Remarks/RemarkParser.h" 17 18 using namespace llvm; 19 using namespace llvm::remarks; 20 21 char YAMLParseError::ID = 0; 22 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(); 27 } 28 29 return make_error<YAMLParseError>("key is not a string.", Node); 30 } 31 32 template <typename T> 33 Error YAMLRemarkParser::parseStr(T &Result, yaml::KeyValueNode &Node) { 34 auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); 35 if (!Value) 36 return make_error<YAMLParseError>("expected a value of scalar type.", Node); 37 StringRef Tmp = Value->getRawValue(); 38 39 if (Tmp.front() == '\'') 40 Tmp = Tmp.drop_front(); 41 42 if (Tmp.back() == '\'') 43 Tmp = Tmp.drop_back(); 44 45 Result = Tmp; 46 47 return Error::success(); 48 } 49 50 template <typename T> 51 Error YAMLRemarkParser::parseUnsigned(T &Result, yaml::KeyValueNode &Node) { 52 SmallVector<char, 4> Tmp; 53 auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); 54 if (!Value) 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.", 59 *Value); 60 Result = UnsignedValue; 61 return Error::success(); 62 } 63 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); 75 Result = Type; 76 return Error::success(); 77 } 78 79 Error YAMLRemarkParser::parseDebugLoc(Optional<RemarkLocation> &Result, 80 yaml::KeyValueNode &Node) { 81 auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue()); 82 if (!DebugLoc) 83 return make_error<YAMLParseError>("expected a value of mapping type.", 84 Node); 85 86 Optional<StringRef> File; 87 Optional<unsigned> Line; 88 Optional<unsigned> Column; 89 90 for (yaml::KeyValueNode &DLNode : *DebugLoc) { 91 StringRef KeyName; 92 if (Error E = parseKey(KeyName, DLNode)) 93 return E; 94 if (KeyName == "File") { 95 if (Error E = parseStr(File, DLNode)) 96 return E; 97 } else if (KeyName == "Column") { 98 if (Error E = parseUnsigned(Column, DLNode)) 99 return E; 100 } else if (KeyName == "Line") { 101 if (Error E = parseUnsigned(Line, DLNode)) 102 return E; 103 } else { 104 return make_error<YAMLParseError>("unknown entry in DebugLoc map.", 105 DLNode); 106 } 107 } 108 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); 112 113 Result = RemarkLocation{*File, *Line, *Column}; 114 115 return Error::success(); 116 } 117 118 Error YAMLRemarkParser::parseRemarkField(yaml::KeyValueNode &RemarkField) { 119 120 StringRef KeyName; 121 if (Error E = parseKey(KeyName, RemarkField)) 122 return E; 123 124 if (KeyName == "Pass") { 125 if (Error E = parseStr(State->TheRemark.PassName, RemarkField)) 126 return E; 127 } else if (KeyName == "Name") { 128 if (Error E = parseStr(State->TheRemark.RemarkName, RemarkField)) 129 return E; 130 } else if (KeyName == "Function") { 131 if (Error E = parseStr(State->TheRemark.FunctionName, RemarkField)) 132 return E; 133 } else if (KeyName == "Hotness") { 134 State->TheRemark.Hotness = 0; 135 if (Error E = parseUnsigned(*State->TheRemark.Hotness, RemarkField)) 136 return E; 137 } else if (KeyName == "DebugLoc") { 138 if (Error E = parseDebugLoc(State->TheRemark.Loc, RemarkField)) 139 return E; 140 } else if (KeyName == "Args") { 141 auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue()); 142 if (!Args) 143 return make_error<YAMLParseError>("wrong value type for key.", 144 RemarkField); 145 146 for (yaml::Node &Arg : *Args) 147 if (Error E = parseArg(State->Args, Arg)) 148 return E; 149 150 State->TheRemark.Args = State->Args; 151 } else { 152 return make_error<YAMLParseError>("unknown key.", RemarkField); 153 } 154 155 return Error::success(); 156 } 157 158 Error YAMLRemarkParser::parseArg(SmallVectorImpl<Argument> &Args, 159 yaml::Node &Node) { 160 auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node); 161 if (!ArgMap) 162 return make_error<YAMLParseError>("expected a value of mapping type.", 163 Node); 164 165 StringRef KeyStr; 166 StringRef ValueStr; 167 Optional<RemarkLocation> Loc; 168 169 for (yaml::KeyValueNode &ArgEntry : *ArgMap) 170 if (Error E = parseArgEntry(ArgEntry, KeyStr, ValueStr, Loc)) 171 return E; 172 173 if (KeyStr.empty()) 174 return make_error<YAMLParseError>("argument key is missing.", *ArgMap); 175 if (ValueStr.empty()) 176 return make_error<YAMLParseError>("argument value is missing.", *ArgMap); 177 178 Args.push_back(Argument{KeyStr, ValueStr, Loc}); 179 180 return Error::success(); 181 } 182 183 Error YAMLRemarkParser::parseArgEntry(yaml::KeyValueNode &ArgEntry, 184 StringRef &KeyStr, StringRef &ValueStr, 185 Optional<RemarkLocation> &Loc) { 186 StringRef KeyName; 187 if (Error E = parseKey(KeyName, ArgEntry)) 188 return E; 189 190 // Try to parse debug locs. 191 if (KeyName == "DebugLoc") { 192 // Can't have multiple DebugLoc entries per argument. 193 if (Loc) 194 return make_error<YAMLParseError>( 195 "only one DebugLoc entry is allowed per argument.", ArgEntry); 196 197 if (Error E = parseDebugLoc(Loc, ArgEntry)) 198 return E; 199 return Error::success(); 200 } 201 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); 206 207 // Try to parse a string. 208 if (Error E = parseStr(ValueStr, ArgEntry)) 209 return E; 210 211 // Keep the key from the string. 212 KeyStr = KeyName; 213 return Error::success(); 214 } 215 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); 220 221 yaml::Node *YAMLRoot = Remark.getRoot(); 222 if (!YAMLRoot) 223 return createStringError(std::make_error_code(std::errc::invalid_argument), 224 "not a valid YAML file."); 225 226 auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot); 227 if (!Root) 228 return make_error<YAMLParseError>("document root is not of mapping type.", 229 *YAMLRoot); 230 231 if (Error E = parseType(State->TheRemark.RemarkType, *Root)) 232 return E; 233 234 for (yaml::KeyValueNode &RemarkField : *Root) 235 if (Error E = parseRemarkField(RemarkField)) 236 return E; 237 238 // If the YAML parsing failed, don't even continue parsing. We might 239 // encounter malformed YAML. 240 if (Stream.failed()) 241 return make_error<YAMLParseError>("YAML parsing failed.", 242 *Remark.getRoot()); 243 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.", 250 *Remark.getRoot()); 251 252 return Error::success(); 253 } 254 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); 262 } 263