* Copyright (c) 2024-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ETSNolintParser.h"
#include "public/public.h"
namespace ark::es2panda::parser {
ETSNolintParser::ETSNolintParser(ParserImpl *mainParser) : parser_(mainParser)
{
line_ = parser_->Lexer()->Line();
}
static bool WarningCollectionEnabled(public_lib::Context *ctx)
{
return !ctx->config->options->GetEtsWarningCollection().empty();
}
void ETSNolintParser::SetStartPos()
{
line_ = parser_->Lexer()->Pos().Line();
startPos_ = parser_->Lexer()->Pos().Iterator().Index();
posOffset_ = startPos_;
BackwardSymbol(startPos_);
}
void ETSNolintParser::CollectETSNolints()
{
if (!WarningCollectionEnabled(parser_->ctx_)) {
return;
}
SetStartPos();
char32_t cp = PeekSymbol();
while (cp != lexer::LEX_CHAR_EOS && cp != lexer::UNICODE_CODE_POINT_MAX && cp != lexer::UNICODE_INVALID_CP) {
if (!IsEtsNolint()) {
NextSymbol();
cp = PeekSymbol();
continue;
}
std::size_t line = line_;
std::set<ETSWarnings> collection;
if (IsNextLine()) {
collection = ParseETSNolintArgs();
line += 1;
} else if (IsBegin()) {
collection = ParseETSNolintArgs();
for (const auto it : collection) {
applyingCollection_.insert(it);
}
} else if (IsEnd()) {
collection = ParseETSNolintArgs();
for (const auto it : collection) {
applyingCollection_.erase(it);
}
cp = PeekSymbol();
continue;
} else {
collection = ParseETSNolintArgs();
}
AddToETSNolintLinesCollection(line, collection);
cp = PeekSymbol();
}
RewindToStart();
}
void ETSNolintParser::ApplyETSNolintsToStatements(ArenaVector<ir::Statement *> &statements) const
{
if (!WarningCollectionEnabled(parser_->ctx_)) {
return;
}
for (auto *it : statements) {
ApplyETSNolintsToNodesRecursively(it);
}
}
void ETSNolintParser::NextSymbol()
{
if (PeekSymbol() == lexer::LEX_CHAR_LF) {
if (!applyingCollection_.empty()) {
AddToETSNolintLinesCollection(line_, applyingCollection_);
}
line_++;
}
posOffset_++;
parser_->Lexer()->Pos().Iterator().Forward(1);
}
void ETSNolintParser::BackwardSymbol()
{
posOffset_--;
parser_->Lexer()->Pos().Iterator().Backward(1);
if (PeekSymbol() == lexer::LEX_CHAR_LF) {
line_--;
}
}
void ETSNolintParser::NextSymbol(std::size_t i)
{
for (; i > 0; --i) {
NextSymbol();
}
}
void ETSNolintParser::BackwardSymbol(std::size_t i)
{
for (; i > 0; --i) {
BackwardSymbol();
}
}
void ETSNolintParser::RewindToStart() const
{
parser_->Lexer()->Pos().Iterator().Backward(posOffset_ - startPos_);
}
void ETSNolintParser::AddToETSNolintLinesCollection(std::size_t line, const std::set<ETSWarnings> &collection)
{
const auto search = linesCollection_.find(line);
if (search != linesCollection_.end()) {
search->second.insert(collection.begin(), collection.end());
return;
}
linesCollection_.insert({line, collection});
}
char32_t ETSNolintParser::PeekSymbol() const
{
return parser_->Lexer()->Pos().Iterator().Peek();
}
bool ETSNolintParser::TryPeekU32String(const std::u32string &u32str)
{
std::size_t localPosOffset = 0;
char32_t cp;
for (const char32_t i : u32str) {
cp = PeekSymbol();
if (i != cp) {
BackwardSymbol(localPosOffset);
return false;
}
NextSymbol();
localPosOffset++;
}
return true;
}
bool ETSNolintParser::IsEtsNolint()
{
static const std::u32string ETSNOLINT_CHAR32T = {
lexer::LEX_CHAR_UPPERCASE_E, lexer::LEX_CHAR_UPPERCASE_T, lexer::LEX_CHAR_UPPERCASE_S,
lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_O, lexer::LEX_CHAR_UPPERCASE_L,
lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_T};
char32_t cp;
for (unsigned long i = 0; i < ETSNOLINT_CHAR32T.length(); i++) {
cp = PeekSymbol();
if (ETSNOLINT_CHAR32T[i] != cp) {
return false;
}
NextSymbol();
}
return true;
}
bool ETSNolintParser::IsNextLine()
{
static const std::u32string NEXTLINE_CHAR32T = {
lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_E,
lexer::LEX_CHAR_UPPERCASE_X, lexer::LEX_CHAR_UPPERCASE_T, lexer::LEX_CHAR_UPPERCASE_L,
lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_E};
return TryPeekU32String(NEXTLINE_CHAR32T);
}
bool ETSNolintParser::IsBegin()
{
static const std::u32string BEGIN_CHAR32T = {lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_B,
lexer::LEX_CHAR_UPPERCASE_E, lexer::LEX_CHAR_UPPERCASE_G,
lexer::LEX_CHAR_UPPERCASE_I, lexer::LEX_CHAR_UPPERCASE_N};
return TryPeekU32String(BEGIN_CHAR32T);
}
bool ETSNolintParser::IsEnd()
{
static const std::u32string END_CHAR32T = {lexer::LEX_CHAR_MINUS, lexer::LEX_CHAR_UPPERCASE_E,
lexer::LEX_CHAR_UPPERCASE_N, lexer::LEX_CHAR_UPPERCASE_D};
return TryPeekU32String(END_CHAR32T);
}
ETSWarnings ETSNolintParser::MapETSNolintArg(const std::string &warningName) const
{
return util::gen::ets_warnings::FromString(warningName);
}
bool ETSNolintParser::ValidETSNolintArg(const std::string &warningName) const
{
return util::gen::ets_warnings::FromString(warningName) != ETSWarnings::INVALID;
}
std::set<ETSWarnings> ETSNolintParser::ParseETSNolintArgs()
{
std::set<ETSWarnings> warningsCollection;
if (PeekSymbol() != lexer::LEX_CHAR_LEFT_PAREN) {
for (std::underlying_type_t<ETSWarnings> wid = ETSWarnings::FIRST; wid <= ETSWarnings::LAST; wid++) {
warningsCollection.insert(ETSWarnings {wid});
}
return warningsCollection;
}
NextSymbol();
char32_t cp = 0;
std::string warningName;
while (cp != lexer::LEX_CHAR_SP && cp != lexer::LEX_CHAR_LF && cp != lexer::LEX_CHAR_EOS) {
cp = PeekSymbol();
const lexer::SourcePosition sPos {line_ + 1, 0, parser_->GetProgram()};
if (cp != lexer::LEX_CHAR_MINUS && cp != lexer::LEX_CHAR_COMMA && cp != lexer::LEX_CHAR_RIGHT_PAREN &&
(cp < lexer::LEX_CHAR_LOWERCASE_A || cp > lexer::LEX_CHAR_LOWERCASE_Z)) {
parser_->DiagnosticEngine().LogDiagnostic(diagnostic::UNEXPECTED_CHAR_ETSNOLINT,
util::DiagnosticMessageParams {}, sPos);
}
if ((cp == lexer::LEX_CHAR_COMMA || cp == lexer::LEX_CHAR_RIGHT_PAREN) && !ValidETSNolintArg(warningName)) {
parser_->DiagnosticEngine().LogDiagnostic(diagnostic::INVALID_ARGUMENT_ETSNOLINT,
util::DiagnosticMessageParams {}, sPos);
}
if ((cp == lexer::LEX_CHAR_COMMA || cp == lexer::LEX_CHAR_RIGHT_PAREN) && ValidETSNolintArg(warningName)) {
warningsCollection.insert(MapETSNolintArg(warningName));
warningName.clear();
} else {
ES2PANDA_ASSERT(cp <= std::numeric_limits<unsigned char>::max());
warningName += cp;
}
if (cp == lexer::LEX_CHAR_RIGHT_PAREN) {
break;
}
NextSymbol();
}
return warningsCollection;
}
bool ETSNolintParser::IsLineWithETSNolint(const std::size_t line) const
{
return linesCollection_.find(line) != linesCollection_.end();
}
std::set<ETSWarnings> ETSNolintParser::GetWarningsCollectionByLine(std::size_t line) const
{
const auto search = linesCollection_.find(line);
return search == linesCollection_.end() ? std::set<ETSWarnings> {} : search->second;
}
void ETSNolintParser::ApplyETSNolintsToNodesRecursively(ir::AstNode *node) const
{
if (node == nullptr) {
return;
}
const std::size_t line = node->Start().line;
if (IsLineWithETSNolint(line)) {
const std::set<ETSWarnings> warningsCollection = GetWarningsCollectionByLine(line);
parser_->GetProgram()->AddNodeToETSNolintCollection(node, warningsCollection);
}
node->Iterate([&](auto *childNode) { ApplyETSNolintsToNodesRecursively(childNode); });
}
}