An example of creating a new diagnostic¶
Diagnostics is a class that analyzes the source code to identify a problem or error.
Diagnostic contents (briefly)¶
To implement diagnostics, you need to create several files
- Diagnostic implementation class
- Two resource files (for English and Russian) containing the name of the diagnostic and the error message
- BSL fixture file used for diagnostic testing
- Diagnostic test class
- Two files (for English and Russian) with diagnostic documentation
A detailed description of the contents of the files and the rules of use can be found in article.
Create Diagnostic¶
Below is an example of creating diagnostics based on the already created one.
Determining the purpose of diagnostic¶
Before implementing a new diagnostic, you need to define its purpose - what error (or deficiency) you need to detect. As an example, we will write a diagnostic that checks for the presence of a semicolon ;
at the end of each expression.
The next step is to come up with a unique diagnostic key, under which it will be added to the general list of diagnostics. Let's take the name SemicolonPresence
.
Diagnostic implementation class¶
In the directory src/main/java
in the package com.github._1c_syntax.bsl.languageserver.diagnostics
, create the SemicolonPresenceDiagnostic.java
diagnostic class file.
In the file, create the class of the same name, inherited from the AbstractVisitorDiagnostic
class. The result will be
package com.github._1c_syntax.bsl.languageserver.diagnostics;
public class SemicolonPresenceDiagnostic extends AbstractVisitorDiagnostic {
}
Each diagnostic must have a @DiagnosticMetadata
, class annotation containing diagnostic metadata. Details on annotations in [article][DiagnosticStructure]. In the example, we implement diagnostics related to the quality of the code (CODE_SMELL
), low priority (MINOR
), which requires 1 minute to fix and related to the 1C standard. The resulting annotated class
package com.github._1c_syntax.bsl.languageserver.diagnostics;
@DiagnosticMetadata(
type = DiagnosticType.CODE_SMELL,
severity = DiagnosticSeverity.MINOR,
minutesToFix = 1,
tag = {
DiagnosticTag.STANDARD
}
)
public class SemicolonPresenceDiagnostic extends AbstractVisitorDiagnostic {
}
Class resources¶
In the directory src/main/resources
in the package com.github._1c_syntax.bsl.languageserver.diagnostics
create 2 diagnostic resource files. In our example, these will be files SemicolonPresenceDiagnostic_ru.properties
and SemicolonPresenceDiagnostic_en.properties
.
Save the name (parameter diagnosticName
) and message (diagnosticMessage
) of the diagnostic in the created files.
In our example, the contents of the files will be like this
File SemicolonPresenceDiagnostic_ru.properties
diagnosticMessage =Missing semicolon at end of expression
diagnosticName =Expression must end with ";"
File SemicolonPresenceDiagnostic_en.properties
diagnosticMessage=Missed semicolon at the end of statement
diagnosticName=Statement should end with ";"
Test fixtures¶
For testing purposes, let's add a file to the project containing examples of both erroneous and correct code. We will place the SemicolonPresenceDiagnostic.bsl
file with fixtures in the src/test/resources
resources directory in the diagnostics
package.
As data for testing, let's add the code to the file
A = 0;
If True Then
A = 0;
A = 0 // Diagnostics should work here
EndIf // and here
Attention!
It is necessary to mark with comments the places where the diagnostics should work.
Test writing¶
In the directory src/test/java
in the package com.github._1c_syntax.bsl.languageserver.diagnostics
create a file SemicolonPresenceDiagnosticTest.java
for the test diagnostic class.
In the file, create a class of the same name inherited from the AbstractDiagnosticTest
class for the created diagnostic class.
As a result, we get
package com.github._1c_syntax.bsl.languageserver.diagnostics;
class SemicolonPresenceDiagnosticTest extends AbstractDiagnosticTest<SemicolonPresenceDiagnostic>{
SemicolonPresenceDiagnosticTest() {
super(SemicolonPresenceDiagnostic.class);
}
}
Simplified basic test consists of the steps
- getting a list of diagnostics
- checking the number of items found
- checking the location of detected items
Without going into detail, remember that
- To get diagnostics for a fixture, use the
getDiagnostics()
method implemented in the abstract classAbstractDiagnosticTest
. It returns a list of encountered errors of the current type. - To check the number of errors, use the
hasSize
, assertion, the parameter of which will be the number of expected elements. - To check for each error, it is necessary to compare the error range with the expected range.
The resulting test class
class SemicolonPresenceDiagnosticTest extends AbstractDiagnosticTest<SemicolonPresenceDiagnostic> {
SemicolonPresenceDiagnosticTest() {
super(SemicolonPresenceDiagnostic.class);
}
@Test
void test() {
List<Diagnostic> diagnostics = getDiagnostics();
assertThat(diagnostics).hasSize(2);
assertThat(diagnostics, true)
.hasRange(4, 0, 4, 9)
.hasRange(3, 6, 3, 7);
}
}
Diagnostic algorithm implementation¶
According to the rules of the grammar of the language, described in the project BSLParser, for our example, it is necessary to analyze nodes with the type statement
. Need to use visitor visitStatement
. Each selected node of the AST-tree must contain an ending "semicolon" represented by node SEMICOLON
.
Thus, the check will be to find the SEMICOLON
token in each statement
node. If the token is not found, then an error must be added.
After implementation of the check, the file will take the form
package com.github._1c_syntax.bsl.languageserver.diagnostics;
@DiagnosticMetadata(
type = DiagnosticType.CODE_SMELL,
severity = DiagnosticSeverity.MINOR,
minutesToFix = 1,
tag = {
DiagnosticTag.STANDARD
}
)
public class SemicolonPresenceDiagnostic extends AbstractVisitorDiagnostic {
@Override
public ParseTree visitStatement(BSLParser.StatementContext ctx) { // selected visitor
if (ctx.SEMICOLON() == null) { // getting child node SEMICOLON
diagnosticStorage.addDiagnostic(ctx); // adding error
}
// For non-terminal expressions, the super method must be
// called as the return value.
return super.visitStatement(ctx);
}
}
It is necessary to run a diagnostic test and make sure that it works correctly.
Creating a diagnostic description¶
To describe the created diagnostics, create two files SemicolonPresence.md
: in catalogdocs/diagnostics
in Russian, in catalog docs/en/diagnostics
in English.
Completion¶
The help index is updated automatically when the documentation site is built, so you don't have to do anything by hand.
Before finishing development, you need to run gradlew precommit
from the command line or precommit
from the Gradle taskbar in the IDE.