1/** 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ETSparser.h" 17#include "ETSNolintParser.h" 18 19#include "lexer/lexer.h" 20#include "ir/module/importNamespaceSpecifier.h" 21 22namespace ark::es2panda::parser { 23ETSNolintParser::ETSNolintParser(const ParserImpl *mainParser) : parser_(mainParser) 24{ 25 line_ = parser_->Lexer()->Line(); 26 27 warningsMap_ = { 28 {std::u32string(U"ets-implicit-boxing-unboxing"), ETSWarnings::IMPLICIT_BOXING_UNBOXING}, 29 {std::u32string(U"ets-prohibit-top-level-statements"), ETSWarnings::PROHIBIT_TOP_LEVEL_STATEMENTS}, 30 {std::u32string(U"ets-boost-equality-statement"), ETSWarnings::BOOST_EQUALITY_STATEMENT}, 31 {std::u32string(U"ets-remove-lambda"), ETSWarnings::REMOVE_LAMBDA}, 32 {std::u32string(U"ets-suggest-final"), ETSWarnings::SUGGEST_FINAL}, 33 {std::u32string(U"ets-remove-async"), ETSWarnings::REMOVE_ASYNC_FUNCTIONS}, 34 }; 35} 36 37void ETSNolintParser::SetStartPos() 38{ 39 line_ = parser_->Lexer()->Pos().Line(); 40 startPos_ = parser_->Lexer()->Pos().Iterator().Index(); 41 posOffset_ = startPos_; 42 43 BackwardSymbol(startPos_); 44} 45 46void ETSNolintParser::CollectETSNolints() 47{ 48 SetStartPos(); 49 char32_t cp = PeekSymbol(); 50 51 while (cp != lexer::LEX_CHAR_EOS && cp != lexer::UNICODE_CODE_POINT_MAX && cp != lexer::UNICODE_INVALID_CP) { 52 if (!IsEtsNolint()) { 53 NextSymbol(); 54 cp = PeekSymbol(); 55 continue; 56 } 57 std::size_t line = line_; 58 std::set<ETSWarnings> collection; 59 if (IsNextLine()) { 60 collection = ParseETSNolintArgs(); 61 line += 1; 62 } else if (IsBegin()) { 63 collection = ParseETSNolintArgs(); 64 for (const auto it : collection) { 65 applyingCollection_.insert(it); 66 } 67 } else if (IsEnd()) { 68 collection = ParseETSNolintArgs(); 69 70 for (const auto it : collection) { 71 applyingCollection_.erase(it); 72 } 73 cp = PeekSymbol(); 74 continue; 75 } else { 76 collection = ParseETSNolintArgs(); 77 } 78 AddToETSNolintLinesCollection(line, collection); 79 cp = PeekSymbol(); 80 } 81 82 RewindToStart(); 83} 84 85void ETSNolintParser::ApplyETSNolintsToStatements(ArenaVector<ir::Statement *> &statements) const 86{ 87 for (auto *it : statements) { 88 ApplyETSNolintsToNodesRecursively(it); 89 } 90} 91 92void ETSNolintParser::NextSymbol() 93{ 94 if (PeekSymbol() == lexer::LEX_CHAR_LF) { 95 if (!applyingCollection_.empty()) { 96 AddToETSNolintLinesCollection(line_, applyingCollection_); 97 } 98 line_++; 99 } 100 101 posOffset_++; 102 parser_->Lexer()->Pos().Iterator().Forward(1); 103} 104 105void ETSNolintParser::BackwardSymbol() 106{ 107 posOffset_--; 108 parser_->Lexer()->Pos().Iterator().Backward(1); 109 110 if (PeekSymbol() == lexer::LEX_CHAR_LF) { 111 line_--; 112 } 113} 114 115void ETSNolintParser::NextSymbol(std::size_t i) 116{ 117 for (; i > 0; --i) { 118 NextSymbol(); 119 } 120} 121 122void ETSNolintParser::BackwardSymbol(std::size_t i) 123{ 124 for (; i > 0; --i) { 125 BackwardSymbol(); 126 } 127} 128 129void ETSNolintParser::RewindToStart() const 130{ 131 parser_->Lexer()->Pos().Iterator().Backward(posOffset_ - startPos_); 132} 133 134void ETSNolintParser::AddToETSNolintLinesCollection(std::size_t line, const std::set<ETSWarnings> &collection) 135{ 136 const auto search = linesCollection_.find(line); 137 if (search != linesCollection_.end()) { 138 search->second.insert(collection.begin(), collection.end()); 139 return; 140 } 141 142 linesCollection_.insert({line, collection}); 143} 144 145char32_t ETSNolintParser::PeekSymbol() const 146{ 147 return parser_->Lexer()->Pos().Iterator().Peek(); 148} 149 150bool ETSNolintParser::TryPeekU32String(const std::u32string &u32str) 151{ 152 std::size_t localPosOffset = 0; 153 char32_t cp; 154 155 for (const char32_t i : u32str) { 156 cp = PeekSymbol(); 157 if (i != cp) { 158 BackwardSymbol(localPosOffset); 159 return false; 160 } 161 NextSymbol(); 162 localPosOffset++; 163 } 164 return true; 165} 166 167bool ETSNolintParser::IsEtsNolint() 168{ 169 static const std::u32string ETSNOLINT_CHAR32T = { 170 lexer::LEX_CHAR_UPPERCASE_E, lexer::LEX_CHAR_UPPERCASE_T, lexer::LEX_CHAR_UPPERCASE_S, 171 lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_O, lexer::LEX_CHAR_UPPERCASE_L, 172 lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_T}; 173 174 char32_t cp; 175 176 for (unsigned long i = 0; i < ETSNOLINT_CHAR32T.length(); i++) { 177 cp = PeekSymbol(); 178 if (ETSNOLINT_CHAR32T[i] != cp) { 179 return false; 180 } 181 182 NextSymbol(); 183 } 184 185 return true; 186} 187 188bool ETSNolintParser::IsNextLine() 189{ 190 static const std::u32string NEXTLINE_CHAR32T = { 191 lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_E, 192 lexer::LEX_CHAR_UPPERCASE_X, lexer::LEX_CHAR_UPPERCASE_T, lexer::LEX_CHAR_UPPERCASE_L, 193 lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_E}; 194 195 return TryPeekU32String(NEXTLINE_CHAR32T); 196} 197 198bool ETSNolintParser::IsBegin() 199{ 200 static const std::u32string BEGIN_CHAR32T = {lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_B, 201 lexer::LEX_CHAR_UPPERCASE_E, lexer::LEX_CHAR_UPPERCASE_G, 202 lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N}; 203 204 return TryPeekU32String(BEGIN_CHAR32T); 205} 206 207bool ETSNolintParser::IsEnd() 208{ 209 static const std::u32string END_CHAR32T = {lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_E, 210 lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_D}; 211 212 return TryPeekU32String(END_CHAR32T); 213} 214 215ETSWarnings ETSNolintParser::MapETSNolintArg(const std::u32string &warningName) const 216{ 217 const auto search = warningsMap_.find(warningName); 218 ASSERT(search != warningsMap_.end()); 219 220 return search->second; 221} 222 223bool ETSNolintParser::ValidETSNolintArg(const std::u32string &warningName) const 224{ 225 return warningsMap_.find(warningName) != warningsMap_.end(); 226} 227 228std::set<ETSWarnings> ETSNolintParser::ParseETSNolintArgs() 229{ 230 std::set<ETSWarnings> warningsCollection; 231 232 if (PeekSymbol() != lexer::LEX_CHAR_LEFT_PAREN) { 233 for (const auto &it : warningsMap_) { 234 warningsCollection.insert(it.second); 235 } 236 237 return warningsCollection; 238 } 239 240 NextSymbol(); 241 char32_t cp = 0; 242 std::u32string warningName; 243 244 while (cp != lexer::LEX_CHAR_SP && cp != lexer::LEX_CHAR_LF && cp != lexer::LEX_CHAR_EOS) { 245 cp = PeekSymbol(); 246 if (cp != lexer::LEX_CHAR_MINUS && cp != lexer::LEX_CHAR_COMMA && cp != lexer::LEX_CHAR_RIGHT_PAREN && 247 (cp < lexer::LEX_CHAR_LOWERCASE_A || cp > lexer::LEX_CHAR_LOWERCASE_Z)) { 248 const std::string msg = "Unexpected character for ETSNOLINT argument! [VALID ONLY: a-z, '-']."; 249 throw Error {ErrorType::SYNTAX, parser_->GetProgram()->SourceFilePath().Utf8(), msg.c_str(), line_ + 1, 0}; 250 } 251 if ((cp == lexer::LEX_CHAR_COMMA || cp == lexer::LEX_CHAR_RIGHT_PAREN) && !ValidETSNolintArg(warningName)) { 252 const std::string msg = "Invalid argument for ETSNOLINT!"; 253 throw Error {ErrorType::SYNTAX, parser_->GetProgram()->SourceFilePath().Utf8(), msg.c_str(), line_ + 1, 0}; 254 } 255 if ((cp == lexer::LEX_CHAR_COMMA || cp == lexer::LEX_CHAR_RIGHT_PAREN) && ValidETSNolintArg(warningName)) { 256 warningsCollection.insert(MapETSNolintArg(warningName)); 257 warningName.clear(); 258 } else { 259 warningName += cp; 260 } 261 if (cp == lexer::LEX_CHAR_RIGHT_PAREN) { 262 break; 263 } 264 265 NextSymbol(); 266 } 267 268 return warningsCollection; 269} 270 271bool ETSNolintParser::IsLineWithETSNolint(const std::size_t line) const 272{ 273 return linesCollection_.find(line) != linesCollection_.end(); 274} 275 276std::set<ETSWarnings> ETSNolintParser::GetWarningsCollectionByLine(std::size_t line) const 277{ 278 const auto search = linesCollection_.find(line); 279 return search == linesCollection_.end() ? std::set<ETSWarnings> {} : search->second; 280} 281 282void ETSNolintParser::ApplyETSNolintsToNodesRecursively(ir::AstNode *node) const 283{ 284 if (node == nullptr) { 285 return; 286 } 287 const std::size_t line = node->Start().line; 288 if (IsLineWithETSNolint(line)) { 289 const std::set<ETSWarnings> warningsCollection = GetWarningsCollectionByLine(line); 290 291 parser_->GetProgram()->AddNodeToETSNolintCollection(node, warningsCollection); 292 } 293 node->Iterate([&](auto *childNode) { ApplyETSNolintsToNodesRecursively(childNode); }); 294} 295} // namespace ark::es2panda::parser 296