• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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