1
2 /**
3 * Copyright (c) 2025 Huawei Device Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "lsp/include/register_code_fix/fix_nan_equality.h"
18 #include <iostream>
19 #include <string>
20 #include "lsp/include/code_fix_provider.h"
21 #include "lsp/include/internal_api.h"
22
23 namespace ark::es2panda::lsp {
24
25 const int G_FIX_NAN_EQUALITY_CODE = 1003; // change this to the error code you want to handle
26
MakeChangeForNaNEquality(ChangeTracker & changeTracker,es2panda_Context * context,size_t pos,std::vector<ark::es2panda::ir::AstNode * > & fixedNodes)27 void FixNaNEquality::MakeChangeForNaNEquality(ChangeTracker &changeTracker, es2panda_Context *context, size_t pos,
28 std::vector<ark::es2panda::ir::AstNode *> &fixedNodes)
29 {
30 auto *token = GetTouchingToken(context, pos, false);
31 if (token == nullptr || !token->IsBinaryExpression()) {
32 return;
33 }
34
35 const auto *binaryExpr = token->AsBinaryExpression();
36 if (binaryExpr->Left() == nullptr || binaryExpr->Right() == nullptr) {
37 return;
38 }
39
40 auto isLeftNaN = binaryExpr->Left()->IsIdentifier() && binaryExpr->Left()->AsIdentifier()->Name() == "NaN";
41 auto isRightNaN = binaryExpr->Right()->IsIdentifier() && binaryExpr->Right()->AsIdentifier()->Name() == "NaN";
42 if (!isLeftNaN && !isRightNaN) {
43 return;
44 }
45
46 auto *expr = isLeftNaN ? binaryExpr->Right() : binaryExpr->Left();
47 std::string exprText = expr->ToString();
48 std::string newText;
49
50 if (binaryExpr->Type() == ir::AstNodeType::TS_IMPORT_EQUALS_DECLARATION) {
51 newText = "Number.isNaN(" + exprText + ")";
52 } else {
53 return;
54 }
55
56 // auto *ctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
57
58 fixedNodes.push_back(const_cast<ir::AstNode *>(token));
59 // ark::es2panda::ir::AstNode *bClone = token->Clone(ctx->allocator, nullptr);
60
61 ChangeNodeOptions changeNodeOptions;
62 changeNodeOptions.insertNodeOptions->prefix = "";
63 changeNodeOptions.insertNodeOptions->suffix = "";
64 changeNodeOptions.insertNodeOptions->delta = 0;
65
66 ark::es2panda::lsp::ChangeNodeOptions options = {};
67 changeTracker.ReplaceNode(context, token, token, changeNodeOptions);
68 }
69
GetCodeActionsToFixNaNEquality(const CodeFixContext & context)70 std::vector<FileTextChanges> FixNaNEquality::GetCodeActionsToFixNaNEquality(const CodeFixContext &context)
71 {
72 TextChangesContext textChangesContext = {context.host, context.formatContext, context.preferences};
73 std::vector<ir::AstNode *> fixedNodes;
74 auto fileTextChanges = ChangeTracker::With(textChangesContext, [&](ChangeTracker &tracker) {
75 MakeChangeForNaNEquality(tracker, context.context, context.span.start, fixedNodes);
76 });
77
78 return fileTextChanges;
79 }
80
FixNaNEquality()81 FixNaNEquality::FixNaNEquality()
82 {
83 const char *fixNanEqualityId = "FixNaNEquality";
84 SetErrorCodes({G_FIX_NAN_EQUALITY_CODE}); // "NaN comparison" error code
85 SetFixIds({fixNanEqualityId}); // "fixNaNEquality" fix ID
86 }
87
GetCodeActions(const CodeFixContext & context)88 std::vector<CodeFixAction> FixNaNEquality::GetCodeActions(const CodeFixContext &context)
89 {
90 std::vector<CodeFixAction> returnedActions;
91 auto changes = GetCodeActionsToFixNaNEquality(context);
92 if (!changes.empty()) {
93 CodeFixAction codeAction;
94 codeAction.fixName = "fixNaNEquality";
95 codeAction.description = "Use Number.isNaN instead of comparing with NaN";
96 codeAction.changes = changes;
97 codeAction.fixId = "FixNaNEquality";
98 codeAction.fixAllDescription = "Replace all NaN equality comparisons";
99 returnedActions.push_back(codeAction);
100 }
101 return returnedActions;
102 }
103
GetAllCodeActions(const CodeFixAllContext & codeFixAll)104 CombinedCodeActions FixNaNEquality::GetAllCodeActions(const CodeFixAllContext &codeFixAll)
105 {
106 const std::vector<ir::AstNode *> fixedNodes;
107 CodeFixProvider provider;
108
109 const auto changes = provider.CodeFixAll(
110 codeFixAll, GetErrorCodes(), [&](ChangeTracker &tracker, const DiagnosticWithLocation &diag) {
111 Initializer initializer = Initializer();
112 auto ctx =
113 initializer.CreateContext(diag.file.source.data(), ES2PANDA_STATE_CHECKED, diag.file.source.data());
114 MakeChangeForNaNEquality(tracker, ctx, diag.start, const_cast<std::vector<ir::AstNode *> &>(fixedNodes));
115 initializer.DestroyContext(ctx);
116 });
117
118 CombinedCodeActions combinedCodeActions;
119 combinedCodeActions.changes = changes.changes;
120 combinedCodeActions.commands = changes.commands;
121 return combinedCodeActions;
122 }
123 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects, cert-err58-cpp)
124 AutoCodeFixRegister<FixNaNEquality> g_fixNaNEquality("FixNaNEquality");
125 } // namespace ark::es2panda::lsp
126