Skip to content

Commit c2b2ba5

Browse files
authored
Feature/in memory db references to variables (#1871)
1 parent 99b2622 commit c2b2ba5

37 files changed

+822
-92
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Неиспользуемая локальная переменная (UnusedLocalVariable)
2+
3+
| Тип | Поддерживаются<br>языки | Важность | Включена<br>по умолчанию | Время на<br>исправление (мин) | Теги |
4+
|:-------------:|:-----------------------------:|:--------:|:------------------------------:|:-----------------------------------:|:--------------------------------------:|
5+
| `Дефект кода` | `BSL`<br>`OS` | `Важный` | `Да` | `1` | `brainoverload`<br>`badpractice` |
6+
7+
<!-- Блоки выше заполняются автоматически, не трогать -->
8+
## Описание диагностики
9+
Программные модули не должны иметь неиспользуемых переменных.
10+
11+
Если локальная переменная объявлена, но не используется, это мертвый код, который следует удалить.
12+
Это улучшит удобство обслуживания, поскольку разработчики не будут удивляться, для чего используется переменная.
13+
14+
## Сниппеты
15+
16+
<!-- Блоки ниже заполняются автоматически, не трогать -->
17+
### Экранирование кода
18+
19+
```bsl
20+
// BSLLS:UnusedLocalVariable-off
21+
// BSLLS:UnusedLocalVariable-on
22+
```
23+
24+
### Параметр конфигурационного файла
25+
26+
```json
27+
"UnusedLocalVariable": false
28+
```

docs/diagnostics/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
## Список реализованных диагностик
1010

11-
Общее количество: **157**
11+
Общее количество: **158**
1212

1313
* Потенциальная уязвимость: **4**
1414
* Уязвимость: **4**
1515
* Ошибка: **52**
16-
* Дефект кода: **97**
16+
* Дефект кода: **98**
1717

1818

1919
| Ключ | Название | Включена по умолчанию | Важность | Тип | Тэги |
@@ -152,6 +152,7 @@
152152
[UnreachableCode](UnreachableCode.md) | Недостижимый код | Да | Незначительный | Ошибка | `design`<br>`suspicious`
153153
[UnsafeSafeModeMethodCall](UnsafeSafeModeMethodCall.md) | Небезопасное использование функции БезопасныйРежим() | Да | Блокирующий | Ошибка | `deprecated`<br>`error`
154154
[UnusedLocalMethod](UnusedLocalMethod.md) | Неиспользуемый локальный метод | Да | Важный | Дефект кода | `standard`<br>`suspicious`<br>`unused`
155+
[UnusedLocalVariable](UnusedLocalVariable.md) | Неиспользуемая локальная переменная | Да | Важный | Дефект кода | `brainoverload`<br>`badpractice`
155156
[UnusedParameters](UnusedParameters.md) | Неиспользуемый параметр | Да | Важный | Дефект кода | `design`<br>`unused`
156157
[UsageWriteLogEvent](UsageWriteLogEvent.md) | Неверное использование метода "ЗаписьЖурналаРегистрации" | Да | Информационный | Дефект кода | `standard`<br>`badpractice`
157158
[UseLessForEach](UseLessForEach.md) | Бесполезный перебор коллекции | Да | Критичный | Ошибка | `clumsy`
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Unused local variable (UnusedLocalVariable)
2+
3+
| Type | Scope | Severity | Activated<br>by default | Minutes<br>to fix | Tags |
4+
|:------------:|:-------------------:|:--------:|:-----------------------------:|:-----------------------:|:--------------------------------------:|
5+
| `Code smell` | `BSL`<br>`OS` | `Major` | `Yes` | `1` | `brainoverload`<br>`badpractice` |
6+
7+
<!-- Блоки выше заполняются автоматически, не трогать -->
8+
## Description
9+
Unused local variables should be removed
10+
11+
If a local variable is declared but not used, it is dead code and should be removed.
12+
Doing so will improve maintainability because developers will not wonder what the variable is used for.
13+
14+
## Snippets
15+
16+
<!-- Блоки ниже заполняются автоматически, не трогать -->
17+
### Diagnostic ignorance in code
18+
19+
```bsl
20+
// BSLLS:UnusedLocalVariable-off
21+
// BSLLS:UnusedLocalVariable-on
22+
```
23+
24+
### Parameter for config
25+
26+
```json
27+
"UnusedLocalVariable": false
28+
```

docs/en/diagnostics/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ To escape individual sections of code or files from triggering diagnostics, you
88

99
## Implemented diagnostics
1010

11-
Total: **157**
11+
Total: **158**
1212

1313
* Security Hotspot: **4**
1414
* Vulnerability: **4**
1515
* Error: **52**
16-
* Code smell: **97**
16+
* Code smell: **98**
1717

1818

1919
| Key | Name| Enabled by default | Severity | Type | Tags |
@@ -152,6 +152,7 @@ Total: **157**
152152
[UnreachableCode](UnreachableCode.md) | Unreachable Code | Yes | Minor | Error | `design`<br>`suspicious`
153153
[UnsafeSafeModeMethodCall](UnsafeSafeModeMethodCall.md) | Unsafe SafeMode method call | Yes | Blocker | Error | `deprecated`<br>`error`
154154
[UnusedLocalMethod](UnusedLocalMethod.md) | Unused local method | Yes | Major | Code smell | `standard`<br>`suspicious`<br>`unused`
155+
[UnusedLocalVariable](UnusedLocalVariable.md) | Unused local variable | Yes | Major | Code smell | `brainoverload`<br>`badpractice`
155156
[UnusedParameters](UnusedParameters.md) | Unused parameter | Yes | Major | Code smell | `design`<br>`unused`
156157
[UsageWriteLogEvent](UsageWriteLogEvent.md) | Incorrect use of the method "WriteLogEvent" | Yes | Info | Code smell | `standard`<br>`badpractice`
157158
[UseLessForEach](UseLessForEach.md) | Useless collection iteration | Yes | Critical | Error | `clumsy`

src/main/java/com/github/_1c_syntax/bsl/languageserver/aop/measures/DocumentContextLazyDataMeasurer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ public void handleEvent(DocumentContextContentChangedEvent event) {
5959
for (SDBLTokenizer sdblTokenizer : documentContext.getQueries()) {
6060
measureCollector.measureIt(sdblTokenizer::getAst, "context: queryAst");
6161
}
62+
measureCollector.measureIt(documentContext.getSymbolTree()::getChildrenFlat, "context: symbolTree - childrenFlat");
63+
measureCollector.measureIt(documentContext.getSymbolTree()::getMethods, "context: symbolTree - methods");
64+
measureCollector.measureIt(documentContext.getSymbolTree()::getVariables, "context: symbolTree - variables");
65+
measureCollector.measureIt(documentContext.getSymbolTree()::getVariablesByName, "context: symbolTree - variablesByName");
66+
measureCollector.measureIt(documentContext.getSymbolTree()::getMethodsByName, "context: symbolTree - methodsByName");
6267
measureCollector.measureIt(documentContext::getDiagnosticIgnorance, "context: diagnosticIgnorance");
6368
measureCollector.measureIt(documentContext::getCognitiveComplexityData, "context: cognitiveComplexity");
6469
measureCollector.measureIt(documentContext::getCyclomaticComplexityData, "context: cyclomaticComplexity");

src/main/java/com/github/_1c_syntax/bsl/languageserver/context/computer/SymbolTreeComputer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public SymbolTree compute() {
4949
ModuleSymbol moduleSymbol = new ModuleSymbolComputer(documentContext).compute();
5050
List<MethodSymbol> methods = new MethodSymbolComputer(documentContext).compute();
5151
List<RegionSymbol> regions = new RegionSymbolComputer(documentContext).compute();
52-
List<VariableSymbol> variables = new VariableSymbolComputer(documentContext).compute();
52+
List<VariableSymbol> variables = new VariableSymbolComputer(documentContext, moduleSymbol, methods).compute();
5353

5454
List<SourceDefinedSymbol> allOfThem = new ArrayList<>(methods);
5555
allOfThem.addAll(regions);

src/main/java/com/github/_1c_syntax/bsl/languageserver/context/computer/VariableSymbolComputer.java

Lines changed: 129 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
package com.github._1c_syntax.bsl.languageserver.context.computer;
2323

2424
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
25+
import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol;
26+
import com.github._1c_syntax.bsl.languageserver.context.symbol.ModuleSymbol;
27+
import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol;
2528
import com.github._1c_syntax.bsl.languageserver.context.symbol.VariableSymbol;
2629
import com.github._1c_syntax.bsl.languageserver.context.symbol.variable.VariableDescription;
2730
import com.github._1c_syntax.bsl.languageserver.context.symbol.variable.VariableKind;
@@ -32,60 +35,170 @@
3235
import com.github._1c_syntax.bsl.parser.BSLParserRuleContext;
3336
import org.antlr.v4.runtime.Token;
3437
import org.antlr.v4.runtime.tree.ParseTree;
38+
import org.eclipse.lsp4j.Range;
3539

3640
import java.util.ArrayList;
41+
import java.util.Collections;
3742
import java.util.HashSet;
3843
import java.util.List;
44+
import java.util.Map;
3945
import java.util.Optional;
4046
import java.util.Set;
47+
import java.util.TreeMap;
48+
import java.util.function.Function;
49+
50+
import static java.util.stream.Collectors.groupingBy;
51+
import static java.util.stream.Collectors.toMap;
4152

4253
public class VariableSymbolComputer extends BSLParserBaseVisitor<ParseTree> implements Computer<List<VariableSymbol>> {
4354

4455
private final DocumentContext documentContext;
56+
private final ModuleSymbol module;
57+
private final Map<Range, SourceDefinedSymbol> methods;
4558
private final Set<VariableSymbol> variables = new HashSet<>();
59+
private final Map<String, String> currentMethodVariables = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
60+
private final Map<String, String> moduleVariables = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
61+
62+
private SourceDefinedSymbol currentMethod;
4663

47-
public VariableSymbolComputer(DocumentContext documentContext) {
64+
public VariableSymbolComputer(DocumentContext documentContext, ModuleSymbol module, List<MethodSymbol> methods) {
4865
this.documentContext = documentContext;
66+
this.module = module;
67+
this.methods = methods.stream().collect(toMap(MethodSymbol::getSubNameRange, Function.identity()));
68+
this.currentMethod = module;
4969
}
5070

5171
@Override
5272
public List<VariableSymbol> compute() {
5373
variables.clear();
74+
moduleVariables.clear();
5475
visitFile(documentContext.getAst());
5576
return new ArrayList<>(variables);
5677
}
5778

5879
@Override
5980
public ParseTree visitModuleVarDeclaration(BSLParser.ModuleVarDeclarationContext ctx) {
60-
var symbol = createVariableSymbol(ctx, ctx.var_name(), ctx.EXPORT_KEYWORD() != null, VariableKind.MODULE);
81+
var symbol = VariableSymbol.builder()
82+
.name(ctx.var_name().getText())
83+
.owner(documentContext)
84+
.range(Ranges.create(ctx))
85+
.variableNameRange(Ranges.create(ctx.var_name()))
86+
.export(ctx.EXPORT_KEYWORD() != null)
87+
.kind(VariableKind.MODULE)
88+
.description(createDescription(ctx))
89+
.scope(module)
90+
.build();
6191
variables.add(symbol);
62-
92+
moduleVariables.put(ctx.var_name().getText(), ctx.var_name().getText());
6393
return ctx;
6494
}
6595

96+
@Override
97+
public ParseTree visitSub(BSLParser.SubContext ctx) {
98+
this.currentMethod = getVariableScope(ctx);
99+
ParseTree tree = super.visitSub(ctx);
100+
currentMethodVariables.clear();
101+
this.currentMethod = module;
102+
return tree;
103+
}
104+
66105
@Override
67106
public ParseTree visitSubVarDeclaration(BSLParser.SubVarDeclarationContext ctx) {
68-
var symbol = createVariableSymbol(ctx, ctx.var_name(), false, VariableKind.LOCAL);
107+
var symbol = VariableSymbol.builder()
108+
.name(ctx.var_name().getText())
109+
.owner(documentContext)
110+
.range(Ranges.create(ctx))
111+
.variableNameRange(Ranges.create(ctx.var_name()))
112+
.export(false)
113+
.kind(VariableKind.LOCAL)
114+
.description(createDescription(ctx))
115+
.scope(getVariableScope(ctx))
116+
.build();
69117
variables.add(symbol);
118+
currentMethodVariables.put(ctx.var_name().getText(), ctx.var_name().getText());
119+
return ctx;
120+
}
70121

122+
@Override
123+
public ParseTree visitParam(BSLParser.ParamContext ctx) {
124+
if (ctx.IDENTIFIER() == null) {
125+
return ctx;
126+
}
127+
128+
var variable = VariableSymbol.builder()
129+
.name(ctx.IDENTIFIER().getText())
130+
.scope(currentMethod)
131+
.owner(documentContext)
132+
.range(Ranges.create(ctx))
133+
.variableNameRange(Ranges.create(ctx.IDENTIFIER()))
134+
.export(false)
135+
.kind(VariableKind.PARAMETER)
136+
.description(Optional.empty())
137+
.build();
138+
variables.add(variable);
139+
140+
currentMethodVariables.put(ctx.IDENTIFIER().getText(), ctx.IDENTIFIER().getText());
71141
return ctx;
72142
}
73143

74-
private VariableSymbol createVariableSymbol(
75-
BSLParserRuleContext ctx,
76-
BSLParser.Var_nameContext varName,
77-
boolean export,
78-
VariableKind kind
79-
) {
80-
return VariableSymbol.builder()
81-
.name(varName.getText())
144+
@Override
145+
public ParseTree visitLValue(BSLParser.LValueContext ctx) {
146+
if (
147+
ctx.getChildCount() > 1
148+
|| currentMethodVariables.containsKey(ctx.getText())
149+
|| moduleVariables.containsKey(ctx.getText())
150+
) {
151+
return ctx;
152+
}
153+
154+
var variable = VariableSymbol.builder()
155+
.name(ctx.getText())
82156
.owner(documentContext)
83157
.range(Ranges.create(ctx))
84-
.variableNameRange(Ranges.create(varName))
85-
.export(export)
86-
.kind(kind)
158+
.variableNameRange(Ranges.create(ctx))
159+
.export(false)
160+
.kind(VariableKind.DYNAMIC)
161+
.scope(currentMethod)
87162
.description(createDescription(ctx))
88163
.build();
164+
variables.add(variable);
165+
166+
currentMethodVariables.put(ctx.getText(), ctx.getText());
167+
return ctx;
168+
}
169+
170+
private SourceDefinedSymbol getVariableScope(BSLParser.SubVarDeclarationContext ctx) {
171+
var sub = (BSLParser.SubContext) Trees.getRootParent(ctx, BSLParser.RULE_sub);
172+
if (sub == null) {
173+
return module;
174+
}
175+
176+
return getVariableScope(sub);
177+
}
178+
179+
private SourceDefinedSymbol getVariableScope(BSLParser.SubContext ctx) {
180+
BSLParserRuleContext subNameNode;
181+
if (Trees.nodeContainsErrors(ctx)) {
182+
return module;
183+
} else if (ctx.function() != null) {
184+
subNameNode = ctx.function().funcDeclaration().subName();
185+
} else {
186+
subNameNode = ctx.procedure().procDeclaration().subName();
187+
}
188+
189+
return methods.getOrDefault(Ranges.create(subNameNode), module);
190+
}
191+
192+
private Optional<VariableDescription> createDescription(BSLParser.LValueContext ctx) {
193+
var trailingComments = Trees.getTrailingComment(documentContext.getTokens(), ctx.getStop());
194+
195+
if (trailingComments.isEmpty()) {
196+
return Optional.empty();
197+
}
198+
199+
return Optional.of(
200+
new VariableDescription(Collections.emptyList(), trailingComments)
201+
);
89202
}
90203

91204
private Optional<VariableDescription> createDescription(BSLParserRuleContext ctx) {
@@ -108,5 +221,4 @@ private Optional<VariableDescription> createDescription(BSLParserRuleContext ctx
108221

109222
}
110223

111-
112-
}
224+
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/MethodSymbol.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
4545
@ToString(exclude = {"children", "parent"})
4646
public class MethodSymbol implements SourceDefinedSymbol, Exportable, Describable {
47+
@EqualsAndHashCode.Include
4748
String name;
4849

4950
@Builder.Default

0 commit comments

Comments
 (0)