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
22 namespace ark::es2panda::parser {
ETSNolintParser(const ParserImpl * mainParser)23 ETSNolintParser::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
SetStartPos()37 void 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
CollectETSNolints()46 void 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
ApplyETSNolintsToStatements(ArenaVector<ir::Statement * > & statements) const85 void ETSNolintParser::ApplyETSNolintsToStatements(ArenaVector<ir::Statement *> &statements) const
86 {
87 for (auto *it : statements) {
88 ApplyETSNolintsToNodesRecursively(it);
89 }
90 }
91
NextSymbol()92 void 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
BackwardSymbol()105 void 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
NextSymbol(std::size_t i)115 void ETSNolintParser::NextSymbol(std::size_t i)
116 {
117 for (; i > 0; --i) {
118 NextSymbol();
119 }
120 }
121
BackwardSymbol(std::size_t i)122 void ETSNolintParser::BackwardSymbol(std::size_t i)
123 {
124 for (; i > 0; --i) {
125 BackwardSymbol();
126 }
127 }
128
RewindToStart() const129 void ETSNolintParser::RewindToStart() const
130 {
131 parser_->Lexer()->Pos().Iterator().Backward(posOffset_ - startPos_);
132 }
133
AddToETSNolintLinesCollection(std::size_t line,const std::set<ETSWarnings> & collection)134 void 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
PeekSymbol() const145 char32_t ETSNolintParser::PeekSymbol() const
146 {
147 return parser_->Lexer()->Pos().Iterator().Peek();
148 }
149
TryPeekU32String(const std::u32string & u32str)150 bool 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
IsEtsNolint()167 bool 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
IsNextLine()188 bool 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
IsBegin()198 bool 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
IsEnd()207 bool 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
MapETSNolintArg(const std::u32string & warningName) const215 ETSWarnings 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
ValidETSNolintArg(const std::u32string & warningName) const223 bool ETSNolintParser::ValidETSNolintArg(const std::u32string &warningName) const
224 {
225 return warningsMap_.find(warningName) != warningsMap_.end();
226 }
227
ParseETSNolintArgs()228 std::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
IsLineWithETSNolint(const std::size_t line) const271 bool ETSNolintParser::IsLineWithETSNolint(const std::size_t line) const
272 {
273 return linesCollection_.find(line) != linesCollection_.end();
274 }
275
GetWarningsCollectionByLine(std::size_t line) const276 std::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
ApplyETSNolintsToNodesRecursively(ir::AstNode * node) const282 void 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