Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/diagnostics/UselessTernaryOperator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Бесполезный тернарный оператор (UselessTernaryOperator)

<!-- Блоки выше заполняются автоматически, не трогать -->
## Описание диагностики
Размещение в тернарном операторе булевых констант "Истина" или "Ложь" указывает на плохую продуманность кода.

## Примеры
Бессмысленные операторы

```Bsl
А = ?(Б = 1, Истина, Ложь);
```
```Bsl
А = ?(Б = 0, False, True);
```

Подозрительные операторы

```Bsl
А = ?(Б = 1, True, Истина);
```
```Bsl
А = ?(Б = 0, 0, False);
```
24 changes: 24 additions & 0 deletions docs/en/diagnostics/UselessTernaryOperator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Useless ternary operator (UselessTernaryOperator)

<!-- Блоки выше заполняются автоматически, не трогать -->
## Description
The placement of Boolean constants "True" or "False" in the ternary operator indicates poor code thoughtfulness.

## Examples
Useless operators

```Bsl
A = ?(B = 1, True, False);
```
```Bsl
A = ?(B = 0, False, True);
```

Suspicious operators

```Bsl
A = ?(B = 1, True, True);
```
```Bsl
A = ?(B = 0, 0, False);
```
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol;
import com.github._1c_syntax.bsl.languageserver.utils.Ranges;
import org.jspecify.annotations.Nullable;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
Expand All @@ -32,6 +31,7 @@
import org.eclipse.lsp4j.DiagnosticCodeDescription;
import org.eclipse.lsp4j.DiagnosticRelatedInformation;
import org.eclipse.lsp4j.Range;
import org.jspecify.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -74,6 +74,17 @@ protected void addDiagnostic(ParserRuleContext node) {
);
}

protected void addDiagnostic(ParserRuleContext node, DiagnosticAdditionalData data) {
if (node.exception != null) {
return;
}

addDiagnostic(
Ranges.create(node),
data
);
}

protected void addDiagnostic(ParserRuleContext node, String diagnosticMessage) {
if (node.exception != null) {
return;
Expand All @@ -98,6 +109,14 @@ protected void addDiagnostic(Range range) {
);
}

protected void addDiagnostic(Range range, DiagnosticAdditionalData data) {
addDiagnostic(
range,
data,
diagnostic.getInfo().getMessage()
);
}

protected void addDiagnostic(Range range, String diagnosticMessage) {
addDiagnostic(
range,
Expand All @@ -106,6 +125,15 @@ protected void addDiagnostic(Range range, String diagnosticMessage) {
);
}

protected void addDiagnostic(Range range, DiagnosticAdditionalData data, String diagnosticMessage) {
addDiagnostic(
range,
data,
diagnosticMessage,
null
);
}

protected void addDiagnostic(Token token) {
addDiagnostic(
Ranges.create(token)
Expand Down Expand Up @@ -202,6 +230,20 @@ public void addDiagnostic(
String diagnosticMessage,
@Nullable List<DiagnosticRelatedInformation> relatedInformation
) {
addDiagnostic(
range,
null,
diagnosticMessage,
relatedInformation
);
}

public void addDiagnostic(
Range range,
@Nullable DiagnosticAdditionalData data,
String diagnosticMessage,
@Nullable List<DiagnosticRelatedInformation> relatedInformation
) {

if (Ranges.isEmpty(range)) {
return;
Expand All @@ -210,6 +252,7 @@ public void addDiagnostic(
diagnosticList.add(createDiagnostic(
diagnostic,
range,
data,
diagnosticMessage,
relatedInformation
));
Expand All @@ -234,9 +277,20 @@ protected void addDiagnostic(SourceDefinedSymbol sourceDefinedSymbol) {
addDiagnostic(sourceDefinedSymbol.getSelectionRange());
}

/**
* Создает доп данные для диагностики на основании строки
*
* @param string Некая строка для помещения в доп данные диагностики
* @return Допданные диагностики
*/
public static DiagnosticAdditionalData createAdditionalData(String string) {
return new DiagnosticAdditionalData(string);
}

private static Diagnostic createDiagnostic(
BSLDiagnostic bslDiagnostic,
Range range,
@Nullable DiagnosticAdditionalData data,
String diagnosticMessage,
@Nullable List<DiagnosticRelatedInformation> relatedInformation
) {
Expand All @@ -258,6 +312,21 @@ private static Diagnostic createDiagnostic(
if (relatedInformation != null) {
diagnostic.setRelatedInformation(relatedInformation);
}

if (data != null) {
diagnostic.setData(data);
}
return diagnostic;
}

/**
* Служебный класс для хранения вспомогательной информации диагностики, которая может использоваться
* например в квикфиксах.
* Пока реализация примитивная под конкретную задачу
*
* @param string Некая строка
*/
public record DiagnosticAdditionalData(String string) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* This file is a part of BSL Language Server.
*
* Copyright (c) 2018-2025
* Alexey Sosnoviy <[email protected]>, Nikita Fedkin <[email protected]> and contributors
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* BSL Language Server is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* BSL Language Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BSL Language Server.
*/
package com.github._1c_syntax.bsl.languageserver.diagnostics;

import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticMetadata;
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticScope;
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticSeverity;
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticTag;
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticType;
import com.github._1c_syntax.bsl.languageserver.providers.CodeActionProvider;
import com.github._1c_syntax.bsl.parser.BSLParser;
import org.antlr.v4.runtime.tree.ParseTree;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.TextEdit;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@DiagnosticMetadata(
type = DiagnosticType.CODE_SMELL,
severity = DiagnosticSeverity.INFO,
scope = DiagnosticScope.BSL,
minutesToFix = 1,
tags = {
DiagnosticTag.BADPRACTICE,
DiagnosticTag.SUSPICIOUS
}
)
public class UselessTernaryOperatorDiagnostic extends AbstractVisitorDiagnostic implements QuickFixProvider {

private static final int SKIPPED_RULE_INDEX = 0;

@Override
public ParseTree visitTernaryOperator(BSLParser.TernaryOperatorContext ctx) {
var exp = ctx.expression();

if (exp != null && exp.size() >= 3) {

Check warning on line 60 in src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/UselessTernaryOperatorDiagnostic.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this magic number 3 to a well-named constant, and use the constant instead.

See more on https://sonarcloud.io/project/issues?id=1c-syntax_bsl-language-server&issues=AZrZzrQ4GSi-Q76s1F19&open=AZrZzrQ4GSi-Q76s1F19&pullRequest=3629
var condition = getBooleanToken(exp.get(0));
var trueBranch = getBooleanToken(exp.get(1));
var falseBranch = getBooleanToken(exp.get(2));

Check warning on line 63 in src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/UselessTernaryOperatorDiagnostic.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this magic number 2 to a well-named constant, and use the constant instead.

See more on https://sonarcloud.io/project/issues?id=1c-syntax_bsl-language-server&issues=AZrZzrQ4GSi-Q76s1F1-&open=AZrZzrQ4GSi-Q76s1F1-&pullRequest=3629

if (condition != SKIPPED_RULE_INDEX) {
diagnosticStorage.addDiagnostic(ctx);
} else if (trueBranch == BSLParser.TRUE && falseBranch == BSLParser.FALSE) {
diagnosticStorage.addDiagnostic(ctx, DiagnosticStorage.createAdditionalData(exp.get(0).getText()));
} else if (trueBranch == BSLParser.FALSE && falseBranch == BSLParser.TRUE) {
diagnosticStorage.addDiagnostic(ctx,
DiagnosticStorage.createAdditionalData(getAdaptedText(exp.get(0).getText())));
} else if (trueBranch != SKIPPED_RULE_INDEX || falseBranch != SKIPPED_RULE_INDEX) {

Check failure on line 72 in src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/UselessTernaryOperatorDiagnostic.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

"if ... else if" constructs should end with "else" clauses.

See more on https://sonarcloud.io/project/issues?id=1c-syntax_bsl-language-server&issues=AZrZzrQ4GSi-Q76s1F2A&open=AZrZzrQ4GSi-Q76s1F2A&pullRequest=3629
diagnosticStorage.addDiagnostic(ctx);
}
}

return super.visitTernaryOperator(ctx);
}

@Override
public List<CodeAction> getQuickFixes(
List<Diagnostic> diagnostics,
CodeActionParams params,
DocumentContext documentContext
) {

List<TextEdit> textEdits = new ArrayList<>();

diagnostics.forEach((Diagnostic diagnostic) -> {
var range = diagnostic.getRange();
var data = diagnostic.getData();
if (data instanceof DiagnosticStorage.DiagnosticAdditionalData additionalData) {
var textEdit = new TextEdit(range, additionalData.string());
textEdits.add(textEdit);
}
});

return CodeActionProvider.createCodeActions(
textEdits,
info.getResourceString("quickFixMessage"),
documentContext.getUri(),
diagnostics
);

}

private String getAdaptedText(String text) {
return info.getResourceString("quickFixAdaptedText", text);
}

private int getBooleanToken(BSLParser.ExpressionContext expCtx) {

Check warning on line 111 in src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/UselessTernaryOperatorDiagnostic.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make "getBooleanToken" a "static" method.

See more on https://sonarcloud.io/project/issues?id=1c-syntax_bsl-language-server&issues=AZrZzrQ4GSi-Q76s1F1_&open=AZrZzrQ4GSi-Q76s1F1_&pullRequest=3629

var tmpCtx = Optional.of(expCtx)
.filter(ctx -> ctx.children.size() == 1)
.map(ctx -> ctx.member(0))
.map(ctx -> ctx.getChild(0))
.filter(BSLParser.ConstValueContext.class::isInstance)
.map(BSLParser.ConstValueContext.class::cast);

return tmpCtx
.map(ctx -> ctx.getToken(BSLParser.TRUE, 0))
.map(ctx -> BSLParser.TRUE)
.or(() -> tmpCtx
.map(ctx -> ctx.getToken(BSLParser.FALSE, 0))
.map(ctx -> BSLParser.FALSE)
)
.orElse(SKIPPED_RULE_INDEX);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2108,6 +2108,16 @@
"title": "Use of system information",
"$id": "#/definitions/UseSystemInformation"
},
"UselessTernaryOperator": {
"description": "Useless ternary operator",
"default": true,
"type": [
"boolean",
"object"
],
"title": "Useless ternary operator",
"$id": "#/definitions/UselessTernaryOperator"
},
"UsingCancelParameter": {
"description": "Using parameter \"Cancel\"",
"default": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,9 @@
"UseSystemInformation": {
"$ref": "parameters-schema.json#/definitions/UseSystemInformation"
},
"UselessTernaryOperator": {
"$ref": "parameters-schema.json#/definitions/UselessTernaryOperator"
},
"UsingCancelParameter": {
"$ref": "parameters-schema.json#/definitions/UsingCancelParameter"
},
Expand Down Expand Up @@ -924,15 +927,30 @@
"properties": {
"type": {
"type": "string",
"enum": ["ERROR", "CODE_SMELL", "VULNERABILITY", "SECURITY_HOTSPOT"]
"enum": [
"ERROR",
"CODE_SMELL",
"VULNERABILITY",
"SECURITY_HOTSPOT"
]
},
"severity": {
"type": "string",
"enum": ["INFO", "MINOR", "MAJOR", "CRITICAL", "BLOCKER"]
"enum": [
"INFO",
"MINOR",
"MAJOR",
"CRITICAL",
"BLOCKER"
]
},
"scope": {
"type": "string",
"enum": ["ALL", "BSL", "OS"]
"enum": [
"ALL",
"BSL",
"OS"
]
},
"modules": {
"type": "array",
Expand Down Expand Up @@ -963,7 +981,12 @@
},
"lspSeverity": {
"type": "string",
"enum": ["Error", "Warning", "Information", "Hint"],
"enum": [
"Error",
"Warning",
"Information",
"Hint"
],
"description": "LSP severity level (Error, Warning, Information, Hint). If not specified, calculated automatically based on type and severity."
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
diagnosticMessage=Useless ternary operator
diagnosticName=Useless ternary operator
quickFixMessage=Fix some useless ternary operators
quickFixAdaptedText=NOT (%s)
Loading
Loading