Skip to content

Commit

Permalink
Merge pull request #4387 from adangel/pmd7-language-versions
Browse files Browse the repository at this point in the history
  • Loading branch information
oowekyala committed Feb 26, 2023
2 parents 22871ad + bf2c699 commit f2f8357
Show file tree
Hide file tree
Showing 56 changed files with 493 additions and 304 deletions.
36 changes: 18 additions & 18 deletions docs/_data/sidebars/pmd_sidebar.yml
Expand Up @@ -160,24 +160,6 @@ entries:
- title: Security
output: web, pdf
url: /pmd_rules_apex_security.html
- title: null
output: web, pdf
subfolders:
- title: Ecmascript Rules
output: web, pdf
subfolderitems:
- title: Index
output: web, pdf
url: /pmd_rules_ecmascript.html
- title: Best Practices
output: web, pdf
url: /pmd_rules_ecmascript_bestpractices.html
- title: Code Style
output: web, pdf
url: /pmd_rules_ecmascript_codestyle.html
- title: Error Prone
output: web, pdf
url: /pmd_rules_ecmascript_errorprone.html
- title: null
output: web, pdf
subfolders:
Expand Down Expand Up @@ -247,6 +229,24 @@ entries:
- title: Security
output: web, pdf
url: /pmd_rules_jsp_security.html
- title: null
output: web, pdf
subfolders:
- title: JavaScript Rules
output: web, pdf
subfolderitems:
- title: Index
output: web, pdf
url: /pmd_rules_ecmascript.html
- title: Best Practices
output: web, pdf
url: /pmd_rules_ecmascript_bestpractices.html
- title: Code Style
output: web, pdf
url: /pmd_rules_ecmascript_codestyle.html
- title: Error Prone
output: web, pdf
url: /pmd_rules_ecmascript_errorprone.html
- title: null
output: web, pdf
subfolders:
Expand Down
7 changes: 4 additions & 3 deletions docs/pages/7_0_0_release_notes.md
Expand Up @@ -153,9 +153,9 @@ See {% jdoc core::lang.ast.NodeStream %} for more details.
#### JavaScript support

The JS specific parser options have been removed. The parser now always retains comments and uses version ES6.
The language module registers only one version (as before), now correctly with version "ES6" instead of "3".
Since there is only one version available for JavaScript there is actually no need to selected a specific version.
The default version is always ES6.
The language module registers a couple of different versions. The latest version, which supports ES6 and also some
new constructs (see [Rhino](https://github.com/mozilla/rhino)]), is the default. This should be fine for most
use cases.

#### New Rules

Expand Down Expand Up @@ -254,6 +254,7 @@ The following previously deprecated rules have been finally removed:
* [#3782](https://github.com/pmd/pmd/issues/3782): \[core] Language lifecycle
* [#3902](https://github.com/pmd/pmd/issues/3902): \[core] Violation decorators
* [#4035](https://github.com/pmd/pmd/issues/4035): \[core] ConcurrentModificationException in DefaultRuleViolationFactory
* [#4120](https://github.com/pmd/pmd/issues/4120): \[core] Explicitly name all language versions
* cli
* [#3828](https://github.com/pmd/pmd/issues/3828): \[core] Progress reporting
* [#4079](https://github.com/pmd/pmd/issues/4079): \[cli] Split off CLI implementation into a pmd-cli submodule
Expand Down
10 changes: 8 additions & 2 deletions docs/pages/pmd/userdocs/cli_reference.md
Expand Up @@ -239,7 +239,14 @@ non-preview version. If you want to use an older version, so that e.g. rules tha
that are not available yet won't be executed, you need to specify a specific version via the `--use-version`
parameter.

These parameters are irrelevant for languages that don't support different versions.
The selected language version can also influence which rules are applied. Some rules might be relevant for
just a specific version of the language. Such rules are marked with either `minimumLanguageVersion` or
`maximumLanguageVersion` or both. Most rules apply for all language versions.

These parameters are most of the time irrelevant, if the rules apply for all versions.

The available versions depend on the language. You can get a list of the currently supported language versions
via the CLI option `--help`.

Example:

Expand All @@ -260,7 +267,6 @@ Example:
* [plsql](pmd_rules_plsql.html)
* [pom](pmd_rules_pom.html) (Maven POM)
* [scala](pmd_rules_scala.html)
* Supported Versions: 2.10, 2.11, 2.12, 2.13 (default)
* [swift](pmd_rules_swift.html)
* [vf](pmd_rules_vf.html) (Salesforce VisualForce)
* [vm](pmd_rules_vm.html) (Apache Velocity)
Expand Down
47 changes: 5 additions & 42 deletions docs/pages/pmd/userdocs/tools/ant.md
Expand Up @@ -211,49 +211,12 @@ sense with Java 1.7 and later. If your project uses Java 1.5, then you should co
accordingly and this rule won't be executed.

The specific version of a language to be used is selected via the `sourceLanguage`
nested element. Possible values are:

<sourceLanguage name="apex" version="48"/>
<sourceLanguage name="ecmascript" version="3"/>
<sourceLanguage name="java" version="1.3"/>
<sourceLanguage name="java" version="1.4"/>
<sourceLanguage name="java" version="1.5"/>
<sourceLanguage name="java" version="5"/> <!-- alias for 1.5 -->
<sourceLanguage name="java" version="1.6"/>
<sourceLanguage name="java" version="6"/> <!-- alias for 1.6 -->
<sourceLanguage name="java" version="1.7"/>
<sourceLanguage name="java" version="7"/> <!-- alias for 1.7 -->
<sourceLanguage name="java" version="1.8"/>
<sourceLanguage name="java" version="8"/> <!-- alias for 1.8 -->
<sourceLanguage name="java" version="9"/>
<sourceLanguage name="java" version="1.9"/> <!-- alias for 9 -->
<sourceLanguage name="java" version="10"/>
<sourceLanguage name="java" version="1.10"/> <!-- alias for 10 -->
<sourceLanguage name="java" version="11"/>
<sourceLanguage name="java" version="12"/>
<sourceLanguage name="java" version="13"/>
<sourceLanguage name="java" version="14"/>
<sourceLanguage name="java" version="15"/>
<sourceLanguage name="java" version="16"/>
nested element. Example:

<sourceLanguage name="java" version="17"/>
<sourceLanguage name="java" version="18"/>
<sourceLanguage name="java" version="19"/>
<sourceLanguage name="java" version="19-preview"/>
<sourceLanguage name="java" version="20"/> <!-- this is the default -->
<sourceLanguage name="java" version="20-preview"/>
<sourceLanguage name="jsp" version=""/>
<sourceLanguage name="modelica" version=""/>
<sourceLanguage name="pom" version=""/>
<sourceLanguage name="plsql" version=""/>
<sourceLanguage name="scala" version="2.10"/>
<sourceLanguage name="scala" version="2.11"/>
<sourceLanguage name="scala" version="2.12"/>
<sourceLanguage name="scala" version="2.13"/> <!-- this is the default -->
<sourceLanguage name="vf" version=""/>
<sourceLanguage name="vm" version=""/>
<sourceLanguage name="wsdl" version=""/>
<sourceLanguage name="xml" version=""/>
<sourceLanguage name="xsl" version=""/>

The available versions depend on the language. You can get a list of the currently supported language versions
via the CLI option `--help`.

### Postprocessing the report file with XSLT

Expand Down
Expand Up @@ -6,14 +6,16 @@

import java.util.Properties;

import net.sourceforge.pmd.lang.apex.ApexLanguageModule;

public class ApexLanguage extends AbstractLanguage {

public ApexLanguage() {
this(new Properties());
}

public ApexLanguage(Properties properties) {
super("Apex", "apex", new ApexTokenizer(), ".cls");
super(ApexLanguageModule.NAME, ApexLanguageModule.TERSE_NAME, new ApexTokenizer(), ApexLanguageModule.EXTENSIONS);
setProperties(properties);
}

Expand Down
Expand Up @@ -4,22 +4,33 @@

package net.sourceforge.pmd.lang.apex;

import static net.sourceforge.pmd.util.CollectionUtil.listOf;

import java.util.List;

import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageModuleBase;
import net.sourceforge.pmd.lang.LanguageProcessor;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
import net.sourceforge.pmd.lang.LanguageRegistry;

import apex.jorje.services.Version;

public class ApexLanguageModule extends LanguageModuleBase {

public static final String NAME = "Apex";
public static final String TERSE_NAME = "apex";

@InternalApi
public static final List<String> EXTENSIONS = listOf("cls", "trigger");

public ApexLanguageModule() {
super(LanguageMetadata.withId(TERSE_NAME).name(NAME).extensions("cls", "trigger")
.addDefaultVersion(String.valueOf((int) Version.CURRENT.getExternal())));
super(LanguageMetadata.withId(TERSE_NAME).name(NAME).extensions(EXTENSIONS)
.addVersion("52")
.addVersion("53")
.addVersion("54")
.addVersion("55")
.addVersion("56")
.addDefaultVersion("57"));
}

@Override
Expand Down
Expand Up @@ -12,7 +12,7 @@
class LanguageVersionTest extends AbstractLanguageVersionTest {

static Collection<TestDescriptor> data() {
return Arrays.asList(new TestDescriptor(ApexLanguageModule.NAME, ApexLanguageModule.TERSE_NAME, "35",
getLanguage("Apex").getVersion("35")));
return Arrays.asList(new TestDescriptor(ApexLanguageModule.NAME, ApexLanguageModule.TERSE_NAME, "57",
ApexLanguageModule.getInstance().getDefaultVersion()));
}
}
Expand Up @@ -11,6 +11,7 @@
import java.util.List;
import java.util.Properties;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import net.sourceforge.pmd.internal.util.PredicateUtil;

Expand All @@ -22,11 +23,23 @@ public abstract class AbstractLanguage implements Language {
private final List<String> extensions;

public AbstractLanguage(String name, String terseName, Tokenizer tokenizer, String... extensions) {
this(name, terseName, tokenizer, Arrays.asList(extensions));
}

protected AbstractLanguage(String name, String terseName, Tokenizer tokenizer, List<String> extensions) {
this.name = name;
this.terseName = terseName;
this.tokenizer = tokenizer;
this.fileFilter = PredicateUtil.toNormalizedFileFilter(PredicateUtil.getFileExtensionFilter(extensions).or(it -> Files.isDirectory(Paths.get(it))));
this.extensions = Arrays.asList(extensions);
List<String> extensionsWithDot = extensions.stream().map(e -> {
if (e.length() > 0 && e.charAt(0) != '.') {
return "." + e;
}
return e;
}).collect(Collectors.toList());
this.fileFilter = PredicateUtil.toNormalizedFileFilter(
PredicateUtil.getFileExtensionFilter(extensionsWithDot.toArray(new String[0]))
.or(it -> Files.isDirectory(Paths.get(it))));
this.extensions = extensionsWithDot;
}

@Override
Expand Down
Expand Up @@ -8,6 +8,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -21,7 +22,6 @@
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata.LangVersionMetadata;
import net.sourceforge.pmd.util.AssertionUtil;
import net.sourceforge.pmd.util.StringUtil;

Expand All @@ -44,7 +44,7 @@ public abstract class LanguageModuleBase implements Language {
* Construct a module instance using the given metadata. The metadata must
* be properly constructed.
*
* @throws IllegalStateException If the metadata is invalid (eg missing extensions or name)
* @throws IllegalStateException If the metadata is invalid (eg missing extensions or name or no versions)
*/
protected LanguageModuleBase(LanguageMetadata metadata) {
this.meta = metadata;
Expand All @@ -55,10 +55,7 @@ protected LanguageModuleBase(LanguageMetadata metadata) {
LanguageVersion defaultVersion = null;

if (metadata.versionMetadata.isEmpty()) {
// Many languages have just one version, which is implicitly
// created here.
// TODO #4120 remove this branch, before 7.0.0
metadata.versionMetadata.add(new LangVersionMetadata("", Collections.emptyList(), true));
throw new IllegalStateException("No versions for '" + getId() + "'");
}

int i = 0;
Expand Down Expand Up @@ -269,7 +266,7 @@ public LanguageMetadata shortName(@NonNull String shortName) {

/**
* Record the {@linkplain Language#getExtensions() extensions}
* assigned to the language. Parameters should not start with a period
* assigned to the language. Extensions should not start with a period
* {@code .}.
*
* @param e1 First extensions
Expand All @@ -283,14 +280,33 @@ public LanguageMetadata extensions(String e1, String... others) {
return this;
}

/**
* Record the {@linkplain Language#getExtensions() extensions}
* assigned to the language. Extensions should not start with a period
* {@code .}. At least one extension must be provided.
*
* @param extensions the extensions
*
* @throws NullPointerException If any extension is null
* @throws IllegalArgumentException If no extensions are provided
*/
public LanguageMetadata extensions(Collection<String> extensions) {
this.extensions = new ArrayList<>(new HashSet<>(extensions));
AssertionUtil.requireContainsNoNullValue("extensions", this.extensions);
if (this.extensions.isEmpty()) {
throw new IllegalArgumentException("At least one extension is required.");
}
return this;
}

/**
* Add a new version by its name.
*
* @param name Version name. Must contain no spaces.
* @param aliases Additional names that are mapped to this version. Must contain no spaces.
*
* @throws NullPointerException If any parameter is null
* @throws IllegalArgumentException If the name or aliases contain spaces
* @throws IllegalArgumentException If the name or aliases are empty or contain spaces
*/

public LanguageMetadata addVersion(String name, String... aliases) {
Expand All @@ -305,7 +321,7 @@ public LanguageMetadata addVersion(String name, String... aliases) {
* @param aliases Additional names that are mapped to this version. Must contain no spaces.
*
* @throws NullPointerException If any parameter is null
* @throws IllegalArgumentException If the name or aliases contain spaces
* @throws IllegalArgumentException If the name or aliases are empty or contain spaces
*/
public LanguageMetadata addDefaultVersion(String name, String... aliases) {
versionMetadata.add(new LangVersionMetadata(name, Arrays.asList(aliases), true));
Expand Down Expand Up @@ -351,7 +367,7 @@ private LangVersionMetadata(String name, List<String> aliases, boolean isDefault
}

private static void checkVersionName(String name) {
if (SPACE_PAT.matcher(name).find()) { // TODO #4120 also check that the name is non-empty
if (StringUtils.isBlank(name) || SPACE_PAT.matcher(name).find()) {
throw new IllegalArgumentException("Invalid version name: " + StringUtil.inSingleQuotes(name));
}
}
Expand Down
Expand Up @@ -31,7 +31,9 @@ public final class PlainTextLanguage extends SimpleLanguageModuleBase {
static final String TERSE_NAME = "text";

private PlainTextLanguage() {
super(LanguageMetadata.withId(TERSE_NAME).name("Plain text").extensions("plain-text-file-goo-extension"),
super(LanguageMetadata.withId(TERSE_NAME).name("Plain text")
.extensions("plain-text-file-goo-extension")
.addDefaultVersion("default"),
new TextLvh());
}

Expand Down
Expand Up @@ -28,6 +28,16 @@ void testInvalidId() {
assertInvalidId("C");
assertInvalidId("ab-c");
assertThrows(NullPointerException.class, () -> LanguageMetadata.withId(null));

Exception e = assertThrows(IllegalArgumentException.class, () -> LanguageMetadata.withId("dummy").addVersion(""),
"Empty versions should not be allowed.");
assertEquals("Invalid version name: ''", e.getMessage());
assertThrows(IllegalArgumentException.class, () -> LanguageMetadata.withId("dummy").addVersion(" "),
"Empty versions should not be allowed.");
assertThrows(IllegalArgumentException.class, () -> LanguageMetadata.withId("dummy").addVersion(null),
"Empty versions should not be allowed.");
assertThrows(IllegalArgumentException.class, () -> LanguageMetadata.withId("dummy").addVersion("1.0", ""),
"Empty versions should not be allowed.");
}

@Test
Expand All @@ -36,6 +46,13 @@ void testVersions() {
assertThat(lang.getDefaultVersion(), equalTo(lang.getVersion("abc")));
}

@Test
void testMissingVersions() {
Exception e = assertThrows(IllegalStateException.class, () -> makeLanguage(LanguageMetadata.withId("dumdum").name("Name").extensions("o")),
"Languages without versions should not be allowed.");
assertEquals("No versions for 'dumdum'", e.getMessage());
}

@Test
void testNoExtensions() {
Exception ex = assertThrows(IllegalStateException.class, () -> makeLanguage(LanguageMetadata.withId("dumdum").name("Name").addVersion("abc")));
Expand Down

0 comments on commit f2f8357

Please sign in to comment.