forked from phoedos/pmd
Merge remote-tracking branch 'upstream/pmd/7.0.x' into text-utils-simple
This commit is contained in:
commit
e248156d96
@ -3267,7 +3267,8 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/24591067?v=4",
|
||||
"profile": "https://github.com/jborgers",
|
||||
"contributions": [
|
||||
"bug"
|
||||
"bug",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -6730,6 +6731,33 @@
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "stokpop",
|
||||
"name": "Peter Paul Bakker",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8797018?v=4",
|
||||
"profile": "https://www.stokpop.nl/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ASBrouwers",
|
||||
"name": "ASBrouwers",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23551289?v=4",
|
||||
"profile": "https://github.com/ASBrouwers",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "341816041",
|
||||
"name": "茅延安",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/100549608?v=4",
|
||||
"profile": "https://github.com/341816041",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@ -412,6 +412,9 @@ entries:
|
||||
- title: HTML
|
||||
url: /pmd_languages_html.html
|
||||
output: web, pdf
|
||||
- title: Gherkin
|
||||
url: /pmd_languages_gherkin.html
|
||||
output: web, pdf
|
||||
- title: Developer Documentation
|
||||
output: web, pdf
|
||||
folderitems:
|
||||
|
16
docs/pages/pmd/languages/gherkin.md
Normal file
16
docs/pages/pmd/languages/gherkin.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: Gherkin
|
||||
permalink: pmd_languages_gherkin.html
|
||||
---
|
||||
|
||||
The [Gherkin](https://cucumber.io/docs/gherkin/) language is used to define test cases for the
|
||||
[Cucumber](https://cucumber.io/) testing tool for behavior-driven development.
|
||||
The Gherkin syntax is designed to be non-technical, making it human-readable for a wide audience.
|
||||
|
||||
## Support in PMD
|
||||
Starting from version 6.48.0, Gherkin support was added to CPD.
|
||||
|
||||
### Limitations
|
||||
- Support for Gherkin only extends to CPD to detect code duplication in Cucumber test cases.
|
||||
- While Gherkin keywords have been translated into various
|
||||
languages, CPD currently supports only the English version of the Gherkin language.
|
File diff suppressed because it is too large
Load Diff
@ -19,11 +19,33 @@ This is a {{ site.pmd.release_type }} release.
|
||||
|
||||
### New and noteworthy
|
||||
|
||||
#### Gherkin support
|
||||
Thanks to the contribution from [Anne Brouwers](https://github.com/ASBrouwers) PMD now has CPD support
|
||||
for the [Gherkin](https://cucumber.io/docs/gherkin/) language. It is used to defined test cases for the
|
||||
[Cucumber](https://cucumber.io/) testing tool for behavior-driven development.
|
||||
|
||||
Being based on a proper Antlr grammar, CPD can:
|
||||
|
||||
* ignore comments
|
||||
* honor [comment-based suppressions](pmd_userdocs_cpd.html#suppression)
|
||||
|
||||
### Fixed Issues
|
||||
* java-bestpractices
|
||||
* [#3455](https://github.com/pmd/pmd/issues/3455): \[java] WhileLoopWithLiteralBoolean - false negative with complex expressions
|
||||
* java-design
|
||||
* [#3729](https://github.com/pmd/pmd/issues/3729): \[java] TooManyMethods ignores "real" methods which are named like getters or setters
|
||||
* [#3949](https://github.com/pmd/pmd/issues/3949): \[java] FinalFieldCouldBeStatic - false negative with unnecessary parenthesis
|
||||
* java-performance
|
||||
* [#3625](https://github.com/pmd/pmd/issues/3625): \[java] AddEmptyString - false negative with empty var
|
||||
|
||||
### API Changes
|
||||
|
||||
### External Contributions
|
||||
* [#3984](https://github.com/pmd/pmd/pull/3984): \[java] Fix AddEmptyString false-negative issue - [@LiGaOg](https://github.com/LiGaOg)
|
||||
* [#3988](https://github.com/pmd/pmd/pull/3988): \[java] Modify WhileLoopWithLiteralBoolean to meet the missing case #3455 - [@VoidxHoshi](https://github.com/VoidxHoshi)
|
||||
* [#3992](https://github.com/pmd/pmd/pull/3992): \[java] FinalFieldCouldBeStatic - fix false negative with unnecessary parenthesis - [@dalizi007](https://github.com/dalizi007)
|
||||
* [#3994](https://github.com/pmd/pmd/pull/3994): \[java] TooManyMethods - improve getter/setter detection (#3729) - [@341816041](https://github.com/341816041)
|
||||
* [#4017](https://github.com/pmd/pmd/pull/4017): Add Gherkin support to CPD - [@ASBrouwers](https://github.com/ASBrouwers)
|
||||
|
||||
{% endtocmaker %}
|
||||
|
||||
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang;
|
||||
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
import net.sourceforge.pmd.lang.ast.AstInfo;
|
||||
import net.sourceforge.pmd.lang.ast.Parser;
|
||||
import net.sourceforge.pmd.lang.ast.RootNode;
|
||||
import net.sourceforge.pmd.lang.ast.SourceCodePositioner;
|
||||
import net.sourceforge.pmd.lang.ast.impl.AbstractNode;
|
||||
|
||||
/**
|
||||
* A dummy language implementation whose parser produces a single node.
|
||||
* This is provided for cases where a non-null language is required, but
|
||||
* the parser is not useful. This is useful eg to mock rules when no other
|
||||
* language is on the classpath. This language is not exposed by {@link LanguageRegistry}
|
||||
* and can only be used explicitly with {@link #getInstance()}.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @since 6.48.0
|
||||
*/
|
||||
@Experimental
|
||||
public final class PlainTextLanguage extends BaseLanguageModule {
|
||||
|
||||
private static final Language INSTANCE = new PlainTextLanguage();
|
||||
|
||||
static final String TERSE_NAME = "text";
|
||||
|
||||
private PlainTextLanguage() {
|
||||
super("Plain text", "Plain text", TERSE_NAME, "plain-text-file-goo-extension");
|
||||
addVersion("default", new TextLvh(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton instance of this language.
|
||||
*/
|
||||
public static Language getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final class TextLvh implements LanguageVersionHandler {
|
||||
@Override
|
||||
public Parser getParser() {
|
||||
return parserTask -> new PlainTextFile(parserTask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The only node produced by the parser of {@link PlainTextLanguage}.
|
||||
*/
|
||||
public static final class PlainTextFile extends AbstractNode implements RootNode {
|
||||
private final int beginLine;
|
||||
private final int beginColumn;
|
||||
private final int endLine;
|
||||
private final int endColumn;
|
||||
|
||||
private final AstInfo<PlainTextFile> astInfo;
|
||||
|
||||
PlainTextFile(Parser.ParserTask parserTask) {
|
||||
SourceCodePositioner positioner = new SourceCodePositioner(parserTask.getSourceText());
|
||||
this.beginLine = 1;
|
||||
this.beginColumn = 1;
|
||||
this.endLine = positioner.getLastLine();
|
||||
this.endColumn = positioner.getLastLineColumn();
|
||||
this.astInfo = new AstInfo<>(parserTask, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getXPathNodeName() {
|
||||
return "TextFile";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBeginLine() {
|
||||
return beginLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBeginColumn() {
|
||||
return beginColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndLine() {
|
||||
return endLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndColumn() {
|
||||
return endColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChildAtIndex(int childIndex) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Plain text file (" + endLine + "lines)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AstInfo<? extends RootNode> getAstInfo() {
|
||||
return astInfo;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -154,6 +154,11 @@
|
||||
<artifactId>pmd-fortran</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-gherkin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-go</artifactId>
|
||||
|
@ -26,7 +26,7 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest {
|
||||
private static final String SUPPORTED_LANGUAGES_PMD;
|
||||
|
||||
static {
|
||||
SUPPORTED_LANGUAGES_CPD = "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, go, groovy, html, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, scala, swift, vf, xml]";
|
||||
SUPPORTED_LANGUAGES_CPD = "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, gherkin, go, groovy, html, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, scala, swift, vf, xml]";
|
||||
SUPPORTED_LANGUAGES_PMD = "apex, ecmascript, html, java, jsp, kotlin, modelica, plsql, pom, scala, swift, vf, vm, wsdl, xml, xsl";
|
||||
}
|
||||
|
||||
|
59
pmd-gherkin/pom.xml
Normal file
59
pmd-gherkin/pom.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>pmd-gherkin</artifactId>
|
||||
<name>PMD Gherkin</name>
|
||||
|
||||
<parent>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd</artifactId>
|
||||
<version>7.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<configuration>
|
||||
<useDefaultDelimiters>false</useDefaultDelimiters>
|
||||
<delimiters>
|
||||
<delimiter>${*}</delimiter>
|
||||
</delimiters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-lang-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,118 @@
|
||||
grammar Gherkin;
|
||||
|
||||
// PARSER
|
||||
|
||||
main
|
||||
// start comment needed because each comment should start on a new line except for the start comment
|
||||
: STARTCOMMENT? feature description* instructionLine* NL* EOF
|
||||
;
|
||||
|
||||
feature
|
||||
: (NL* tagline)* NL* FEATURE?
|
||||
;
|
||||
|
||||
instructionLine
|
||||
: NL+ (instruction | datatable)
|
||||
;
|
||||
|
||||
instruction
|
||||
: rulex description* // the name "rule" is not allowed by ANTLR (used for internal usage), so calling it rulex
|
||||
| stepInstruction description* (NL+ stepDescription description*)* (NL+ step)*
|
||||
| tagline
|
||||
| instructionDescription description*
|
||||
;
|
||||
|
||||
stepInstruction
|
||||
: background
|
||||
| scenario
|
||||
| scenarioOutline
|
||||
;
|
||||
|
||||
background: BACKGROUND ;
|
||||
rulex: RULEX ;
|
||||
scenario: SCENARIO ;
|
||||
scenarioOutline : SCENARIOOUTLINE ;
|
||||
|
||||
step : stepItem description*;
|
||||
|
||||
stepItem
|
||||
: and
|
||||
| anystep
|
||||
| but
|
||||
| datatable
|
||||
| given
|
||||
| then
|
||||
| when
|
||||
| (NL* tagline )* NL* examples
|
||||
;
|
||||
|
||||
tagline
|
||||
: TAG+
|
||||
;
|
||||
|
||||
and: AND ;
|
||||
anystep: ANYSTEP ;
|
||||
but: BUT ;
|
||||
datatable: DATATABLE+ ;
|
||||
given: GIVEN ;
|
||||
then: THEN ;
|
||||
when: WHEN ;
|
||||
examples: EXAMPLES ;
|
||||
|
||||
// Descriptions
|
||||
instructionDescription: text | PARAMETER | AND | ANYSTEP | BUT | GIVEN | THEN | WHEN | SCENARIO ; // We have to deal with overlaps with keywords
|
||||
stepDescription: text | PARAMETER ; // We have to deal with overlaps with keywords
|
||||
description: text | PARAMETER | TAG | AND | ANYSTEP | BUT | DATATABLE | GIVEN | THEN | WHEN | SCENARIO | SCENARIOOUTLINE | STARTCOMMENT ; // We have to deal with overlaps with keywords
|
||||
|
||||
text: TOKEN+ ;
|
||||
|
||||
// LEXER
|
||||
|
||||
// skipped
|
||||
|
||||
BOMUTF8 : '\u00EF\u00BB\u00BF' -> skip ;
|
||||
|
||||
BOMUTF16 : '\uFEFF' -> skip ;
|
||||
|
||||
WHITESPACE: [ \t]+ -> channel(1) ;
|
||||
|
||||
COMMENT: '\r'?'\n' [ \t]* '#' ~[\r\n]* -> channel(2) ;
|
||||
|
||||
STARTCOMMENT: '#' ~[\r\n]* ;
|
||||
|
||||
DOCSTRING1
|
||||
: '"""' .*? '"""' ;
|
||||
|
||||
DOCSTRING2
|
||||
: '```' .*? '```' ;
|
||||
|
||||
// Instructions
|
||||
BACKGROUND: 'Background:' ;
|
||||
EXAMPLES: ('Examples:' | 'Scenarios:') ;
|
||||
FEATURE: 'Feature:';
|
||||
RULEX: 'Rule:' ;
|
||||
SCENARIO: ('Example:' | 'Scenario:') ;
|
||||
SCENARIOOUTLINE : 'Scenario ' ('Outline:' | 'Template:') ;
|
||||
|
||||
// Steps
|
||||
AND: 'And' ;
|
||||
ANYSTEP: '*' ;
|
||||
BUT: 'But' ;
|
||||
DATATABLE: '|' DATATABLEID? ; // must be an ID because it can contain a space
|
||||
GIVEN: 'Given' ;
|
||||
THEN: 'Then' ;
|
||||
WHEN: 'When' ;
|
||||
|
||||
TAG: '@' ELEMENT+ ;
|
||||
PARAMETER: '<' PARID '>' | '"' '<' PARID '>' '"' | '\'' '<' PARID '>' '\'';
|
||||
fragment PARID: [A-Za-z0-9] ([!-=?-~ ]* [!-=?-~])?; // start with an alpha numerical and then all printable characters and end with a non-space
|
||||
fragment ID: (IDELEMENT | ' ')* IDELEMENT (IDELEMENT | ' ')*; // ID should contain at least one non-whitespace character otherwise the trailing | with a trailing space will match
|
||||
fragment DATATABLEID: (DATATABLEELEMENT | ' ')* DATATABLEELEMENT (DATATABLEELEMENT | ' ')*; // ID should contain at least one non-whitespace character otherwise the trailing | with a trailing space will match
|
||||
fragment DATATABLEELEMENT: ELEMENT | '<' | '>' | '"' | '\'' | '\\|' ;
|
||||
fragment IDELEMENT: ELEMENT | '|' ;
|
||||
fragment ELEMENT: [!-&(-;=?-{}~\u00A0-\uFFFF] ;
|
||||
|
||||
NL: '\r'? '\n' ;
|
||||
TOKEN: [!-{}-~\u00A0-\uFFFF]+ ; // match everything that isn't matched yet
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains the Antlr grammar for Gherkin.
|
||||
*/
|
||||
package net.sourceforge.pmd.lang.gherkin.ast;
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.gherkin.cpd;
|
||||
|
||||
import net.sourceforge.pmd.cpd.AbstractLanguage;
|
||||
|
||||
/**
|
||||
* Language implementation for Gherkin.
|
||||
*/
|
||||
public class GherkinLanguage extends AbstractLanguage {
|
||||
|
||||
/**
|
||||
* Creates a new Gherkin Language instance.
|
||||
*/
|
||||
public GherkinLanguage() {
|
||||
super("Gherkin", "gherkin", new GherkinTokenizer(), ".feature");
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.gherkin.cpd;
|
||||
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
|
||||
import net.sourceforge.pmd.cpd.SourceCode;
|
||||
import net.sourceforge.pmd.cpd.internal.AntlrTokenizer;
|
||||
import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrTokenManager;
|
||||
import net.sourceforge.pmd.lang.gherkin.ast.GherkinLexer;
|
||||
|
||||
/**
|
||||
* The Gherkin Tokenizer.
|
||||
*/
|
||||
public class GherkinTokenizer extends AntlrTokenizer {
|
||||
|
||||
@Override
|
||||
protected AntlrTokenManager getLexerForSource(SourceCode sourceCode) {
|
||||
CharStream charStream = AntlrTokenizer.getCharStreamFromSourceCode(sourceCode);
|
||||
return new AntlrTokenManager(new GherkinLexer(charStream), sourceCode.getFileName());
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains Gherkin tokenizer and language classes.
|
||||
*/
|
||||
package net.sourceforge.pmd.lang.gherkin.cpd;
|
@ -0,0 +1 @@
|
||||
net.sourceforge.pmd.lang.gherkin.cpd.GherkinLanguage
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest;
|
||||
import net.sourceforge.pmd.lang.gherkin.cpd.GherkinTokenizer;
|
||||
|
||||
public class GherkinTokenizerTest extends CpdTextComparisonTest {
|
||||
public GherkinTokenizerTest() {
|
||||
super(".feature");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResourcePrefix() {
|
||||
return "../lang/gherkin/cpd/testdata";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tokenizer newTokenizer(Properties properties) {
|
||||
GherkinTokenizer tok = new GherkinTokenizer();
|
||||
return tok;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatedSource() {
|
||||
doTest("annotatedSource");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDocstring() {
|
||||
doTest("docstring");
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
@AnnotatedSource
|
||||
Feature: Annotated Source
|
||||
The annotated source displays violations in the source file. It opens in a new window.
|
||||
|
||||
Rules:
|
||||
1 Annotation metrics can be selected with a dropdown menu. Only enabled metrics must be shown.
|
||||
2 When the annotated source is opened while a specific metric is selected, only that metric should be selected.
|
||||
3 A user can scroll through the violations using two buttons (illustrated by up and down arrows). The arrows wrap around the document.
|
||||
4 The table collumn "Type" is only shown when multiple metrics are selected
|
||||
|
||||
Scenario: Select a metric type
|
||||
Given the Annotated Source for file "HIE://11261-37/main/monop/execute.c"
|
||||
When a user opens the dropdown menu containing "Metric:"
|
||||
And the user clicks on the dropdown option "Violations/Coding Standard Violations"
|
||||
Then the selected annotation in the source code should be on line 38
|
||||
And the selected annotation in the table should be on line 38
|
||||
|
||||
Scenario: The user can use the arrows, or "a" and "z" keys, to scroll through the annotations
|
||||
Given the Annotated Source for file "HIE://11261-37/main/monop/execute.c"
|
||||
And metric "Coding Standard Violation Annotations" is selected
|
||||
When the user clicks on the down arrow
|
||||
And the user presses the "Z" key
|
||||
And the user clicks on the up arrow
|
||||
And the user clicks on the up arrow
|
||||
And the user presses the "A" key
|
||||
Then the selected annotation in the source code should be on line 254
|
||||
|
||||
@Rule2
|
||||
Scenario Outline: If the user opens the annotated source from e.g. the dashboard for a metric,
|
||||
only the related annotations should be shown.
|
||||
Given the Dashboard
|
||||
And filtering by Project "17607"
|
||||
And grouping by "File"
|
||||
And metric "<metric>" is selected
|
||||
When a user opens the file "clalgorithm_settings.c" using the metric table
|
||||
Then a new browser window with an "Annotated Source" should be opened
|
||||
Then only the annotations "<annotations>" should be selected
|
||||
|
||||
Examples:
|
||||
| metric | annotations |
|
||||
| TQI Coding Standards | Coding Standard Violation Annotations |
|
||||
| Coding Standard Violations | Coding Standard Violation Annotations |
|
||||
| TQI Compiler Warnings | Compiler Warning Annotations |
|
||||
| Fan Out (%) | Fan Out Annotations |
|
||||
| TQI Dead Code | Dead Code Annotations |
|
||||
| TQI Code Duplication | Code Duplication Annotations |
|
||||
|
||||
Scenario: The user should be able to filter Coding Standard Violations by Level
|
||||
Given the Annotated Source for file "HIE://11514/trunk/components/java/BuildUtil/src/com/tiobe/util/BuildProperties.java"
|
||||
And the metric "Coding Standard Violations" is selected
|
||||
When the user opens the dropdown menu "Level"
|
||||
And the user clicks on the dropdown option "5"
|
||||
Then there should be 1 violation
|
||||
And the selected annotation in the source code should be on line 57
|
||||
And the annotation should be of level 5
|
||||
|
||||
@PR27030
|
||||
Scenario Outline: The user should be able to filter Coding Standard Violations by Level, Category, Rule, etc
|
||||
Given the Annotated Source for file "HIE://12939/main/Implementatie/DRGL/src/DirectDoorvoerenAdmin.cpp"
|
||||
And metric "<metric>" is selected
|
||||
When the user opens the dropdown menu containing "<filter>" inside the filter bar
|
||||
And the user clicks on the dropdown option "<option>"
|
||||
Then there should be <number> violations
|
||||
|
||||
Examples:
|
||||
| metric | filter | option | number |
|
||||
| Coding Standard Violations | Level | 2 | 7 |
|
||||
| Coding Standard Violations | Category | Comments | 1 |
|
||||
| Coding Standard Violations | Rule | CFL#011 | 2 |
|
||||
| Coding Standard Violations | Suppressions | Yes | 0 |
|
||||
| TQI Code Coverage | Coverage Type | Decision | 7 |
|
||||
| TQI Code Coverage | Kind | Issue | 10 |
|
||||
|
637
pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/annotatedSource.txt
vendored
Normal file
637
pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/annotatedSource.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
Given I have a lot to say:
|
||||
"""
|
||||
One
|
||||
Two
|
||||
Three
|
||||
"""
|
15
pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/docstring.txt
vendored
Normal file
15
pmd-gherkin/src/test/resources/net/sourceforge/pmd/lang/gherkin/cpd/testdata/docstring.txt
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
[Image] or [Truncated image[ Bcol Ecol
|
||||
L1
|
||||
[Given] 1 6
|
||||
[I] 7 8
|
||||
[have] 9 13
|
||||
[a] 14 15
|
||||
[lot] 16 19
|
||||
[to] 20 22
|
||||
[say:] 23 27
|
||||
[\n] 27 28
|
||||
L2
|
||||
["""\n One\n Two\n Three\n """] 1 5
|
||||
L6
|
||||
[\n] 5 6
|
||||
EOF
|
@ -1824,21 +1824,48 @@ a block `{}` is sufficient.
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value><![CDATA[
|
||||
//DoStatement/BooleanLiteral
|
||||
| //WhileStatement/BooleanLiteral[@True = false()]
|
||||
(: while loops with single boolean literal, maybe parenthesized :)
|
||||
//WhileStatement/BooleanLiteral[@True = false()]
|
||||
|
|
||||
(: do-while loops with single boolean literal, maybe parenthesized :)
|
||||
//DoStatement/BooleanLiteral
|
||||
|
|
||||
(: while loops with conditional or'ed or and'ed boolean literals, maybe parenthesized :)
|
||||
//WhileStatement[(.|InfixExpression)
|
||||
[count(BooleanLiteral) = 2]
|
||||
(: at least one false literal :)
|
||||
[count(BooleanLiteral[@True = false()]) >= 1]]
|
||||
|
|
||||
(: while loops with conditional and'ed boolean literals, maybe parenthesized :)
|
||||
//WhileStatement[(InfixExpression[@Operator=('&', '&&')])
|
||||
(: at least one false literal :)
|
||||
[count(BooleanLiteral[@True = false()]) >= 1]]
|
||||
|
|
||||
(: do-while loops with conditional or'ed or and'ed boolean literals, maybe parenthesized :)
|
||||
//DoStatement[(.|InfixExpression)
|
||||
[count(BooleanLiteral) = 2]]
|
||||
|
|
||||
(: do-while loops with conditional and'ed boolean literals, maybe parenthesized :)
|
||||
//DoStatement[(InfixExpression[@Operator=('&', '&&')])
|
||||
(: at least one false literal :)
|
||||
[count(BooleanLiteral[@True = false()]) >= 1]]
|
||||
]]></value>
|
||||
</property>
|
||||
<property name="version" value="2.0" />
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Example {
|
||||
{
|
||||
while (true) { } // allowed
|
||||
while (false) { } // disallowed
|
||||
do { } while (true); // disallowed
|
||||
do { } while (false); // disallowed
|
||||
do { } while (false | false); // disallowed
|
||||
do { } while (false || false); // disallowed
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
|
@ -785,7 +785,8 @@ in each object at runtime.
|
||||
[not(.//Annotation[pmd-java:typeIs('lombok.Builder.Default')])]
|
||||
/VariableDeclarator[*[pmd-java:nodeIs('Literal')]
|
||||
or VariableAccess[@Name = //FieldDeclaration[pmd-java:modifiers() = 'static']/VariableDeclarator/VariableDeclaratorId/@Name]
|
||||
or FieldAccess]
|
||||
or FieldAccess
|
||||
or ArrayAllocation/ArrayType/ArrayDimensions/ArrayDimExpr/NumericLiteral[@IntLiteral = true()][@Image = "0"]]
|
||||
/VariableDeclaratorId
|
||||
]]>
|
||||
</value>
|
||||
@ -1391,11 +1392,9 @@ complexity and find a way to have more fine grained objects.
|
||||
[
|
||||
count(MethodDeclaration[
|
||||
not (
|
||||
starts-with(@Name,'get')
|
||||
or
|
||||
starts-with(@Name,'set')
|
||||
or
|
||||
starts-with(@Name,'is')
|
||||
(starts-with(@Name,'get') or starts-with(@Name,'set') or starts-with(@Name,'is'))
|
||||
and
|
||||
count(Block/*) <= 1
|
||||
)
|
||||
]) > $maxmethods
|
||||
]
|
||||
|
@ -17,7 +17,7 @@ Rules that flag suboptimal code.
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#addemptystring">
|
||||
<description>
|
||||
The conversion of literals to strings by concatenating them with empty strings is inefficient.
|
||||
It is much better to use one of the type-specific toString() methods instead.
|
||||
It is much better to use one of the type-specific `toString()` methods instead or `String.valueOf()`.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
@ -25,6 +25,12 @@ It is much better to use one of the type-specific toString() methods instead.
|
||||
<value>
|
||||
<![CDATA[
|
||||
//InfixExpression[@Operator='+']/StringLiteral[@Empty=true()][not(ancestor::Annotation)]
|
||||
|
|
||||
//InfixExpression[@Operator='+']/VariableAccess
|
||||
[@Name = (//FieldDeclaration[pmd-java:modifiers() = 'final']|ancestor::MethodDeclaration//LocalVariableDeclaration[pmd-java:modifiers() = 'final'])
|
||||
/VariableDeclarator[@Initializer = true()]
|
||||
[StringLiteral[@Empty=true()]]
|
||||
/VariableDeclaratorId/@Name]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
|
@ -6,13 +6,31 @@
|
||||
|
||||
<test-code>
|
||||
<description>do while true</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>4</expected-linenumbers>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-linenumbers>4,6</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do {
|
||||
} while (true);
|
||||
do {
|
||||
} while ((true));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>do while true | true</description>
|
||||
<expected-problems>4</expected-problems>
|
||||
<expected-linenumbers>3,4,5,6</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do { } while (true | true);
|
||||
do { } while (true || true);
|
||||
do { } while ((true | true));
|
||||
do { } while ((true || true));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
@ -20,13 +38,48 @@ class Foo {
|
||||
|
||||
<test-code>
|
||||
<description>do while false</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>4</expected-linenumbers>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-linenumbers>4,6</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do {
|
||||
} while (false);
|
||||
do {
|
||||
} while ((false));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>do while false | false #3455</description>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-linenumbers>3,5</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do {
|
||||
} while (false | false);
|
||||
do {
|
||||
} while ((false | false));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>do while false || false #3455</description>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-linenumbers>3,6</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do {
|
||||
} while (false || false);
|
||||
|
||||
do {
|
||||
} while ((false || false));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
@ -46,7 +99,7 @@ class Foo {
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>while true</description>
|
||||
<description>while true - allowed</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
@ -60,13 +113,31 @@ class Foo {
|
||||
|
||||
<test-code>
|
||||
<description>while false</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>3</expected-linenumbers>
|
||||
<expected-problems>2</expected-problems>
|
||||
<expected-linenumbers>3,5</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
while (false) {
|
||||
}
|
||||
while ((false)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>while false | false</description>
|
||||
<expected-problems>4</expected-problems>
|
||||
<expected-linenumbers>3,4,5,6</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
while (false | false) { }
|
||||
while ((false | false)) { }
|
||||
while (false || false) { }
|
||||
while ((false || false)) { }
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
@ -84,4 +155,82 @@ class Foo {
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>conditional or with only one boolean literal</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
void bar(boolean arg) {
|
||||
while (false | arg) { }
|
||||
while (false || arg) { }
|
||||
do { } while (false || arg);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>conditional and with only one boolean literal</description>
|
||||
<expected-problems>3</expected-problems>
|
||||
<expected-linenumbers>3,4,5</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
void bar(boolean arg) {
|
||||
while (false & arg) { }
|
||||
while (false && arg) { }
|
||||
do { } while (false && arg);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>conditional-and boolean literal</description>
|
||||
<expected-problems>28</expected-problems>
|
||||
<expected-linenumbers>10,11,12,13,14,15,16,17,18,19,20,21,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
// these evaluate to true and are allowed
|
||||
while (true & true) { }
|
||||
while ((true & true)) { }
|
||||
while (true && true) { }
|
||||
while ((true && true)) { }
|
||||
|
||||
// these evaluate to false and should be flagged
|
||||
while (false & false) { } // line 10
|
||||
while ((false & false)) { }
|
||||
while (false && false) { }
|
||||
while ((false && false)) { }
|
||||
while (false & true) { }
|
||||
while ((false & true)) { }
|
||||
while (false && true) { }
|
||||
while ((false && true)) { }
|
||||
while (true & false) { }
|
||||
while ((true & false)) { }
|
||||
while (true && false) { }
|
||||
while ((true && false)) { }
|
||||
|
||||
// do-while loops should always be flagged
|
||||
do {} while (false & false);
|
||||
do {} while ((false & false));
|
||||
do {} while (false && false);
|
||||
do {} while ((false && false));
|
||||
do {} while (true & false);
|
||||
do {} while ((true & false));
|
||||
do {} while (true && false);
|
||||
do {} while ((true && false));
|
||||
do {} while (false & true);
|
||||
do {} while ((false & true));
|
||||
do {} while (false && true);
|
||||
do {} while ((false && true));
|
||||
do {} while (true & true);
|
||||
do {} while ((true & true));
|
||||
do {} while (true && true);
|
||||
do {} while ((true && true));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
</test-data>
|
||||
|
@ -193,6 +193,45 @@ public class Foo {
|
||||
//private static final int staticFinal = nonStatic; //noncompliant: Non-static field 'nonStatic' cannot be referenced from a static context
|
||||
private static int staticNonFinal = 1; //no violation cause non-final
|
||||
private final int nonStatic2 = staticNonFinal; //violation because it could be static
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>#3949 - FinalFieldCouldBeStatic false negative with unnecessary parenthesis</description>
|
||||
<expected-problems>3</expected-problems>
|
||||
<expected-linenumbers>5,8,9</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Foo {
|
||||
public final int BAR = (42);
|
||||
|
||||
// these empty arrays could theoretically be shared and therefore be static
|
||||
private final Object[] argsObjs1 = new Object[0];
|
||||
private final Object[] argsObjs2 = new Object[(0)];
|
||||
|
||||
// not flagging anonymous class instantiation
|
||||
private final StringBuffer mFilter = new StringBuffer(new CharSequence() {
|
||||
@Override public char charAt(int index) { return 'A'; }
|
||||
@Override public int length() { return 1; }
|
||||
@Override public CharSequence subSequence(int start, int end) { return this; }
|
||||
@Override public String toString() { return "Foo"; }
|
||||
});
|
||||
|
||||
// not flagging any instantiation in order to avoid false positives
|
||||
// especially for lists one could still require a separate list for each instance...
|
||||
public final List<String> mList = new ArrayList<>();
|
||||
|
||||
// not flagging instantiation of boxed types - would be a different rule
|
||||
// this is to keep this rule simple
|
||||
public final Integer DefaultInit = new Integer(27);
|
||||
private final String mString = new String("Foo");
|
||||
|
||||
// not flagging array creation. Same reasoning as for lists:
|
||||
// one could still require a separate array for each instance...
|
||||
private final int[] p = new int[42];
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
@ -113,4 +113,53 @@ public class OuterClass {
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<code-fragment id="code_22_real_methods"><![CDATA[
|
||||
public class Foo {
|
||||
public void setMethod1(){int a = 1;a++;}
|
||||
public void setMethod2(){int a = 1;a++;}
|
||||
public void setMethod3(){int a = 1;a++;}
|
||||
public void setMethod4(){int a = 1;a++;}
|
||||
public void setMethod5(){int a = 1;a++;}
|
||||
|
||||
public void getMethod6(){int a = 1;a++;}
|
||||
public void getMethod7(){int a = 1;a++;}
|
||||
public void getMethod8(){int a = 1;a++;}
|
||||
public void getMethod9(){int a = 1;a++;}
|
||||
public void getMethod10(){int a = 1;a++;}
|
||||
|
||||
public boolean isMethod11(){int a = 1;a++;}
|
||||
public boolean isMethod12(){int a = 1;a++;}
|
||||
public boolean isMethod13(){int a = 1;a++;}
|
||||
public boolean isMethod14(){int a = 1;a++;}
|
||||
public boolean isMethod15(){int a = 1;a++;}
|
||||
public boolean isMethod16(){int a = 1;a++;}
|
||||
|
||||
public void method17(){}
|
||||
public void method18(){}
|
||||
public void method19(){}
|
||||
public void method20(){}
|
||||
public void method21(){}
|
||||
public void method22(){}
|
||||
|
||||
private String field;
|
||||
public String getField() { return field; } // real getter
|
||||
public void setField(String field) { this.field = field; } // real setter
|
||||
}
|
||||
]]>
|
||||
</code-fragment>
|
||||
|
||||
<test-code>
|
||||
<description>#3729 TooManyMethods ignores "real" methods which are named like getters or setters - default</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>1</expected-linenumbers>
|
||||
<code-ref id="code_22_real_methods"/>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>#3729 TooManyMethods ignores "real" methods which are named like getters or setters - max 22 methods</description>
|
||||
<rule-property name="maxmethods">22</rule-property>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code-ref id="code_22_real_methods"/>
|
||||
</test-code>
|
||||
</test-data>
|
||||
|
@ -50,6 +50,72 @@ public class Foo {
|
||||
@Deprecated(since = "" + VERSION)
|
||||
public void foo() {
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>[java] AddEmptyString - false negative with empty var #3625</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>4</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
public class Main {
|
||||
public void func() {
|
||||
final String var = "";
|
||||
String test = var + "123"; // should report a warning here
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>Local Variables be empty String</description>
|
||||
<expected-problems>3</expected-problems>
|
||||
<expected-linenumbers>8,9,10</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Main {
|
||||
public static void main(String[] args) {
|
||||
final String string1 = "";
|
||||
final String string2 = "";
|
||||
final String string3 = "";
|
||||
String string4 = "";
|
||||
|
||||
String a = string1 + 114;
|
||||
String b = string2 + 514;
|
||||
String c = string3 + 1919810;
|
||||
|
||||
string4 = "foo";
|
||||
String d = string4 + 1; // should not be flagged, because string4 is not empty anymore
|
||||
|
||||
final String s = "bar";
|
||||
String e = s + 2; // should not be flagged, because s is not empty
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Local and global variables be empty String</description>
|
||||
<expected-problems>4</expected-problems>
|
||||
<expected-linenumbers>9,10,11,12</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Main {
|
||||
final String outerString1 = "";
|
||||
final String outerString2 = "";
|
||||
|
||||
public static void main(String[] args, String otherString) {
|
||||
final String innerString1 = "";
|
||||
final String innerString2 = "";
|
||||
|
||||
String a = outerString1 + 114;
|
||||
String b = outerString2 + 514;
|
||||
String c = innerString1 + 1919;
|
||||
String d = innerString2 + 810;
|
||||
String e = otherString + 42; // should not be flagged, otherString is a method parameter. Not to be confused with otherString local var in otherMethod
|
||||
}
|
||||
|
||||
void otherMethod() {
|
||||
final String otherString = "";
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
@ -4,11 +4,15 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.kotlin;
|
||||
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
import net.sourceforge.pmd.lang.BaseLanguageModule;
|
||||
|
||||
/**
|
||||
* Language Module for Kotlin
|
||||
*
|
||||
* <p>Note: Kotlin support is considered an experimental feature. The AST structure might change.</p>
|
||||
*/
|
||||
@Experimental
|
||||
public class KotlinLanguageModule extends BaseLanguageModule {
|
||||
|
||||
/** The name. */
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package net.sourceforge.pmd.test.lang;
|
||||
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler;
|
||||
import net.sourceforge.pmd.lang.BaseLanguageModule;
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
@ -20,7 +21,12 @@ import net.sourceforge.pmd.test.lang.ast.DummyNode;
|
||||
|
||||
/**
|
||||
* Dummy language used for testing PMD.
|
||||
*
|
||||
* @deprecated Don't use this directly. We can probably remove this in favour of plaintextlanguage
|
||||
* when https://github.com/pmd/pmd/issues/3918 is merged
|
||||
*/
|
||||
@Deprecated
|
||||
@InternalApi
|
||||
public class DummyLanguageModule extends BaseLanguageModule {
|
||||
|
||||
public static final String NAME = "Dummy";
|
||||
|
Loading…
x
Reference in New Issue
Block a user