1 /**
2 * Copyright (c) 2024-2025 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 "ETSNolintParser.h"
17
18 namespace ark::es2panda::parser {
ETSNolintParser(ParserImpl * mainParser)19 ETSNolintParser::ETSNolintParser(ParserImpl *mainParser) : parser_(mainParser)
20 {
21 line_ = parser_->Lexer()->Line();
22 }
23
SetStartPos()24 void ETSNolintParser::SetStartPos()
25 {
26 line_ = parser_->Lexer()->Pos().Line();
27 startPos_ = parser_->Lexer()->Pos().Iterator().Index();
28 posOffset_ = startPos_;
29
30 BackwardSymbol(startPos_);
31 }
32
CollectETSNolints()33 void ETSNolintParser::CollectETSNolints()
34 {
35 SetStartPos();
36 char32_t cp = PeekSymbol();
37
38 while (cp != lexer::LEX_CHAR_EOS && cp != lexer::UNICODE_CODE_POINT_MAX && cp != lexer::UNICODE_INVALID_CP) {
39 if (!IsEtsNolint()) {
40 NextSymbol();
41 cp = PeekSymbol();
42 continue;
43 }
44 std::size_t line = line_;
45 std::set<ETSWarnings> collection;
46 if (IsNextLine()) {
47 collection = ParseETSNolintArgs();
48 line += 1;
49 } else if (IsBegin()) {
50 collection = ParseETSNolintArgs();
51 for (const auto it : collection) {
52 applyingCollection_.insert(it);
53 }
54 } else if (IsEnd()) {
55 collection = ParseETSNolintArgs();
56
57 for (const auto it : collection) {
58 applyingCollection_.erase(it);
59 }
60 cp = PeekSymbol();
61 continue;
62 } else {
63 collection = ParseETSNolintArgs();
64 }
65 AddToETSNolintLinesCollection(line, collection);
66 cp = PeekSymbol();
67 }
68
69 RewindToStart();
70 }
71
ApplyETSNolintsToStatements(ArenaVector<ir::Statement * > & statements) const72 void ETSNolintParser::ApplyETSNolintsToStatements(ArenaVector<ir::Statement *> &statements) const
73 {
74 for (auto *it : statements) {
75 ApplyETSNolintsToNodesRecursively(it);
76 }
77 }
78
NextSymbol()79 void ETSNolintParser::NextSymbol()
80 {
81 if (PeekSymbol() == lexer::LEX_CHAR_LF) {
82 if (!applyingCollection_.empty()) {
83 AddToETSNolintLinesCollection(line_, applyingCollection_);
84 }
85 line_++;
86 }
87
88 posOffset_++;
89 parser_->Lexer()->Pos().Iterator().Forward(1);
90 }
91
BackwardSymbol()92 void ETSNolintParser::BackwardSymbol()
93 {
94 posOffset_--;
95 parser_->Lexer()->Pos().Iterator().Backward(1);
96
97 if (PeekSymbol() == lexer::LEX_CHAR_LF) {
98 line_--;
99 }
100 }
101
NextSymbol(std::size_t i)102 void ETSNolintParser::NextSymbol(std::size_t i)
103 {
104 for (; i > 0; --i) {
105 NextSymbol();
106 }
107 }
108
BackwardSymbol(std::size_t i)109 void ETSNolintParser::BackwardSymbol(std::size_t i)
110 {
111 for (; i > 0; --i) {
112 BackwardSymbol();
113 }
114 }
115
RewindToStart() const116 void ETSNolintParser::RewindToStart() const
117 {
118 parser_->Lexer()->Pos().Iterator().Backward(posOffset_ - startPos_);
119 }
120
AddToETSNolintLinesCollection(std::size_t line,const std::set<ETSWarnings> & collection)121 void ETSNolintParser::AddToETSNolintLinesCollection(std::size_t line, const std::set<ETSWarnings> &collection)
122 {
123 const auto search = linesCollection_.find(line);
124 if (search != linesCollection_.end()) {
125 search->second.insert(collection.begin(), collection.end());
126 return;
127 }
128
129 linesCollection_.insert({line, collection});
130 }
131
PeekSymbol() const132 char32_t ETSNolintParser::PeekSymbol() const
133 {
134 return parser_->Lexer()->Pos().Iterator().Peek();
135 }
136
TryPeekU32String(const std::u32string & u32str)137 bool ETSNolintParser::TryPeekU32String(const std::u32string &u32str)
138 {
139 std::size_t localPosOffset = 0;
140 char32_t cp;
141
142 for (const char32_t i : u32str) {
143 cp = PeekSymbol();
144 if (i != cp) {
145 BackwardSymbol(localPosOffset);
146 return false;
147 }
148 NextSymbol();
149 localPosOffset++;
150 }
151 return true;
152 }
153
IsEtsNolint()154 bool ETSNolintParser::IsEtsNolint()
155 {
156 static const std::u32string ETSNOLINT_CHAR32T = {
157 lexer::LEX_CHAR_UPPERCASE_E, lexer::LEX_CHAR_UPPERCASE_T, lexer::LEX_CHAR_UPPERCASE_S,
158 lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_O, lexer::LEX_CHAR_UPPERCASE_L,
159 lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_T};
160
161 char32_t cp;
162
163 for (unsigned long i = 0; i < ETSNOLINT_CHAR32T.length(); i++) {
164 cp = PeekSymbol();
165 if (ETSNOLINT_CHAR32T[i] != cp) {
166 return false;
167 }
168
169 NextSymbol();
170 }
171
172 return true;
173 }
174
IsNextLine()175 bool ETSNolintParser::IsNextLine()
176 {
177 static const std::u32string NEXTLINE_CHAR32T = {
178 lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_E,
179 lexer::LEX_CHAR_UPPERCASE_X, lexer::LEX_CHAR_UPPERCASE_T, lexer::LEX_CHAR_UPPERCASE_L,
180 lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_E};
181
182 return TryPeekU32String(NEXTLINE_CHAR32T);
183 }
184
IsBegin()185 bool ETSNolintParser::IsBegin()
186 {
187 static const std::u32string BEGIN_CHAR32T = {lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_B,
188 lexer::LEX_CHAR_UPPERCASE_E, lexer::LEX_CHAR_UPPERCASE_G,
189 lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N};
190
191 return TryPeekU32String(BEGIN_CHAR32T);
192 }
193
IsEnd()194 bool ETSNolintParser::IsEnd()
195 {
196 static const std::u32string END_CHAR32T = {lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_E,
197 lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_D};
198
199 return TryPeekU32String(END_CHAR32T);
200 }
201
MapETSNolintArg(const std::string & warningName) const202 ETSWarnings ETSNolintParser::MapETSNolintArg(const std::string &warningName) const
203 {
204 return util::gen::ets_warnings::FromString(warningName);
205 }
206
ValidETSNolintArg(const std::string & warningName) const207 bool ETSNolintParser::ValidETSNolintArg(const std::string &warningName) const
208 {
209 return util::gen::ets_warnings::FromString(warningName) != ETSWarnings::INVALID;
210 }
211
ParseETSNolintArgs()212 std::set<ETSWarnings> ETSNolintParser::ParseETSNolintArgs()
213 {
214 std::set<ETSWarnings> warningsCollection;
215
216 if (PeekSymbol() != lexer::LEX_CHAR_LEFT_PAREN) {
217 for (std::underlying_type_t<ETSWarnings> wid = ETSWarnings::FIRST; wid <= ETSWarnings::LAST; wid++) {
218 warningsCollection.insert(ETSWarnings {wid});
219 }
220 return warningsCollection;
221 }
222
223 NextSymbol();
224 char32_t cp = 0;
225 std::string warningName;
226
227 while (cp != lexer::LEX_CHAR_SP && cp != lexer::LEX_CHAR_LF && cp != lexer::LEX_CHAR_EOS) {
228 cp = PeekSymbol();
229 const lexer::SourcePosition sPos {line_ + 1, 0, parser_->GetProgram()};
230 if (cp != lexer::LEX_CHAR_MINUS && cp != lexer::LEX_CHAR_COMMA && cp != lexer::LEX_CHAR_RIGHT_PAREN &&
231 (cp < lexer::LEX_CHAR_LOWERCASE_A || cp > lexer::LEX_CHAR_LOWERCASE_Z)) {
232 parser_->DiagnosticEngine().LogDiagnostic(diagnostic::UNEXPECTED_CHAR_ETSNOLINT,
233 util::DiagnosticMessageParams {}, sPos);
234 }
235 if ((cp == lexer::LEX_CHAR_COMMA || cp == lexer::LEX_CHAR_RIGHT_PAREN) && !ValidETSNolintArg(warningName)) {
236 parser_->DiagnosticEngine().LogDiagnostic(diagnostic::INVALID_ARGUMENT_ETSNOLINT,
237 util::DiagnosticMessageParams {}, sPos);
238 }
239 if ((cp == lexer::LEX_CHAR_COMMA || cp == lexer::LEX_CHAR_RIGHT_PAREN) && ValidETSNolintArg(warningName)) {
240 warningsCollection.insert(MapETSNolintArg(warningName));
241 warningName.clear();
242 } else {
243 ES2PANDA_ASSERT(cp <= std::numeric_limits<unsigned char>::max());
244 warningName += cp;
245 }
246 if (cp == lexer::LEX_CHAR_RIGHT_PAREN) {
247 break;
248 }
249
250 NextSymbol();
251 }
252
253 return warningsCollection;
254 }
255
IsLineWithETSNolint(const std::size_t line) const256 bool ETSNolintParser::IsLineWithETSNolint(const std::size_t line) const
257 {
258 return linesCollection_.find(line) != linesCollection_.end();
259 }
260
GetWarningsCollectionByLine(std::size_t line) const261 std::set<ETSWarnings> ETSNolintParser::GetWarningsCollectionByLine(std::size_t line) const
262 {
263 const auto search = linesCollection_.find(line);
264 return search == linesCollection_.end() ? std::set<ETSWarnings> {} : search->second;
265 }
266
ApplyETSNolintsToNodesRecursively(ir::AstNode * node) const267 void ETSNolintParser::ApplyETSNolintsToNodesRecursively(ir::AstNode *node) const
268 {
269 if (node == nullptr) {
270 return;
271 }
272 const std::size_t line = node->Start().line;
273 if (IsLineWithETSNolint(line)) {
274 const std::set<ETSWarnings> warningsCollection = GetWarningsCollectionByLine(line);
275
276 parser_->GetProgram()->AddNodeToETSNolintCollection(node, warningsCollection);
277 }
278 node->Iterate([&](auto *childNode) { ApplyETSNolintsToNodesRecursively(childNode); });
279 }
280 } // namespace ark::es2panda::parser
281