com.github._1c_syntax.bsl.languageserver.diagnostics.RedundantAccessToObjectDiagnostic Maven / Gradle / Ivy
/*
* This file is a part of BSL Language Server.
*
* Copyright (c) 2018-2022
* Alexey Sosnoviy , Nikita Fedkin 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.DiagnosticParameter;
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.mdo.support.ReturnValueReuse;
import com.github._1c_syntax.bsl.parser.BSLParser;
import com.github._1c_syntax.bsl.types.MDOType;
import com.github._1c_syntax.bsl.types.ModuleType;
import com.github._1c_syntax.mdclasses.mdo.AbstractMDObjectBase;
import com.github._1c_syntax.mdclasses.mdo.MDCommonModule;
import com.github._1c_syntax.utils.CaseInsensitivePattern;
import org.antlr.v4.runtime.tree.ParseTree;
import org.eclipse.lsp4j.Diagnostic;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
@DiagnosticMetadata(
type = DiagnosticType.CODE_SMELL,
severity = DiagnosticSeverity.INFO,
minutesToFix = 1,
modules = {
ModuleType.CommonModule,
ModuleType.ObjectModule,
ModuleType.ManagerModule,
ModuleType.FormModule,
ModuleType.RecordSetModule
},
tags = {
DiagnosticTag.STANDARD,
DiagnosticTag.CLUMSY
},
scope = DiagnosticScope.BSL
)
public class RedundantAccessToObjectDiagnostic extends AbstractVisitorDiagnostic {
private static final Pattern PATTERN = CaseInsensitivePattern.compile("^ЭтотОбъект|ThisObject");
private static final Pattern PATTERN_WITH_DOT = CaseInsensitivePattern.compile("^(ЭтотОбъект|ThisObject)\\..*");
private static final boolean CHECK_OBJECT_MODULE = true;
private static final boolean CHECK_FORM_MODULE = true;
private static final boolean CHECK_RECORD_SET_MODULE = true;
private boolean needCheckName = false;
private boolean skipLValue = false;
private Pattern namePatternWithDot;
@DiagnosticParameter(
type = Boolean.class,
defaultValue = "" + CHECK_OBJECT_MODULE
)
private boolean checkObjectModule = CHECK_OBJECT_MODULE;
@DiagnosticParameter(
type = Boolean.class,
defaultValue = "" + CHECK_FORM_MODULE
)
private boolean checkFormModule = CHECK_FORM_MODULE;
@DiagnosticParameter(
type = Boolean.class,
defaultValue = "" + CHECK_RECORD_SET_MODULE
)
private boolean checkRecordSetModule = CHECK_RECORD_SET_MODULE;
@Override
public List getDiagnostics(DocumentContext documentContext) {
var typeModule = documentContext.getModuleType();
if (typeModule == ModuleType.CommonModule || typeModule == ModuleType.ManagerModule) {
documentContext.getMdObject().ifPresent((AbstractMDObjectBase mdObjectBase) -> {
needCheckName = !(mdObjectBase instanceof MDCommonModule)
|| ((MDCommonModule) mdObjectBase).getReturnValuesReuse() == ReturnValueReuse.DONT_USE;
skipLValue = true;
namePatternWithDot = CaseInsensitivePattern.compile(
String.format(getManagerModuleName(mdObjectBase.getMdoType()), mdObjectBase.getName())
);
});
}
if (skipModule(typeModule)) {
return new ArrayList<>();
}
return super.getDiagnostics(documentContext);
}
@Override
public ParseTree visitCallStatement(BSLParser.CallStatementContext ctx) {
if (ctx.globalMethodCall() != null && ctx.getStart() == ctx.globalMethodCall().getStart()) {
return super.visitCallStatement(ctx);
}
if (PATTERN_WITH_DOT.matcher(ctx.getText()).matches()) {
diagnosticStorage.addDiagnostic(ctx.getStart());
}
if (needCheckName && namePatternWithDot.matcher(ctx.getText()).matches()) {
diagnosticStorage.addDiagnostic(ctx.getStart());
}
return super.visitCallStatement(ctx);
}
@Override
public ParseTree visitComplexIdentifier(BSLParser.ComplexIdentifierContext ctx) {
var identifier = ctx.IDENTIFIER();
var modifiers = ctx.modifier();
if (identifier == null || modifiers.isEmpty()) {
return ctx;
}
if (
PATTERN.matcher(identifier.getText()).matches()
&& modifiers.get(0) != null
&& modifiers.get(0).accessIndex() == null
) {
diagnosticStorage.addDiagnostic(ctx.getStart());
}
return ctx;
}
@Override
public ParseTree visitLValue(BSLParser.LValueContext ctx) {
if (skipLValue) {
return ctx;
}
var identifier = ctx.IDENTIFIER();
var acceptor = ctx.acceptor();
if (identifier == null || acceptor == null) {
return ctx;
}
if (
PATTERN.matcher(identifier.getText()).matches()
&& notHasAccessIndex(acceptor)
&& hasAccessProperty(acceptor)
) {
diagnosticStorage.addDiagnostic(ctx.getStart());
}
return ctx;
}
private static String getManagerModuleName(MDOType objectType) {
if (objectType == MDOType.CATALOG) {
return "^(Справочники|Catalogs)\\.%s\\..*";
} else if (objectType == MDOType.DOCUMENT) {
return "^(Документы|Documents)\\.%s\\..*";
} else if (objectType == MDOType.ACCOUNTING_REGISTER) {
return "^(РегистрыБухгалтерии|AccountingRegisters)\\.%s\\..*";
} else if (objectType == MDOType.ACCUMULATION_REGISTER) {
return "^(РегистрыНакопления|AccumulationRegisters)\\.%s\\..*";
} else if (objectType == MDOType.CALCULATION_REGISTER) {
return "^(РегистрыРасчета|CalculationRegisters)\\.%s\\..*";
} else if (objectType == MDOType.INFORMATION_REGISTER) {
return "^(РегистрыСведений|InformationRegisters)\\.%s\\..*";
} else {
return "^%s\\..*";
}
}
private boolean skipModule(ModuleType typeModule) {
return typeModule == ModuleType.ObjectModule && !checkObjectModule
|| typeModule == ModuleType.RecordSetModule && !checkRecordSetModule
|| typeModule == ModuleType.FormModule && !checkFormModule;
}
private static boolean notHasAccessIndex(BSLParser.AcceptorContext acceptor) {
var modifiers = acceptor.modifier();
return modifiers == null
|| modifiers.isEmpty()
|| modifiers.get(0) == null
|| modifiers.get(0).accessIndex() == null;
}
private static boolean hasAccessProperty(BSLParser.AcceptorContext acceptor) {
return acceptor.accessProperty() != null;
}
}