diff --git a/ets2panda/lsp/BUILD.gn b/ets2panda/lsp/BUILD.gn index 906d494c38ed022dee06f1ed9078196e073f0874..2e6655e584d43f917131854284db867fe709ca9a 100644 --- a/ets2panda/lsp/BUILD.gn +++ b/ets2panda/lsp/BUILD.gn @@ -106,6 +106,7 @@ ohos_source_set("libes2panda_lsp_static") { "src/register_code_fix/fix_nan_equality.cpp", "src/register_code_fix/fix_remove_override_modifier.cpp", "src/register_code_fix/fix_return_type_in_async_function.cpp", + "src/register_code_fix/fix_unreachable_code.cpp", "src/register_code_fix/forgotten_this_property_access.cpp", "src/register_code_fix/import_fixes.cpp", "src/register_code_fix/remove_accidental_call_parentheses.cpp", diff --git a/ets2panda/lsp/CMakeLists.txt b/ets2panda/lsp/CMakeLists.txt index 587f416137f5f502c579963be50f367cf1ac0591..97b269d5fe8c664eeb6b24d2bdc7e671262a0f81 100644 --- a/ets2panda/lsp/CMakeLists.txt +++ b/ets2panda/lsp/CMakeLists.txt @@ -120,7 +120,7 @@ set(ES2PANDA_LSP_SRC ./src/register_code_fix/add_name_to_nameless_parameter.cpp ./src/register_code_fix/add_missing_declare_property.cpp ./src/register_code_fix/convert_const_to_let.cpp - ./src/register_code_fix/constructor_for_derived_need_super_call + ./src/register_code_fix/constructor_for_derived_need_super_call.cpp ./src/register_code_fix/fix_missing_call_parantheses.cpp ./src/register_code_fix/fix_import_non_exported_member.cpp ./src/register_code_fix/fix_nan_equality.cpp @@ -133,6 +133,7 @@ set(ES2PANDA_LSP_SRC ./src/register_code_fix/fix_spelling.cpp ./src/register_code_fix/fix_class_incorrectly_implements_interface.cpp ./src/get_signature.cpp + ./src/register_code_fix/fix_unreachable_code.cpp ./src/get_name_or_dotted_name_span.cpp ./src/get_node.cpp ./src/node_matchers.cpp diff --git a/ets2panda/lsp/include/register_code_fix/fix_unreachable_code.h b/ets2panda/lsp/include/register_code_fix/fix_unreachable_code.h new file mode 100644 index 0000000000000000000000000000000000000000..4bdcd028495c0c0c85064f55c12a641127f368a9 --- /dev/null +++ b/ets2panda/lsp/include/register_code_fix/fix_unreachable_code.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 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. + */ + +#ifndef FIX_UNREACHABLE_CODE_H +#define FIX_UNREACHABLE_CODE_H + +#include + +#include "lsp/include/code_fixes/code_fix_types.h" +#include "lsp/include/services/text_change/change_tracker.h" +#include "lsp/include/types.h" + +namespace ark::es2panda::lsp { + +class FixUnreachableCode : public CodeFixRegistration { +public: + FixUnreachableCode(); + std::vector GetCodeActions(const CodeFixContext &context) override; + CombinedCodeActions GetAllCodeActions(const CodeFixAllContext &codeFixAll) override; + +private: + void MakeChangeForUnreachableCode(ChangeTracker &changeTracker, es2panda_Context *context, size_t pos); + std::vector GetCodeActionsToRemoveUnreachableCode(const CodeFixContext &context); + TextRange HandleUnreachableAfterTerminator(ir::AstNode *stmt); + TextRange HandleUnreachableStatement(ir::AstNode *statement); +}; + +} // namespace ark::es2panda::lsp +#endif diff --git a/ets2panda/lsp/src/register_code_fix/fix_unreachable_code.cpp b/ets2panda/lsp/src/register_code_fix/fix_unreachable_code.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a999732235500a56a75035c697c92915726091d --- /dev/null +++ b/ets2panda/lsp/src/register_code_fix/fix_unreachable_code.cpp @@ -0,0 +1,200 @@ +/** + * Copyright (c) 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 "lsp/include/register_code_fix/fix_unreachable_code.h" +#include +#include "generated/code_fix_register.h" +#include "lsp/include/code_fix_provider.h" +#include "lsp/include/internal_api.h" + +namespace ark::es2panda::lsp { + +using codefixes::FIX_UNREACHABLE_CODE; + +FixUnreachableCode::FixUnreachableCode() +{ + auto errorCodes = FIX_UNREACHABLE_CODE.GetSupportedCodeNumbers(); + SetErrorCodes({errorCodes.begin(), errorCodes.end()}); + SetFixIds({FIX_UNREACHABLE_CODE.GetFixId().data()}); +} + +static inline bool IsTerminatorStmt(const ir::AstNode *s) +{ + return (s != nullptr) && (s->IsReturnStatement() || s->IsThrowStatement()); +} + +TextRange FixUnreachableCode::HandleUnreachableAfterTerminator(ir::AstNode *stmt) +{ + ir::AstNode *parent = stmt->Parent(); + if (parent == nullptr) { + return {0, 0}; + } + + if (!parent->IsBlockStatement()) { + return {0, 0}; + } + + auto *block = parent->AsBlockStatement(); + const auto &stmts = block->Statements(); + + int idx = -1; + for (int i = 0; i < static_cast(stmts.size()); ++i) { + if (stmts[i] == stmt) { + idx = i; + break; + } + } + if (idx < 0) { + return {0, 0}; + } + + for (int j = idx - 1; j >= 0; --j) { + const ir::AstNode *prev = stmts[j]; + if (prev != nullptr && IsTerminatorStmt(prev)) { + return {stmt->Start().index, stmt->End().index}; + } + } + + return {0, 0}; +} + +TextRange FixUnreachableCode::HandleUnreachableStatement(ir::AstNode *statement) +{ + if (statement == nullptr) { + return {0, 0}; + } + + while (statement != nullptr) { + if (statement->IsWhileStatement() || statement->IsIfStatement() || statement->IsForUpdateStatement()) { + break; + } + statement = statement->Parent(); + } + + if (statement == nullptr) { + return {0, 0}; + } + + ir::Expression *expr = nullptr; + if (statement->IsWhileStatement()) { + expr = statement->AsWhileStatement()->Test(); + } else if (statement->IsIfStatement()) { + expr = statement->AsIfStatement()->Test(); + } else if (statement->IsForUpdateStatement()) { + expr = statement->AsForUpdateStatement()->Test(); + } + + if (expr == nullptr) { + return {0, 0}; + } + + if (expr->IsBooleanLiteral()) { + auto boolLiteral = expr->AsBooleanLiteral(); + if (!boolLiteral->Value()) { + return {statement->Start().index, statement->End().index}; + } + } else if (expr->IsNumberLiteral()) { + if (expr->AsNumberLiteral()->Number().IsZero()) { + return {statement->Start().index, statement->End().index}; + } + } else if (expr->IsStringLiteral()) { + if (expr->AsStringLiteral()->ToString().empty()) { + return {statement->Start().index, statement->End().index}; + } + } else if (expr->IsCharLiteral()) { + if (expr->AsCharLiteral()->ToString().empty()) { + return {statement->Start().index, statement->End().index}; + } + } else if (expr->IsNullLiteral()) { + return {statement->Start().index, statement->End().index}; + } + + return {0, 0}; +} + +void FixUnreachableCode::MakeChangeForUnreachableCode(ChangeTracker &changeTracker, es2panda_Context *context, + size_t pos) +{ + TextRange range = {0, 0}; + auto *token = GetTouchingToken(context, pos, false); + if (token == nullptr) { + return; + } + + while (token != nullptr && !token->IsStatement()) { + token = token->Parent(); + } + + if (token == nullptr) { + return; + } + + range = HandleUnreachableStatement(token); + if (range.pos != range.end) { + auto ctx = reinterpret_cast(context); + changeTracker.DeleteRange(ctx->sourceFile, {range.pos, range.end}); + return; + } + + range = HandleUnreachableAfterTerminator(token); + if (range.pos != range.end) { + auto ctx = reinterpret_cast(context); + changeTracker.DeleteRange(ctx->sourceFile, {range.pos, range.end}); + return; + } +} + +std::vector FixUnreachableCode::GetCodeActionsToRemoveUnreachableCode(const CodeFixContext &context) +{ + TextChangesContext textChangesContext = {context.host, context.formatContext, context.preferences}; + return ChangeTracker::With(textChangesContext, [&](ChangeTracker &tracker) { + MakeChangeForUnreachableCode(tracker, context.context, context.span.start); + }); +} + +std::vector FixUnreachableCode::GetCodeActions(const CodeFixContext &context) +{ + std::vector actions; + auto changes = GetCodeActionsToRemoveUnreachableCode(context); + if (!changes.empty()) { + CodeFixAction fix; + fix.fixName = FIX_UNREACHABLE_CODE.GetFixId().data(); + fix.description = "Remove unreachable code"; + fix.fixAllDescription = "Remove all unreachable code"; + fix.changes = changes; + fix.fixId = FIX_UNREACHABLE_CODE.GetFixId().data(); + actions.push_back(std::move(fix)); + } + + return actions; +} + +CombinedCodeActions FixUnreachableCode::GetAllCodeActions(const CodeFixAllContext &codeFixAllCtx) +{ + CodeFixProvider provider; + auto changes = provider.CodeFixAll( + codeFixAllCtx, GetErrorCodes(), [&](ChangeTracker &tracker, const DiagnosticWithLocation &diag) { + MakeChangeForUnreachableCode(tracker, codeFixAllCtx.context, diag.GetStart()); + }); + + CombinedCodeActions combined; + combined.changes = std::move(changes.changes); + combined.commands = std::move(changes.commands); + return combined; +} +// NOLINTNEXTLINE +AutoCodeFixRegister g_fixUnreachableCode(FIX_UNREACHABLE_CODE.GetFixId().data()); + +} // namespace ark::es2panda::lsp diff --git a/ets2panda/test/unit/lsp/CMakeLists.txt b/ets2panda/test/unit/lsp/CMakeLists.txt index 943b07f160397d1b60cc78fe12f0637d53ee664c..660f7a829487ec64e378d6ce7987ce317bcf3046 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -393,3 +393,8 @@ ets2panda_add_gtest(lsp_api_get_node_export_test CPP_SOURCES ets2panda_add_gtest(lsp_api_get_node_ts_class_Implements_test CPP_SOURCES get_node_ts_class_Implements_test.cpp ) + + +ets2panda_add_gtest(lsp_api_fix_unreachable_code_test CPP_SOURCES + fix_unreachable_code_test.cpp +) \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/fix_unreachable_code_test.cpp b/ets2panda/test/unit/lsp/fix_unreachable_code_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d295e5f680c2c64f707edcd84f0ea721b96711f7 --- /dev/null +++ b/ets2panda/test/unit/lsp/fix_unreachable_code_test.cpp @@ -0,0 +1,631 @@ +/** + * Copyright (c) 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 "lsp_api_test.h" +#include +#include +#include "lsp/include/api.h" +#include "lsp/include/cancellation_token.h" +#include "lsp/include/register_code_fix/fix_unreachable_code.h" + +namespace { + +using ark::es2panda::lsp::Initializer; +using ark::es2panda::lsp::codefixes::FIX_UNREACHABLE_CODE; + +constexpr std::string_view EXPECTED_FIX_NAME = FIX_UNREACHABLE_CODE.GetFixId(); +constexpr auto ERROR_CODES = FIX_UNREACHABLE_CODE.GetSupportedCodeNumbers(); +constexpr std::string_view EXPECTED_FIX_DESCRIPTION = "Remove unreachable code"; +constexpr int DEFAULT_THROTTLE = 20; + +class FixUnreachableCodeTests : public LSPAPITests { +public: + static ark::es2panda::lsp::CancellationToken CreateNonCancellationToken() + { + return ark::es2panda::lsp::CancellationToken(DEFAULT_THROTTLE, &GetNullHost()); + } + + static size_t LineColToPos(es2panda_Context *context, const size_t line, const size_t col) + { + auto ctx = reinterpret_cast(context); + auto index = ark::es2panda::lexer::LineIndex(ctx->parserProgram->SourceCode()); + return index.GetOffset(ark::es2panda::lexer::SourceLocation(line, col, ctx->parserProgram)); + } + + static void ValidateCodeFixActionInfo(const CodeFixActionInfo &info, const size_t expectedTextChangeStart, + const size_t expectedTextChangeLength, const std::string &expectedFileName) + { + ASSERT_EQ(info.fixName_, EXPECTED_FIX_NAME); + ASSERT_EQ(info.fixId_, EXPECTED_FIX_NAME); + ASSERT_EQ(info.description_, EXPECTED_FIX_DESCRIPTION); + ASSERT_EQ(info.changes_[0].fileName, expectedFileName); + ASSERT_EQ(info.changes_[0].textChanges[0].span.start, expectedTextChangeStart); + ASSERT_EQ(info.changes_[0].textChanges[0].span.length, expectedTextChangeLength); + ASSERT_EQ(info.changes_[0].textChanges[0].newText, ""); + } + +private: + class NullCancellationToken : public ark::es2panda::lsp::HostCancellationToken { + public: + bool IsCancellationRequested() override + { + return false; + } + }; + + static NullCancellationToken &GetNullHost() + { + static NullCancellationToken instance; + return instance; + } +}; + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterReturn1) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn1.ets"}; + std::vector fileContents = { + R"( +function func(): boolean { +return false; +console.log("log"); +})"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + + const size_t start = LineColToPos(context, 4, 1); + const size_t length = 1; + const size_t expectedTextChangeStart = 42; + const size_t expectedTextChangeLength = 19; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterReturn2) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn2.ets"}; + std::vector fileContents = { + R"( +function func(): boolean { +return false; +if (true) { +console.log("log"); +} +})"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + + const size_t start = LineColToPos(context, 4, 1); + const size_t length = 1; + const size_t expectedTextChangeStart = 42; + const size_t expectedTextChangeLength = 33; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterReturn3) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn3.ets"}; + std::vector fileContents = { + R"( +function func(): void{ +if (true) { +return; +console.log("log"); +} +})"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + + const size_t start = LineColToPos(context, 5, 1); + const size_t length = 1; + const size_t expectedTextChangeStart = 44; + const size_t expectedTextChangeLength = 19; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterReturn4) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn4.ets"}; + std::vector fileContents = { + R"( +function test() : void { + do { + return; + console.log("log"); + } while (true) +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 5, 9); + const size_t length = 1; + const size_t expectedTextChangeStart = 59; + const size_t expectedTextChangeLength = 19; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterReturn5) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn5.ets"}; + std::vector fileContents = { + R"( +function test() : void { +return; +for (; false ;) { +console.log("log"); +return; +console.log("log2"); +} +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 4, 1); + const size_t length = 1; + const size_t expectedTextChangeStart = 34; + const size_t expectedTextChangeLength = 68; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterReturn6) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn6.ets"}; + std::vector fileContents = { + R"( +function test() : void { +return; +console.log("log"); +console.log("log"); +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 4, 1); + const size_t length = 1; + const size_t expectedTextChangeStart = 34; + const size_t expectedTextChangeLength = 19; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterReturn7) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn7.ets"}; + std::vector fileContents = { + R"( +function test() : void { +return; +console.log("log"); +console.log("log"); +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 5, 1); + const size_t length = 1; + const size_t expectedTextChangeStart = 54; + const size_t expectedTextChangeLength = 19; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterWhileFalse1) +{ + std::vector fileNames = {"FixUnreachableCodeWhileFalse1.ets"}; + std::vector fileContents = { + R"( +function test(): void{ +while (false) { +console.log("log"); +} +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + + const size_t start = LineColToPos(context, 3, 13); + const size_t length = 1; + const size_t expectedTextChangeStart = 24; + const size_t expectedTextChangeLength = 37; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterWhileFalse2) +{ + std::vector fileNames = {"FixUnreachableCodeWhileFalse2.ets"}; + std::vector fileContents = { + R"( +function test(): void{ +while (0) { +console.log("log"); +} +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + + const size_t start = LineColToPos(context, 3, 11); + const size_t length = 1; + const size_t expectedTextChangeStart = 24; + const size_t expectedTextChangeLength = 33; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterWhileFalse3) +{ + std::vector fileNames = {"FixUnreachableCodeWhileFalse4.ets"}; + std::vector fileContents = { + R"( +function test():void { +const x=5; +while (x!=5) { +console.log("log"); +}} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + + const size_t start = LineColToPos(context, 4, 12); + const size_t length = 1; + const size_t expectedTextChangeStart = 35; + const size_t expectedTextChangeLength = 36; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterWhileFalse4) +{ + std::vector fileNames = {"FixUnreachableCodeWhileFalse5.ets"}; + std::vector fileContents = { + R"( +function test():void { +while (1!=1) { +console.log("log"); +}} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + + const size_t start = LineColToPos(context, 3, 12); + const size_t length = 1; + const size_t expectedTextChangeStart = 24; + const size_t expectedTextChangeLength = 36; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterForFalse) +{ + std::vector fileNames = {"FixUnreachableCodeIfFalse.ets"}; + std::vector fileContents = { + R"( +function test() : void { +for (; false ;) { + console.log("log"); +} +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 3, 17); + const size_t length = 1; + const size_t expectedTextChangeStart = 26; + const size_t expectedTextChangeLength = 47; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterIfFalse) +{ + std::vector fileNames = {"FixUnreachableCodeIfFalse.ets"}; + std::vector fileContents = { + R"( +function nested(): void { +if (false) { +console.log("log"); +} +return; +})"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 4, 11); + const size_t length = 1; + const size_t expectedTextChangeStart = 27; + const size_t expectedTextChangeLength = 34; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestNoFixReachableStatement) +{ + std::vector fileNames = {"NoFixReachableStatement.ets"}; + std::vector fileContents = { + R"( +function func() : void { +console.log("log"); +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 2, 1); + const size_t length = 1; + const int expectedFixResultSize = 0; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestCaseLetUsage) +{ + std::vector fileNames = {"FixUnreachableCodeLetUsage.ets"}; + std::vector fileContents = { + R"( +let x = false; +function test() : void { +for (; x ;) { +console.log("no unreachable error"); +} +})"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 4, 13); + const size_t length = 1; + const int expectedFixResultSize = 0; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + initializer.DestroyContext(context); +} + +TEST_F(FixUnreachableCodeTests, TestFixRemoveUnreachableAfterThrowStmnt) +{ + std::vector fileNames = {"FixUnreachableCodeAfterReturn6.ets"}; + std::vector fileContents = { + R"( +function test() : void { +throw Error(); +console.log("log"); +} +)"}; + + auto filePaths = CreateTempFile(fileNames, fileContents); + ASSERT_EQ(fileNames.size(), filePaths.size()); + + Initializer initializer; + auto *context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + const size_t start = LineColToPos(context, 4, 1); + const size_t length = 1; + const size_t expectedTextChangeStart = 41; + const size_t expectedTextChangeLength = 19; + const int expectedFixResultSize = 1; + + std::vector errorCodes(ERROR_CODES.begin(), ERROR_CODES.end()); + CodeFixOptions options = {CreateNonCancellationToken(), ark::es2panda::lsp::FormatCodeSettings(), {}}; + + auto fixResult = + ark::es2panda::lsp::GetCodeFixesAtPositionImpl(context, start, start + length, errorCodes, options); + ASSERT_EQ(fixResult.size(), expectedFixResultSize); + + ValidateCodeFixActionInfo(fixResult[0], expectedTextChangeStart, expectedTextChangeLength, filePaths[0]); + + initializer.DestroyContext(context); +} + +} // namespace \ No newline at end of file diff --git a/ets2panda/util/diagnostic/warning.yaml b/ets2panda/util/diagnostic/warning.yaml index 5d2579c02557ad6190fc322af03f9144d475dbd0..e669bb2b74ab2a5eb24ccad3ed9579412d76cc4b 100644 --- a/ets2panda/util/diagnostic/warning.yaml +++ b/ets2panda/util/diagnostic/warning.yaml @@ -124,6 +124,7 @@ warning: - name: UNREACHABLE_STMT id: 26 message: "Unreachable statement." + code_fix_ids: [FixUnreachableCode] graveyard: - 18 diff --git a/ets2panda/util/importPathManager.cpp b/ets2panda/util/importPathManager.cpp index 712cb83141253fc9eb99cb2641d2099298414c20..1060550d2cc88b2e6d2639cf419174f3a6124db0 100644 --- a/ets2panda/util/importPathManager.cpp +++ b/ets2panda/util/importPathManager.cpp @@ -113,7 +113,7 @@ void ImportPathManager::ProcessExternalLibraryImport(ImportMetadata &importData) } // If needed, the result of this function can be cached -std::string_view ImportPathManager::tryImportFromDeclarationCache(std::string_view resolvedImportPath) const +std::string_view ImportPathManager::TryImportFromDeclarationCache(std::string_view resolvedImportPath) const { // if package or unresolved file, just skip if (ark::os::file::File::IsDirectory(std::string(resolvedImportPath)) || @@ -220,7 +220,7 @@ ImportPathManager::ResolvedPathRes ImportPathManager::ResolvePath(std::string_vi } if (!result.resolvedIsExternalModule) { - result.resolvedPath = tryImportFromDeclarationCache(result.resolvedPath); + result.resolvedPath = TryImportFromDeclarationCache(result.resolvedPath); } return result; } diff --git a/ets2panda/util/importPathManager.h b/ets2panda/util/importPathManager.h index 9d4a7017775c5ad103db43071486ad8c912cf436..a0f525c43deb08d980bbbe62b87386b9817442bb 100644 --- a/ets2panda/util/importPathManager.h +++ b/ets2panda/util/importPathManager.h @@ -185,7 +185,7 @@ private: std::string TryMatchDependencies(std::string_view fixedPath) const; StringView GetRealPath(StringView path) const; void ProcessExternalLibraryImport(ImportMetadata &importData); - std::string_view tryImportFromDeclarationCache(std::string_view resolvedImportPath) const; + std::string_view TryImportFromDeclarationCache(std::string_view resolvedImportPath) const; public: void AddToParseList(const ImportMetadata &importMetadata);