Skip to content

Commit

Permalink
Merge pull request #3447 from oowekyala:issue3446-apex-file-name
Browse files Browse the repository at this point in the history
[core] Add access to file name in XPath rules #3447
  • Loading branch information
adangel committed Aug 22, 2021
2 parents 5770785 + e9e51dd commit b2adc91
Show file tree
Hide file tree
Showing 26 changed files with 357 additions and 79 deletions.
16 changes: 16 additions & 0 deletions docs/_data/xpath_funs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ aliases:
- &needs_typenode "The context node must be a {% jdoc jast::TypeNode %}"

langs:
- name: "Any language"
ns: "pmd"
funs:
- name: fileName
returnType: "xs:string"
shortDescription: "Returns the current filename"
description: "Returns the current simple filename without path but including the extension.
This can be used to write rules that check filename naming conventions.
<p>This function is available since PMD 6.38.0.</p>"
notes: "The function can be called on any node."
examples:
- code: "//b[pmd:fileName() = 'Foo.xml']"
outcome: "Matches any `&lt;b&gt;` tags in files called `Foo.xml`."


- name: "Java"
ns: "pmd-java"
funs:
Expand Down
2 changes: 2 additions & 0 deletions docs/pages/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ This is a {{ site.pmd.release_type }} release.

### Fixed Issues

* core
* [#3446](https://github.com/pmd/pmd/issues/3446): \[core] Allow XPath rules to access the current file name
* java-bestpractices
* [#3403](https://github.com/pmd/pmd/issues/3403): \[java] MethodNamingConventions junit5TestPattern does not detect parameterized tests

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.junit.Test;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.internal.FileNameXPathFunction;

import apex.jorje.semantic.ast.compilation.Compilation;

Expand All @@ -40,6 +41,15 @@ public void understandsSimpleFile() {
assertEquals(4, methods.size());
}

@Test
public void fileName() {
String code = "class Outer { class Inner {}}";

ASTUserClass rootNode = (ASTUserClass) parse(code, "src/filename.cls");

assertEquals("filename.cls", rootNode.getUserMap().get(FileNameXPathFunction.FILE_NAME_KEY));
}

private String testCodeForLineNumbers =
"public class SimpleClass {\n" // line 1
+ " public void method1() {\n" // line 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ protected ApexNode<Compilation> parse(String code) {
return apex.parse(code);
}

protected ApexNode<? extends Compilation> parse(String code, String fileName) {
return apex.parse(code, null, fileName);
}

protected ApexNode<Compilation> parseResource(String code) {
return apex.parseResource(code);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public ApexTreeDumpTest() {
}

@Test
public void safeNavigationOperator() throws Exception {
public void safeNavigationOperator() {
doTest("SafeNavigationOperator");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.apex.rule;

import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSize;

import org.junit.Test;

import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.apex.ApexLanguageModule;
import net.sourceforge.pmd.lang.apex.ast.ApexParserTestBase;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;

/**
* @author daniels
*/
public class ApexXPathRuleTest extends ApexParserTestBase {

private XPathRule makeXPath(String expression) {
XPathRule rule = new XPathRule(XPathVersion.XPATH_2_0, expression);
rule.setLanguage(LanguageRegistry.getLanguage(ApexLanguageModule.NAME));
rule.setMessage("XPath Rule Failed");
return rule;
}


@Test
public void testFileNameInXpath() {
Report report = apex.executeRule(makeXPath("/UserClass[pmd:fileName() = 'Foo.cls']"),
"class Foo {}",
"src/Foo.cls");

assertSize(report, 1);
}

@Test
public void testBooleanExpressions() {
Report report = apex.executeRuleOnResource(makeXPath("//BooleanExpression[@Operator='&&']"),
"BooleanExpressions.cls");
assertSize(report, 1);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import net.sourceforge.pmd.benchmark.TimeTracker;
import net.sourceforge.pmd.benchmark.TimedOperation;
import net.sourceforge.pmd.benchmark.TimedOperationCategory;
import net.sourceforge.pmd.lang.AbstractParser;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
Expand Down Expand Up @@ -132,7 +133,7 @@ private void processSourceCodeWithoutCache(final Reader sourceCode, final RuleSe

private Node parse(RuleContext ctx, Reader sourceCode, Parser parser) {
try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.PARSER)) {
Node rootNode = parser.parse(String.valueOf(ctx.getSourceCodeFile()), sourceCode);
Node rootNode = AbstractParser.doParse(parser, String.valueOf(ctx.getSourceCodeFile()), sourceCode);
ctx.getReport().suppress(parser.getSuppressMap());
return rootNode;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RulesetsFactoryUtils;
import net.sourceforge.pmd.SourceCodeProcessor;
import net.sourceforge.pmd.lang.AbstractParser;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageFilenameFilter;
import net.sourceforge.pmd.lang.LanguageRegistry;
Expand Down Expand Up @@ -147,7 +148,7 @@ private static void parseStress(Parser parser, List<DataSource> dataSources, boo

for (DataSource ds : dataSources) {
try (DataSource dataSource = ds; InputStreamReader reader = new InputStreamReader(dataSource.getInputStream())) {
parser.parse(dataSource.getNiceFileName(false, null), reader);
AbstractParser.doParse(parser, dataSource.getNiceFileName(false, null), reader);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
package net.sourceforge.pmd.lang;

import java.io.Reader;
import java.nio.file.Paths;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.internal.FileNameXPathFunction;

/**
* This is a generic implementation of the Parser interface.
Expand Down Expand Up @@ -34,4 +38,13 @@ public TokenManager getTokenManager(String fileName, Reader source) {
}

protected abstract TokenManager createTokenManager(Reader source);

@Deprecated
public static Node doParse(Parser parser, String fileName, Reader source) {
Node rootNode = parser.parse(fileName, source);
// remove prefixed path segments.
String simpleFileName = Paths.get(fileName).getFileName().toString();
rootNode.getUserMap().set(FileNameXPathFunction.FILE_NAME_KEY, simpleFileName);
return rootNode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package net.sourceforge.pmd.lang.ast.xpath;

import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.xpath.internal.FileNameXPathFunction;

import net.sf.saxon.sxpath.IndependentContext;

Expand All @@ -15,11 +16,12 @@ public class DefaultASTXPathHandler extends AbstractASTXPathHandler {

@Override
public void initialize() {
// override if needed
FileNameXPathFunction.registerSelfInSimpleContext();
}

@Override
public void initialize(IndependentContext context) {
// override if needed
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.ast.xpath.internal;

import java.util.List;
import java.util.Objects;

import org.jaxen.Context;
import org.jaxen.FunctionCallException;
import org.jaxen.SimpleFunctionContext;
import org.jaxen.XPathFunctionContext;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.DataMap;
import net.sourceforge.pmd.util.DataMap.SimpleDataKey;

/**
* A function that returns the current file name.
*
* @author Clément Fournier
*/
public class FileNameXPathFunction implements org.jaxen.Function {

/**
* The name of the file, including its extension. This
* excludes any segments for containing directories.
*/
public static final SimpleDataKey<String> FILE_NAME_KEY = DataMap.simpleDataKey("pmd.fileName");

public static void registerSelfInSimpleContext() {
((SimpleFunctionContext) XPathFunctionContext.getInstance()).registerFunction(null, "fileName",
new FileNameXPathFunction("fileName"));
}

private final String name;

public FileNameXPathFunction(String name) {
this.name = name;
}

@Override
public Object call(Context context, List args) throws FunctionCallException {
if (!args.isEmpty()) {
throw new IllegalArgumentException(name + " function takes no arguments.");
}
Node n = (Node) context.getNodeSet().get(0);

return getFileName(n);
}

public static String getFileName(Node n) {
// todo pmd7: replace with Node.getRoot()
while (n.getParent() != null) {
n = n.getParent();
}
Objects.requireNonNull(n, "No root node in tree?");

String fileName = n.getUserMap().get(FILE_NAME_KEY);
return Objects.requireNonNull(fileName, "File name was not set");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
package net.sourceforge.pmd.lang.xpath;

import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.internal.FileNameXPathFunction;
import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode;

import net.sf.saxon.expr.XPathContext;


@InternalApi
Expand Down Expand Up @@ -37,4 +42,9 @@ public static boolean matches(String s, String pattern1, String pattern2, String
String pattern5, String pattern6) {
return MatchesFunction.matches(s, pattern1, pattern2, pattern3, pattern4, pattern5, pattern6);
}

public static String fileName(final XPathContext context) {
Node ctxNode = ((ElementNode) context.getContextItem()).getUnderlyingNode();
return FileNameXPathFunction.getFileName(ctxNode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.SourceCodeProcessor;
import net.sourceforge.pmd.lang.AbstractParser;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
Expand Down Expand Up @@ -199,7 +200,8 @@ private Node getCompilationUnit() {

static Node getCompilationUnit(LanguageVersionHandler languageVersionHandler, String code) {
Parser parser = languageVersionHandler.getParser(languageVersionHandler.getDefaultParserOptions());
Node node = parser.parse(null, new StringReader(code));
Node node = AbstractParser.doParse(parser, "no file name", new StringReader(code));

languageVersionHandler.getSymbolFacade().start(node);
languageVersionHandler.getTypeResolutionFacade(Designer.class.getClassLoader()).start(node);
return node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.commons.lang3.StringEscapeUtils;

import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.AbstractParser;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersionHandler;
Expand Down Expand Up @@ -174,7 +175,7 @@ private void run(TreeRenderer renderer) throws IOException {
Logger.getLogger(Attribute.class.getName()).setLevel(Level.OFF);

try (Reader reader = source) {
Node root = parser.parse(file, reader);
Node root = AbstractParser.doParse(parser, file, reader);
languageHandler.getQualifiedNameResolutionFacade(this.getClass().getClassLoader()).start(root);

renderer.renderSubtree(root, System.out);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public XPathHandler getXPathHandler() {
return new DefaultASTXPathHandler() {
@Override
public void initialize() {
super.initialize();
TypeOfFunction.registerSelfInSimpleContext();
GetCommentOnFunction.registerSelfInSimpleContext();
MetricFunction.registerSelfInSimpleContext();
Expand All @@ -71,6 +72,7 @@ public void initialize() {

@Override
public void initialize(IndependentContext context) {
super.initialize(context);
super.initialize(context, LanguageRegistry.getLanguage(JavaLanguageModule.NAME), JavaFunctions.class);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public class ParserCornersTest {
@Test
public void testInvalidUnicodeEscape() {
expect.expect(TokenMgrError.class); // previously Error
expect.expectMessage("Lexical error in file (no file name provided) at line 1, column 2. Encountered: Invalid unicode escape");
java.parse("\\u00k0");
expect.expectMessage("Lexical error in file x/filename.java at line 1, column 2. Encountered: Invalid unicode escape");
java.parse("\\u00k0", null, "x/filename.java");
}

/**
Expand Down

0 comments on commit b2adc91

Please sign in to comment.