diff --git a/.gitignore b/.gitignore index 5bf49d2cb2..3c33657fab 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ bin/ .classpath .checkstyle .pmd +.pmdruleset.xml .ruleset .settings/ *.iml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 594aa8b29e..f86d7d2287 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,10 +18,7 @@ First off, thanks for taking the time to contribute! ## Bug reports -We used to use Sourceforge for bug tracking, but we are in the process of moving to github issues. - -* Old bugs are still available at . -* Please report new bugs at . +We use the issue tracker on Github. Please report new bugs at . When filing a bug report, please provide as much information as possible, so that we can reproduce the issue: @@ -32,7 +29,7 @@ When filing a bug report, please provide as much information as possible, so tha ## Documentation -There is some documentation available under . Feel free to create a bug report if +There is some documentation available under . Feel free to create a bug report if documentation is missing, incomplete or outdated. See [Bug reports](#bug-reports). The documentation is generated as a Jekyll site, the source is available at: . You can find build instructions there. @@ -42,18 +39,18 @@ For more on contributing documentation check - * On [StackOverflow](https://stackoverflow.com/questions/tagged/pmd): Make sure, to tag your question with "pmd". +* Create a issue for your question at . + +* Ask your question on Gitter . + ## Code Style PMD uses [checkstyle](http://checkstyle.sourceforge.net/) to enforce a common code style. -See [pmd-checkstyle-config.xml](https://github.com/pmd/build-tools/blob/master/config/src/main/resources/net/sourceforge/pmd/pmd-checkstyle-config.xml) for the configuration and -[the eclipse configuration files](https://github.com/pmd/build-tools/tree/master/config/eclipse) that can +See [pmd-checkstyle-config.xml](https://github.com/pmd/build-tools/blob/master/src/main/resources/net/sourceforge/pmd/pmd-checkstyle-config.xml) for the configuration and +[the eclipse configuration files](https://github.com/pmd/build-tools/tree/master/eclipse) that can be imported into a fresh workspace. diff --git a/README.md b/README.md index e89fa6db57..dbfbd7d9ca 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,16 @@ Additionally it includes **CPD**, the copy-paste-detector. CPD finds duplicated C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica, Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift and Visualforce. -## Source and Documentation +## Support + +* How do I? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd). +* I got this error, why? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd). +* I got this error and I'm sure it's a bug -- file an [issue](https://github.com/pmd/pmd/issues). +* I have an idea/request/question -- file an [issue](https://github.com/pmd/pmd/issues). +* I have a quick question -- ask on our [Gitter chat](https://gitter.im/pmd/pmd). +* Where's your documentation? -- + +## Source Our latest source of PMD can be found on [GitHub](https://github.com/pmd/pmd). Fork us! @@ -26,6 +35,6 @@ The rule designer is developed over at [pmd/pmd-designer](https://github.com/pmd Please see [its README](https://github.com/pmd/pmd-designer#contributing) for developer documentation. -## News and Website +## Website -More information can be found on our [Website](https://pmd.github.io) and on [SourceForge](https://sourceforge.net/projects/pmd/). +More information can be found on our [Website](https://pmd.github.io). diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java index 008fbb01e9..f140d7ade1 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParser.java @@ -52,7 +52,7 @@ public class ApexParser { ApexRootNode treeRoot = (ApexRootNode) treeBuilder.build(astRoot); treeRoot.setNoPmdComments(treeBuilder.getSuppressMap()); return treeRoot; - } catch (IOException e) { + } catch (IOException | apex.jorje.services.exception.ParseException e) { throw new ParseException(e); } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java index 2f5b3c0817..5b62b786b4 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java @@ -24,6 +24,8 @@ import apex.jorje.semantic.compiler.sfdc.AccessEvaluator; import apex.jorje.semantic.compiler.sfdc.NoopCompilerProgressCallback; import apex.jorje.semantic.compiler.sfdc.QueryValidator; import apex.jorje.semantic.compiler.sfdc.SymbolProvider; +import apex.jorje.services.exception.CompilationException; +import apex.jorje.services.exception.ParseException; import com.google.common.collect.ImmutableList; /** @@ -42,14 +44,6 @@ public class CompilerService { /** * Configure a compiler with the default configurations: - * - * @param symbolProvider - * EmptySymbolProvider, doesn't provide any symbols that are not - * part of source. - * @param accessEvaluator - * TestAccessEvaluator, doesn't provide any validation. - * @param queryValidator - * TestQueryValidators.Noop, no validation of queries. */ CompilerService() { this(EmptySymbolProvider.get(), new TestAccessEvaluator(), new TestQueryValidators.Noop()); @@ -72,14 +66,18 @@ public class CompilerService { this.queryValidator = queryValidator; } + + /** @throws ParseException If the code is unparsable */ public ApexCompiler visitAstFromString(String source, AstVisitor visitor) { return visitAstsFromStrings(ImmutableList.of(source), visitor, CompilerStage.POST_TYPE_RESOLVE); } + /** @throws ParseException If the code is unparsable */ public ApexCompiler visitAstsFromStrings(List sources, AstVisitor visitor) { return visitAstsFromStrings(sources, visitor, CompilerStage.POST_TYPE_RESOLVE); } + /** @throws ParseException If the code is unparsable */ public ApexCompiler visitAstsFromStrings(List sources, AstVisitor visitor, CompilerStage compilerStage) { List sourceFiles = sources.stream().map(s -> SourceFile.builder().setBody(s).build()) @@ -92,13 +90,32 @@ public class CompilerService { ApexCompiler compiler = ApexCompiler.builder().setInput(compilationInput).build(); compiler.compile(compilerStage); callAdditionalPassVisitor(compiler); + throwParseErrorIfAny(compiler); return compiler; } + private void throwParseErrorIfAny(ApexCompiler compiler) { + // this ignores semantic errors + + ParseException parseError = null; + for (CompilationException error : compiler.getErrors()) { + if (error instanceof ParseException) { + if (parseError == null) { + parseError = (ParseException) error; + } else { + parseError.addSuppressed(error); + } + } + } + if (parseError != null) { + throw parseError; + } + } + private CompilationInput createCompilationInput(List sourceFiles, - AstVisitor visitor) { + AstVisitor visitor) { return new CompilationInput(sourceFiles, symbolProvider, accessEvaluator, queryValidator, visitor, - NoopCompilerProgressCallback.get()); + NoopCompilerProgressCallback.get()); } /** diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/AbstractNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/AbstractNamingConventionsRule.java index 53e6fa6894..fb21041ef7 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/AbstractNamingConventionsRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/AbstractNamingConventionsRule.java @@ -23,12 +23,7 @@ abstract class AbstractNamingConventionsRule extends AbstractApexRule { abstract String displayName(String name); protected void checkMatches(PropertyDescriptor propertyDescriptor, ApexNode node, Object data) { - Pattern regex = getProperty(propertyDescriptor); - String name = node.getImage(); - if (!regex.matcher(name).matches()) { - String displayName = displayName(propertyDescriptor.name()); - addViolation(data, node, new Object[] { displayName, name, regex.toString() }); - } + checkMatches(propertyDescriptor, getProperty(propertyDescriptor), node, data); } protected void checkMatches(PropertyDescriptor propertyDescriptor, Pattern overridePattern, ApexNode node, Object data) { diff --git a/pmd-apex/src/main/resources/category/apex/errorprone.xml b/pmd-apex/src/main/resources/category/apex/errorprone.xml index cd458821a1..884c0f31f0 100644 --- a/pmd-apex/src/main/resources/category/apex/errorprone.xml +++ b/pmd-apex/src/main/resources/category/apex/errorprone.xml @@ -72,7 +72,7 @@ Avoid directly accessing Trigger.old and Trigger.new as it can lead to a bug. Tr trigger AccountTrigger on Account (before insert, before update) { Account a = Trigger.new[0]; //Bad: Accessing the trigger array directly is not recommended. - foreach ( Account a : Trigger.new ){ + for ( Account a : Trigger.new ){ //Good: Iterate through the trigger.new array instead. } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpressionTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpressionTest.java index aa421fc83e..4bf940aaa2 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpressionTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpressionTest.java @@ -13,8 +13,8 @@ public class ASTSoqlExpressionTest extends ApexParserTestBase { @Test public void testQuery() { - ApexNode node = parse("class Foo { void test1() { Account acc = [SELECT 1 FROM Account]; } }"); + ApexNode node = parse("class Foo { void test1() { Account acc = [SELECT col FROM Account]; } }"); ASTSoqlExpression soqlExpression = node.getFirstDescendantOfType(ASTSoqlExpression.class); - Assert.assertEquals("SELECT 1 FROM Account", soqlExpression.getQuery()); + Assert.assertEquals("SELECT col FROM Account", soqlExpression.getQuery()); } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCompilerTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCompilerTest.java new file mode 100644 index 0000000000..320e82b234 --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCompilerTest.java @@ -0,0 +1,17 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.ParseException; + +public class ApexCompilerTest extends ApexParserTestBase { + + @Test(expected = ParseException.class) + public void compileShouldFail() { + apex.parse("public class Foo { private String myField = \"a\"; }"); + } +} diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java index 3f051e0a09..79ecc234c6 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java @@ -44,7 +44,7 @@ public class ApexParserTest extends ApexParserTestBase { private String testCodeForLineNumbers = "public class SimpleClass {\n" // line 1 + " public void method1() {\n" // line 2 - + " System.out.println(\"abc\");\n" // line 3 + + " System.out.println('abc');\n" // line 3 + " // this is a comment\n" // line 4 + " }\n" // line 5 + "}\n"; // line 6 diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java index 74f8e59c43..82966d4c34 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTestBase.java @@ -12,10 +12,10 @@ public class ApexParserTestBase { protected ApexNode parse(String code) { - return (ApexNode) apex.parse(code); + return apex.parse(code); } protected ApexNode parseResource(String code) { - return (ApexNode) apex.parseResource(code); + return apex.parseResource(code); } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParsingHelper.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParsingHelper.java index 95ad4b3bd4..6be2d26215 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParsingHelper.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParsingHelper.java @@ -8,16 +8,17 @@ import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionHandler; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.apex.multifile.ApexMultifileVisitorFacade; -import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; -public class ApexParsingHelper extends BaseParsingHelper { +import apex.jorje.semantic.ast.compilation.Compilation; + +public class ApexParsingHelper extends BaseParsingHelper> { public static final ApexParsingHelper DEFAULT = new ApexParsingHelper(Params.getDefaultProcess()); private ApexParsingHelper(Params p) { - super(ApexLanguageModule.NAME, RootNode.class, p); + super(ApexLanguageModule.NAME, (Class>) (Class) ApexRootNode.class, p); } @Override @@ -26,8 +27,8 @@ public class ApexParsingHelper extends BaseParsingHelper rootNode) { super.postProcessing(handler, lversion, rootNode); - new ApexMultifileVisitorFacade().initializeWith((ApexNode) rootNode); + new ApexMultifileVisitorFacade().initializeWith(rootNode); } } diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/StackOverflowClass.cls b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/StackOverflowClass.cls index 2896906ffe..44ba1f8643 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/StackOverflowClass.cls +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/StackOverflowClass.cls @@ -85,7 +85,7 @@ public class SomeClass { if(object1Map.get(object1.Id).Id != null && object1.Text != null){ Item post = new Item(); post.ParentId = object1Map.get(object1.Id).Id; - post.Body = "Something"; + post.Body = 'Something'; itemList.add(post); } @@ -99,7 +99,7 @@ public class SomeClass { if(object1.Text != null && object1.Id != null){ Item post = new Item(); post.ParentId = object1.Id; - post.Body = "Something"; + post.Body = 'Something'; itemList.add(post); } } diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/metrics/impl/xml/CycloTest.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/metrics/impl/xml/CycloTest.xml index 522f07545b..ca8ccfdca1 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/metrics/impl/xml/CycloTest.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/metrics/impl/xml/CycloTest.xml @@ -13,7 +13,7 @@ k++; } catch (IOException ioe) { ioe.printStackTrace(); - throw new Exception("surprise", ioe); + throw new Exception('surprise', ioe); } catch (Exception e) { // do nothing } @@ -22,7 +22,7 @@ int x = 0, y = 1, z = 2, t = 2; boolean a = false, b = true, c = false, d = true; - for (String s : list) { + for (String s : lst) { // list is a kw x++; } @@ -86,7 +86,7 @@ public Test() { if (a == 1) { if (b == 2) { - System.out.println("b"); + System.out.println('b'); } else if (b == 1) { } } else { diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml index 28f5acf78c..470fbdee69 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/AvoidGlobalModifier.xml @@ -42,8 +42,8 @@ global class Foo { 1 diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/DebugsShouldUseLoggingLevel.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/DebugsShouldUseLoggingLevel.xml index 431c9c2e47..1b79209ec3 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/DebugsShouldUseLoggingLevel.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/bestpractices/xml/DebugsShouldUseLoggingLevel.xml @@ -14,7 +14,7 @@ public class Foo { @isTest static void methodATest() { System.debug('I am a bad debug!'); // not good - System.debug(LoggingLevel.WARN, 'I am a good warning.') // good + System.debug(LoggingLevel.WARN, 'I am a good warning.'); // good } } ]]> @@ -44,7 +44,7 @@ public class Foo { public class Foo { @isTest static void methodATest() { - System.debug(LoggingLevel.DEBUG, 'I am a good debug.') // good + System.debug(LoggingLevel.DEBUG, 'I am a good debug.'); // good } } ]]> diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml index 8817e4f30b..b649a57ab2 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml @@ -9,7 +9,7 @@ class names should not start with lowercase character 1 @@ -17,7 +17,7 @@ public class foo {}; all is well 0 diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml index ea9db4a8d0..c8c901f389 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml @@ -34,18 +34,18 @@ public class Foo { } ]]> - + - #1343 MethodNamingConventions for overrided methods + #1343 MethodNamingConventions for overriden methods 0 - + + #25 Method rules should ignore Property Getter/Setter 0 @@ -84,7 +84,7 @@ public class Foo { 0 @@ -107,7 +107,7 @@ public class Foo { - \ No newline at end of file + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessiveParameterList.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessiveParameterList.xml index 7b3b14d252..2c3b6e3b48 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessiveParameterList.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessiveParameterList.xml @@ -41,7 +41,7 @@ public class SomeClass { * Comment */ public void doSomething() { - System.debug("hello world"); + System.debug('hello world'); } /** Field comment */ diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessivePublicCount.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessivePublicCount.xml index 91c1856bca..87fa58fd6d 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessivePublicCount.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/ExcessivePublicCount.xml @@ -106,7 +106,7 @@ public class SomeClass { * Comment */ public void doSomething() { - System.debug("hello world"); + System.debug('hello world'); } /** Field comment */ diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssConstructorCount.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssConstructorCount.xml index e0a4320647..a6c831fdf4 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssConstructorCount.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssConstructorCount.xml @@ -129,7 +129,7 @@ public class SomeClass { * Comment */ public void doSomething() { - System.debug("hello world"); + System.debug('hello world'); } /** Field comment */ diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssMethodCount.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssMethodCount.xml index 58e523add4..70ae35a88e 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssMethodCount.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssMethodCount.xml @@ -143,7 +143,7 @@ public class SomeClass { * Comment */ public void doSomething() { - System.debug("hello world"); + System.debug('hello world'); } /** Field comment */ diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssTypeCount.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssTypeCount.xml index 55692690aa..c2f2562084 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssTypeCount.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/NcssTypeCount.xml @@ -119,7 +119,7 @@ public class SomeClass { * Comment */ public void doSomething() { - System.debug("hello world"); + System.debug('hello world'); } /** Field comment */ diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/StdCyclomaticComplexity.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/StdCyclomaticComplexity.xml index ddaed915e2..fe56df1285 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/StdCyclomaticComplexity.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/StdCyclomaticComplexity.xml @@ -150,7 +150,7 @@ public class Test { public Test() { if (a == 1) { if (b == 2) { - System.out.println("b"); + System.out.println('b'); } else if (b == 1) { } } else { diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/TooManyFields.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/TooManyFields.xml index a830a6f1d1..d26b4d8ff0 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/TooManyFields.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/TooManyFields.xml @@ -63,33 +63,12 @@ public class Foo { Integer a5; Integer a6; public class Bar { - Integer a7; - Integer a8; - Integer a9; - Integer a10; - Integer a11; - Integer a12; - } -} - ]]> - - - - outer class, inner interface, both OK - 0 - @@ -119,34 +98,22 @@ public class Foo { Integer b16; } public class Bar2 { - Integer b1; - Integer b2; - Integer b3; - Integer b4; - Integer b5; - Integer b6; - Integer b7; - Integer b8; - Integer b9; - Integer b10; - Integer b11; - Integer b12; - Integer b13; - Integer b14; - Integer b15; - Integer b16; - } -} - ]]> - - - - anonymous class with a field - 0 - diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml index 49d7898859..99568b713f 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/AvoidDirectAccessTriggerMap.xml @@ -20,9 +20,9 @@ trigger AccountTrigger on Account (before insert, before update) { 0 @@ -40,5 +40,5 @@ class NotATrigger { ]]> - + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml index 2c7a4768b6..d6e9bf8b4d 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexCRUDViolation.xml @@ -607,7 +607,8 @@ public class Foo { } public void doChecks() { - if (!Contact.sObjectType.getDescribe().isCreateable() && !Contact.sObjectType.getDescribe().isUpdateable()) { + if (!Contact.sObjectType.getDescribe().isCreateable() + && !Contact.sObjectType.getDescribe().isUpdateable()) { throw new NoAccessException(); } } @@ -626,7 +627,7 @@ public class Foo { } public void doChecks() { - anotherLevelHere("yolo"); + anotherLevelHere('yolo'); } private void anotherLevelHere(String s) { diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSOQLInjection.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSOQLInjection.xml index 920ef3e1ba..b6a00c7a4f 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSOQLInjection.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSOQLInjection.xml @@ -274,11 +274,11 @@ public class Foo { Integer is safe in query 0 - res = Database.query('Select Id, Name From ' + String.escapeSingleQuotes(objName) + ' LIMIT ' + nLimit); + public void test1(String objName, String lim) { // "limit" is a kw + Integer nLimit = Integer.valueOf(lim); + List res = Database.query('Select Id, Name From ' + String.escapeSingleQuotes(objName) + ' LIMIT ' + nLimit); } } ]]> @@ -316,10 +316,10 @@ public class Foo { isn't 1 - res = Database.query('Select Id,Name From ' + (name == 'Account' ? name : 'Cases'); + List res = Database.query('Select Id,Name From ' + (name == 'Account' ? name : 'Cases')); } } ]]> diff --git a/pmd-core/.gitignore b/pmd-core/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/pmd-core/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java index 3a73dbe9aa..fe90838a0c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java @@ -984,6 +984,36 @@ public interface NodeStream extends Iterable<@NonNull T> { } + /** + * Filters the input stream down to those nodes that are instances + * of any of the specified classes. + * + * @param input Input node stream + * @param c1 First type to test + * @param rest Other types to test + * @param Type of the returned stream + * + * @return A filtered node stream + */ + @SafeVarargs // this method is static because of the generic varargs + @SuppressWarnings("unchecked") + static NodeStream filterIsAny(NodeStream input, Class c1, Class... rest) { + return (NodeStream) input.filter( + node -> { + if (c1.isInstance(node)) { + return true; + } + + for (Class aClass : rest) { + if (aClass.isInstance(node)) { + return true; + } + } + return false; + } + ); + } + /** * A specialization of {@link NodeStream} that allows configuring diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java index f1afd60351..e01e21317f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java @@ -138,6 +138,20 @@ public final class StringUtil { return col; } + /** + * Returns the substring following the last occurrence of the + * given character. If the character doesn't occur, returns + * the whole string. This contrasts with {@link StringUtils#substringAfterLast(String, String)}, + * which returns the empty string in that case. + * + * @param str String to cut + * @param c Delimiter + */ + public static String substringAfterLast(String str, int c) { + int i = str.lastIndexOf(c); + return i < 0 ? str : str.substring(i + 1); + } + /** * Formats a double to a percentage, keeping {@code numDecimal} decimal places. * diff --git a/pmd-java/.gitignore b/pmd-java/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/pmd-java/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/pmd-java/etc/grammar/Java.jjt b/pmd-java/etc/grammar/Java.jjt index a0139d4349..ffa88e5f4c 100644 --- a/pmd-java/etc/grammar/Java.jjt +++ b/pmd-java/etc/grammar/Java.jjt @@ -2282,47 +2282,32 @@ void RSIGNEDSHIFT() #void: /* Annotation syntax follows. */ -void Annotation() #void: +void Annotation(): {} { - LOOKAHEAD( "@" VoidName() "(" ( "=" | ")" )) - NormalAnnotation() -| - LOOKAHEAD( "@" VoidName() "(" ) - SingleMemberAnnotation() -| - MarkerAnnotation() + "@" jjtThis.name=VoidName() [ AnnotationMemberList() ] } -void AnnotationBase(Node n) #void: -{String name = null;} -{ - "@" name=VoidName() {n.setImage(name);} -} - -void NormalAnnotation(): +void AnnotationMemberList(): {} { - AnnotationBase(jjtThis) "(" [ MemberValuePairs() ] ")" + "(" + ( LOOKAHEAD( "=") + MemberValuePair() ( "," MemberValuePair() )* + | [ ShorthandAnnotationValue() ] + ) + ")" } -void MarkerAnnotation(): -{} +void ShorthandAnnotationValue() #MemberValuePair: { - AnnotationBase(jjtThis) + jjtThis.setImage("value"); + jjtThis.setShorthand(); +} +{ + MemberValue() } -void SingleMemberAnnotation(): -{} -{ - AnnotationBase(jjtThis) "(" MemberValue() ")" -} - -void MemberValuePairs() #void: -{} -{ - MemberValuePair() ( "," MemberValuePair() )* -} void MemberValuePair(): {} @@ -2334,11 +2319,8 @@ void MemberValue() #void: {} { Annotation() - | - MemberValueArrayInitializer() - | - // Constant expression - ConditionalExpression() + | MemberValueArrayInitializer() + | ConditionalExpression() // Constant expression } void MemberValueArrayInitializer(): @@ -2471,12 +2453,8 @@ String VoidName() #void: JavaccToken t; } { - t= - { - s.append(t.getImage()); - } - ( LOOKAHEAD(2) "." t= - {s.append('.').append(t.getImage());} + t= { s.append(t.getImage()); } + ( LOOKAHEAD(2) "." t= {s.append('.').append(t.getImage());} )* {return s.toString();} } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java index 20b8265829..129b30f871 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java @@ -4,38 +4,82 @@ package net.sourceforge.pmd.lang.java.ast; +import java.util.Iterator; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.util.StringUtil; + /** - * Represents an annotation. This node has three specific syntactic variants, - * represented by nodes that implement this interface. + * Represents an annotation. * *
  *
- * Annotation ::= {@linkplain ASTNormalAnnotation NormalAnnotation}
- *              | {@linkplain ASTSingleMemberAnnotation SingleMemberAnnotation}
- *              | {@linkplain ASTMarkerAnnotation MarkerAnnotation}
+ * Annotation ::= "@" Name {@link ASTAnnotationMemberList AnnotationMemberList}?
  *
  * 
*/ -public interface ASTAnnotation extends TypeNode, ASTMemberValue { +public final class ASTAnnotation extends AbstractJavaTypeNode implements TypeNode, ASTMemberValue, Iterable { + + String name; + + ASTAnnotation(int id) { + super(id); + } /** * Returns the name of the annotation as it is used, * eg {@code java.lang.Override} or {@code Override}. */ - default String getAnnotationName() { - return getImage(); + public String getAnnotationName() { + return name; } + @Override + @Deprecated + public String getImage() { + return name; + } /** * Returns the simple name of the annotation. */ - default String getSimpleName() { - String[] split = getImage().split("\\."); - return split[split.length - 1]; + public String getSimpleName() { + return StringUtil.substringAfterLast(getImage(), '.'); } -} + /** + * Returns the list of members, or null if there is none. + */ + public @Nullable ASTAnnotationMemberList getMemberList() { + return children().first(ASTAnnotationMemberList.class); + } + /** + * Returns the stream of explicit members for this annotation. + */ + public NodeStream getMembers() { + return children(ASTAnnotationMemberList.class).children(ASTMemberValuePair.class); + } + + + @Override + public Iterator iterator() { + return children(ASTMemberValuePair.class).iterator(); + } + + @Override + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + + @Override + public void jjtAccept(SideEffectingVisitor visitor, T data) { + visitor.visit(this, data); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMemberList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMemberList.java new file mode 100644 index 0000000000..2cedd5513c --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotationMemberList.java @@ -0,0 +1,56 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import java.util.Iterator; + +import net.sourceforge.pmd.lang.ast.NodeStream; + +/** + * Represents the list of {@link ASTMemberValuePair member-value pairs} + * in an {@link ASTAnnotation annotation}. + * + *
+ *
+ * AnnotationMemberList ::= "(" {@link ASTMemberValuePair MemberValuePair} ( "," {@link ASTMemberValuePair MemberValuePair} )* ")"
+ *                        | "(" {@link ASTMemberValuePair ValueShorthand} ")"
+ *                        | "(" ")"
+ *
+ * 
+ */ +public final class ASTAnnotationMemberList extends AbstractJavaNode implements Iterable { + + ASTAnnotationMemberList(int id) { + super(id); + } + + + @Override + public ASTAnnotation getParent() { + return (ASTAnnotation) super.getParent(); + } + + @Override + @SuppressWarnings("unchecked") + public NodeStream children() { + return (NodeStream) super.children(); + } + + @Override + public Object jjtAccept(JavaParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + + @Override + public void jjtAccept(SideEffectingVisitor visitor, T data) { + visitor.visit(this, data); + } + + @Override + public Iterator iterator() { + return children().iterator(); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java index 53c24f5eab..5cc9ad8c99 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnonymousClassDeclaration.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.ast; +import org.checkerframework.checker.nullness.qual.NonNull; + /** * An anonymous class declaration. This can occur in a {@linkplain ASTConstructorCall class instance creation * expression} @@ -24,6 +26,11 @@ public final class ASTAnonymousClassDeclaration extends AbstractAnyTypeDeclarati } + @Override + public @NonNull String getSimpleName() { + return ""; + } + @Override public boolean isFindBoundary() { return true; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java index 913f22231d..78a8c563ed 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java @@ -9,11 +9,13 @@ import static net.sourceforge.pmd.lang.java.ast.JModifier.ABSTRACT; import java.util.Collections; import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; /** @@ -29,11 +31,15 @@ public interface ASTAnyTypeDeclaration ASTTopLevelDeclaration, FinalizableNode { + @Override + @NonNull + JClassSymbol getSymbol(); + /** - * Returns the simple name of this type declaration. Returns null - * if this is an anonymous class declaration. + * Returns the simple name of this type declaration. Returns the + * empty string if this is an anonymous class declaration. */ - @Nullable + @NonNull default String getSimpleName() { return getImage(); } @@ -51,9 +57,57 @@ public interface ASTAnyTypeDeclaration * Returns the binary name of this type declaration. This * is like {@link Class#getName()}. */ - // @NotNull + @NonNull String getBinaryName(); + + /** + * Returns the canonical name of this class, if it exists. + * Otherwise returns null. This is like {@link Class#getCanonicalName()}. + * + *

A canonical name exists if all enclosing types have a + * canonical name, and this is neither a local class nor an + * anonymous class. For example: + * + *

{@code
+     * package p;
+     *
+     * public class A { // p.A
+     *     class M { // p.A.M
+     *         {
+     *             class Local { // null, local class
+     *                class M2 {} // null, member of a local class
+     *             }
+     *
+     *             new Local() { // null, anonymous class
+     *                class M2 {} // null, member of an anonymous class
+     *             };
+     *         }
+     *     }
+     *
+     * }
+     * }
+ * + * + * So non-local/anonymous classes declared + * somewhere in a local/anonymous class also have no loc + */ + @Nullable + default String getCanonicalName() { + if (isAnonymous() || isLocal()) { + return null; + } + + ASTAnyTypeDeclaration encl = getEnclosingType(); + if (encl == null) { + return getBinaryName(); // toplevel + } + + String enclCanon = encl.getCanonicalName(); + return enclCanon == null ? null : enclCanon + '.' + getSimpleName(); + } + + /** * Returns true if this is an abstract type. Interfaces and annotations * types are implicitly abstract. diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java index f9eb1adb38..4df9a2c670 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTArrayType.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.List; +import net.sourceforge.pmd.lang.ast.NodeStream; /** * Represents an array type. @@ -23,11 +23,8 @@ public final class ASTArrayType extends AbstractJavaTypeNode implements ASTRefer @Override - public List getDeclaredAnnotations() { - // an array type's annotations are on its dimensions - // any annotations found before the element type apply to the - // element type - return ((ASTArrayTypeDim) getDimensions().getLastChild()).getDeclaredAnnotations(); + public NodeStream getDeclaredAnnotations() { + return getDimensions().getLastChild().getDeclaredAnnotations(); } public ASTArrayDimensions getDimensions() { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java index 5f2d40440e..dd9d7bcc22 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.NonNull; +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; + /** * A constructor of a {@linkplain ASTConstructorDeclaration class} or * {@linkplain ASTEnumDeclaration enum} declaration. @@ -21,7 +23,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; * * */ -public final class ASTConstructorDeclaration extends AbstractMethodOrConstructorDeclaration { +public final class ASTConstructorDeclaration extends AbstractMethodOrConstructorDeclaration { ASTConstructorDeclaration(int id) { super(id); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java index 40fd575fbb..4f6d3966ce 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameter.java @@ -24,8 +24,9 @@ import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefin * * */ -public final class ASTFormalParameter extends AbstractJavaTypeNode +public final class ASTFormalParameter extends AbstractJavaNode implements FinalizableNode, + TypeNode, Annotatable, VariableIdOwner { @@ -41,6 +42,13 @@ public final class ASTFormalParameter extends AbstractJavaTypeNode } + /** + * Returns the list of formal parameters containing this param. + */ + public ASTFormalParameters getOwnerList() { + return (ASTFormalParameters) jjtGetParent(); + } + /** * Returns true if this node is a varargs parameter. Then, the type * node is an {@link ASTArrayType ArrayType}, and its last dimension @@ -89,33 +97,9 @@ public final class ASTFormalParameter extends AbstractJavaTypeNode } - /** - * Returns the type of this formal parameter. That type - * is exactly that of the variable declarator id, - * which means that the declarator id's type takes into - * account whether this parameter is varargs or not. - */ - @Override - public Class getType() { - return getVarId().getType(); - } - - @Override public JavaTypeDefinition getTypeDefinition() { return getVarId().getTypeDefinition(); } - - /** - * Noop, the type of this node is defined by the type - * of the declarator id. - */ - @InternalApi - @Deprecated - @Override - public void setTypeDefinition(JavaTypeDefinition type) { - // see javadoc - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMarkerAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMarkerAnnotation.java deleted file mode 100644 index c680680794..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMarkerAnnotation.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -/** - * Represents an annotation with no declared member, e.g. {@code @Override}. - * - *
- *
- * MarkerAnnotation ::= "@" Name
- *
- * 
- * - * @see ASTSingleMemberAnnotation - * @see ASTNormalAnnotation - */ -public final class ASTMarkerAnnotation extends AbstractJavaTypeNode implements ASTAnnotation { - - ASTMarkerAnnotation(int id) { - super(id); - } - - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java index 0ff2d9e33b..b7b78bdcbb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValue.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.ast; /** * Represents the value of a member of an annotation. * This can appear in a {@linkplain ASTMemberValuePair member-value pair}, - * in a {@linkplain ASTSingleMemberAnnotation single-member annotation}, * or in the {@linkplain ASTDefaultValue default clause} of an annotation * method. * diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java index a4afe7c074..3055da7a26 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePair.java @@ -5,38 +5,54 @@ package net.sourceforge.pmd.lang.java.ast; /** - * Represents a single member-value pair in a {@linkplain ASTNormalAnnotation NormalAnnotation}. + * Represents a single pair of member name to value in an annotation. + * This node also represents the shorthand syntax, see {@link #isShorthand()}. * *
  *
- * MemberValuePair ::=  <IDENTIFIER> "=" {@linkplain ASTMemberValue MemberValue}
+ * MemberValuePair ::= <IDENTIFIER> "=" {@linkplain ASTMemberValue MemberValue}
+ *
+ * ValueShorthand  ::= {@linkplain ASTMemberValue MemberValue}
  *
  * 
*/ public final class ASTMemberValuePair extends AbstractJavaNode { + + private boolean isShorthand; + ASTMemberValuePair(int id) { super(id); } /** * Returns the name of the member set by this pair. + * This returns {@code "value"} if this is a shorthand declaration. */ - public String getMemberName() { + public String getName() { return getImage(); } + /** + * Returns true if this is a shorthand for the {@code value} attribute. + * For example, {@code @A("v")} has exactly the same structure as + * {@code @A(value = "v")}, except this attribute returns true for + * the first one only. + */ + public boolean isShorthand() { + return isShorthand; + } /** * Returns the value of the member set by this pair. */ - public ASTMemberValue getMemberValue() { + public ASTMemberValue getValue() { return (ASTMemberValue) getChild(0); } @Override - public ASTNormalAnnotation getParent() { - return (ASTNormalAnnotation) super.getParent(); + public ASTAnnotationMemberList getParent() { + return (ASTAnnotationMemberList) super.getParent(); } @@ -50,4 +66,8 @@ public final class ASTMemberValuePair extends AbstractJavaNode { public void jjtAccept(SideEffectingVisitor visitor, T data) { visitor.visit(this, data); } + + void setShorthand() { + this.isShorthand = true; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePairs.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePairs.java deleted file mode 100644 index 146c7f9c81..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMemberValuePairs.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import java.util.Iterator; - - -/** - * Represents a list of member values in an {@linkplain ASTNormalAnnotation annotation}. - * - *
- *
- *  MemberValuePairs ::= {@linkplain ASTMemberValuePair MemberValuePair} ( "," {@linkplain ASTMemberValuePair MemberValuePair} )*
- *
- * 
- * - * @deprecated Removed from the tree, added no info - */ -@Deprecated -public final class ASTMemberValuePairs extends AbstractJavaNode implements Iterable { - - ASTMemberValuePairs(int id) { - super(id); - } - - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - - - @Override - public ASTMemberValuePair getChild(int index) { - return (ASTMemberValuePair) super.getChild(index); - } - - - @Override - public ASTNormalAnnotation getParent() { - return (ASTNormalAnnotation) super.getParent(); - } - - - @Override - public Iterator iterator() { - return children(ASTMemberValuePair.class).iterator(); - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java index 4be44d7e5a..6d6322e7af 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; /** @@ -35,7 +36,7 @@ import net.sourceforge.pmd.annotation.InternalApi; * * */ -public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDeclaration { +public final class ASTMethodDeclaration extends AbstractMethodOrConstructorDeclaration { @InternalApi diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java index 1d75b6357e..d9c1fc81b8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTMethodOrConstructorDeclaration.java @@ -10,6 +10,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.ast.SignedNode; import net.sourceforge.pmd.lang.java.multifile.signature.JavaOperationSignature; import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; +import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; /** @@ -34,6 +35,10 @@ public interface ASTMethodOrConstructorDeclaration TypeParamOwnerNode { + @Override + JExecutableSymbol getSymbol(); + + /** * Returns the name of the method, or the simple name of the declaring class for * a constructor declaration. This is consistent with the result of diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java index 9fc7ed08fb..c5f826ff26 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTModifierList.java @@ -16,6 +16,8 @@ import java.util.Collections; import java.util.EnumSet; import java.util.Set; +import net.sourceforge.pmd.lang.ast.NodeStream; + /** * List of modifiers of a declaration. * @@ -225,7 +227,31 @@ public final class ASTModifierList extends AbstractJavaNode { @Override public void visit(ASTAnonymousClassDeclaration node, Set effective) { - // TODO add static modifier in static context + JavaNode enclosing = NodeStream.filterIsAny(node.ancestors(), + ASTAnyTypeDeclaration.class, + ASTEnumConstant.class, + ASTAnyTypeBodyDeclaration.class) + .first(); + + assert enclosing != null && !(enclosing instanceof ASTAnyTypeDeclaration) + : "Weird position for an anonymous class " + enclosing; + + if (enclosing instanceof ASTEnumConstant) { + effective.add(STATIC); + } else { + JavaNode decl = ((ASTAnyTypeBodyDeclaration) enclosing).getDeclarationNode(); + if (decl instanceof AccessNode && ((AccessNode) decl).hasModifiers(STATIC) + || decl instanceof ASTInitializer && ((ASTInitializer) decl).isStatic()) { + effective.add(STATIC); + } + } + } + + @Override + public void visit(ASTConstructorDeclaration node, Set effective) { + if (node.getEnclosingType().isEnum()) { + effective.add(PRIVATE); + } } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNormalAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNormalAnnotation.java deleted file mode 100644 index 4ef7407b71..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTNormalAnnotation.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -import java.util.Iterator; - -/** - * Represents an annotation that with a parenthesized list - * of key-value pairs (possibly empty). - * - *
- *
- * NormalAnnotation ::=  "@" Name "(" ( {@linkplain ASTMemberValuePair MemberValuePair} ( "," {@linkplain ASTMemberValuePair MemberValuePair} )* )? ")"
- *
- * 
- * - * @see ASTSingleMemberAnnotation - * @see ASTMarkerAnnotation - */ -public final class ASTNormalAnnotation extends AbstractJavaTypeNode implements ASTAnnotation, Iterable { - ASTNormalAnnotation(int id) { - super(id); - } - - - @Override - public Iterator iterator() { - return children(ASTMemberValuePair.class).iterator(); - } - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSingleMemberAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSingleMemberAnnotation.java deleted file mode 100644 index 058a0bef0b..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSingleMemberAnnotation.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.ast; - -/** - * Represents an annotation using the shorthand syntax for the default member. - * - *
- *
- * SingleMemberAnnotation ::=  "@" Name "(" {@linkplain ASTMemberValue MemberValue} ")"
- *
- * 
- * - * @see ASTMarkerAnnotation - * @see ASTNormalAnnotation - */ -public final class ASTSingleMemberAnnotation extends AbstractJavaTypeNode implements ASTAnnotation { - ASTSingleMemberAnnotation(int id) { - super(id); - } - - @Override - public Object jjtAccept(JavaParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } - - - @Override - public void jjtAccept(SideEffectingVisitor visitor, T data) { - visitor.visit(this, data); - } - - - /** - * Returns the value of the default member - * set by this annotation. - */ - public ASTMemberValue getMemberValue() { - return (ASTMemberValue) getChild(0); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java index 05f84f456d..c3a5bc9f64 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTypeParameter.java @@ -5,10 +5,10 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.List; - import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; + /** * Represents a type parameter declaration of a method, constructor, class or interface declaration. * @@ -24,17 +24,12 @@ import org.checkerframework.checker.nullness.qual.Nullable; * * @see JLS */ -public final class ASTTypeParameter extends AbstractJavaTypeNode implements Annotatable { +public final class ASTTypeParameter extends AbstractTypedSymbolDeclarator implements Annotatable { ASTTypeParameter(int id) { super(id); } - @Override - public List getDeclaredAnnotations() { - return children(ASTAnnotation.class).toList(); - } - /** * Returns the name of the type variable introduced by this declaration. */ @@ -62,6 +57,13 @@ public final class ASTTypeParameter extends AbstractJavaTypeNode implements Anno return getFirstChildOfType(ASTType.class); } + /** + * Returns the node to which this type parameter belongs. + */ + public TypeParamOwnerNode getOwner() { + return (TypeParamOwnerNode) jjtGetParent().jjtGetParent(); + } + @Override public Object jjtAccept(JavaParserVisitor visitor, Object data) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java index e0c0f30439..ee21f2067f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorId.java @@ -10,6 +10,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; @@ -43,7 +44,7 @@ import net.sourceforge.pmd.lang.symboltable.NameOccurrence; * */ // @formatter:on -public final class ASTVariableDeclaratorId extends AbstractJavaTypeNode implements AccessNode { +public final class ASTVariableDeclaratorId extends AbstractTypedSymbolDeclarator implements AccessNode, SymbolDeclaratorNode { private VariableNameDeclaration nameDeclaration; @@ -58,7 +59,6 @@ public final class ASTVariableDeclaratorId extends AbstractJavaTypeNode implemen return visitor.visit(this, data); } - @Override public void jjtAccept(SideEffectingVisitor visitor, T data) { visitor.visit(this, data); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java index b63ce5d9fb..fe9d180a8d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractAnyTypeDeclaration.java @@ -5,13 +5,14 @@ package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; /** * Abstract class for type declarations nodes. */ -abstract class AbstractAnyTypeDeclaration extends AbstractJavaTypeNode implements ASTAnyTypeDeclaration, LeftRecursiveNode { +abstract class AbstractAnyTypeDeclaration extends AbstractTypedSymbolDeclarator implements ASTAnyTypeDeclaration, LeftRecursiveNode { private JavaTypeQualifiedName qualifiedName; @@ -44,6 +45,5 @@ abstract class AbstractAnyTypeDeclaration extends AbstractJavaTypeNode implement this.qualifiedName = qualifiedName; setTypeDefinition(JavaTypeDefinition.forClass(qualifiedName.getType())); } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaNode.java index 7cf89487eb..12e2732e95 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractJavaNode.java @@ -56,7 +56,11 @@ abstract class AbstractJavaNode extends AbstractJjtreeNode implements } @Override + @NonNull public JSymbolTable getSymbolTable() { + if (symbolTable == null) { + return getParent().getSymbolTable(); + } return symbolTable; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java index eb7176093a..ccd4debfee 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractMethodOrConstructorDeclaration.java @@ -6,10 +6,15 @@ package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.lang.java.multifile.signature.JavaOperationSignature; import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; +import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; -abstract class AbstractMethodOrConstructorDeclaration extends AbstractJavaNode implements ASTMethodOrConstructorDeclaration, LeftRecursiveNode, AccessNode { +abstract class AbstractMethodOrConstructorDeclaration + extends AbstractJavaNode + implements ASTMethodOrConstructorDeclaration, + LeftRecursiveNode { + private T symbol; private JavaOperationSignature signature; private JavaOperationQualifiedName qualifiedName; @@ -39,4 +44,13 @@ abstract class AbstractMethodOrConstructorDeclaration extends AbstractJavaNode i } + void setSymbol(T symbol) { + this.symbol = symbol; + } + + @Override + public T getSymbol() { + AbstractTypedSymbolDeclarator.assertSymbolNotNull(symbol, this); + return symbol; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractTypedSymbolDeclarator.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractTypedSymbolDeclarator.java new file mode 100644 index 0000000000..4d1f134e03 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AbstractTypedSymbolDeclarator.java @@ -0,0 +1,41 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; + + +/** + * Abstract class for type declarations nodes. + */ +abstract class AbstractTypedSymbolDeclarator + extends AbstractJavaTypeNode + implements SymbolDeclaratorNode { + + private T symbol; + + AbstractTypedSymbolDeclarator(int i) { + super(i); + } + + @NonNull + @Override + public T getSymbol() { + assertSymbolNotNull(symbol, this); + return symbol; + } + + static void assertSymbolNotNull(JElementSymbol symbol, SymbolDeclaratorNode node) { + assert symbol != null : "Symbol was null, not set by qualified name resolver, on " + node; + } + + void setSymbol(T symbol) { + this.symbol = symbol; + } + +} + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java index f5918859f0..a016ac8e97 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/AccessNode.java @@ -6,11 +6,12 @@ package net.sourceforge.pmd.lang.java.ast; import static net.sourceforge.pmd.lang.java.ast.JModifier.STRICTFP; -import java.util.List; import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; +import net.sourceforge.pmd.lang.ast.NodeStream; + /** * A node that owns a {@linkplain ASTModifierList modifier list}. * @@ -31,10 +32,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; */ public interface AccessNode extends Annotatable { - @Override - default List getDeclaredAnnotations() { - return getModifiers().children(ASTAnnotation.class).toList(); + default NodeStream getDeclaredAnnotations() { + return getModifiers().children(ASTAnnotation.class); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java index 12afe8904f..6a136e705a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/Annotatable.java @@ -4,11 +4,9 @@ package net.sourceforge.pmd.lang.java.ast; -import java.util.Collection; -import java.util.List; - -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.Predicate; +import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; /** @@ -24,54 +22,25 @@ public interface Annotatable extends JavaNode { /** * Returns all annotations present on this node. */ - default List getDeclaredAnnotations() { - return this.findChildrenOfType(ASTAnnotation.class); - } - - - /** - * Returns the annotation with the given qualified name if it is present, - * otherwise returns null. The argument should be a qualified name, though - * this method will find also usages of an annotation that use the simple - * name if it is in scope. - * - *

E.g. {@code getAnnotation("java.lang.Override")} will find both - * {@code @java.lang.Override} and {@code @Override}. - */ - @Nullable - default ASTAnnotation getAnnotation(String annotQualifiedName) { - // TODO use node streams - List annotations = getDeclaredAnnotations(); - for (ASTAnnotation annotation : annotations) { - if (TypeHelper.isA(annotation, annotQualifiedName)) { - return annotation; - } - } - return null; - } - - - /** - * Returns true if any annotation in the given collection is present, - * using {@link #isAnnotationPresent(String)}, otherwise false. - */ - default boolean isAnyAnnotationPresent(Collection annotQualifiedNames) { - // TODO use node streams - for (String annotQualifiedName : annotQualifiedNames) { - if (isAnnotationPresent(annotQualifiedName)) { - return true; - } - } - return false; + default NodeStream getDeclaredAnnotations() { + return children(ASTAnnotation.class); } /** * Returns true if an annotation with the given qualified name is - * applied to this node. In this case, {@link #getAnnotation(String)} - * will not return null. + * applied to this node. */ default boolean isAnnotationPresent(String annotQualifiedName) { - return getAnnotation(annotQualifiedName) != null; + return getDeclaredAnnotations().any(t -> TypeHelper.isA(t, annotQualifiedName)); + } + + + /** + * Returns true if an annotation with the given type is + * applied to this node. + */ + default boolean isAnnotationPresent(Class type) { + return getDeclaredAnnotations().any((Predicate) t -> TypeHelper.subclasses(t, type)); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java index 4676b62bbd..00463da788 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/InternalApiBridge.java @@ -7,6 +7,12 @@ package net.sourceforge.pmd.lang.java.ast; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.java.qname.JavaOperationQualifiedName; import net.sourceforge.pmd.lang.java.qname.JavaTypeQualifiedName; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; import net.sourceforge.pmd.lang.java.symbols.table.JSymbolTable; import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; import net.sourceforge.pmd.lang.symboltable.Scope; @@ -28,6 +34,20 @@ public final class InternalApiBridge { } + public static void setSymbol(SymbolDeclaratorNode node, JElementSymbol symbol) { + if (node instanceof ASTMethodDeclaration) { + ((ASTMethodDeclaration) node).setSymbol((JMethodSymbol) symbol); + } else if (node instanceof ASTConstructorDeclaration) { + ((ASTConstructorDeclaration) node).setSymbol((JConstructorSymbol) symbol); + } else if (node instanceof ASTAnyTypeDeclaration) { + ((AbstractAnyTypeDeclaration) node).setSymbol((JClassSymbol) symbol); + } else if (node instanceof ASTVariableDeclaratorId) { + ((ASTVariableDeclaratorId) node).setSymbol((JVariableSymbol) symbol); + } else if (node instanceof ASTTypeParameter) { + ((ASTTypeParameter) node).setSymbol((JTypeParameterSymbol) symbol); + } + } + public static void setSymbolTable(JavaNode node, JSymbolTable table) { ((AbstractJavaNode) node).setSymbolTable(table); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java index d6c5a8036e..649a96810e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JModifier.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.ast; import java.lang.reflect.Modifier; +import java.util.Collection; import java.util.Locale; /** @@ -65,4 +66,14 @@ public enum JModifier { public String toString() { return getToken(); } + + + public static int toReflect(Collection mods) { + int res = 0; + for (JModifier mod : mods) { + res |= mod.getReflectMod(); + } + return res; + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaNode.java index cb4096cfb0..1168530699 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaNode.java @@ -129,8 +129,7 @@ public interface JavaNode extends ScopedNode, TextAvailableNode { /** * Returns the symbol table for the program point represented by * this node. - * - * TODO */ + @NonNull JSymbolTable getSymbolTable(); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java index 8744e1a6c7..41d2f5ca9e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParserVisitorAdapter.java @@ -25,24 +25,6 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; public class JavaParserVisitorAdapter implements JavaParserVisitor { - public Object visit(ASTAnnotation node, Object data) { - return visit((JavaNode) node, data); - } - - @Override - public Object visit(ASTMarkerAnnotation node, Object data) { - return visit((ASTAnnotation) node, data); - } - - @Override - public Object visit(ASTSingleMemberAnnotation node, Object data) { - return visit((ASTAnnotation) node, data); - } - - @Override - public Object visit(ASTNormalAnnotation node, Object data) { - return visit((ASTAnnotation) node, data); - } public Object visit(ASTType node, Object data) { return visit((JavaNode) node, data); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java index 6af146c604..2b4970e7c9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SideEffectingVisitorAdapter.java @@ -15,28 +15,22 @@ package net.sourceforge.pmd.lang.java.ast; public class SideEffectingVisitorAdapter implements SideEffectingVisitor { - public void visit(ASTAnnotation node, T data) { + public void visit(ASTMethodOrConstructorDeclaration node, T data) { visit((JavaNode) node, data); } - + @Override - public void visit(ASTSingleMemberAnnotation node, T data) { - visit((ASTAnnotation) node, data); + public void visit(ASTMethodDeclaration node, T data) { + visit((ASTMethodOrConstructorDeclaration) node, data); } - + @Override - public void visit(ASTNormalAnnotation node, T data) { - visit((ASTAnnotation) node, data); - } - - @Override - public void visit(ASTMarkerAnnotation node, T data) { - visit((ASTAnnotation) node, data); + public void visit(ASTConstructorDeclaration node, T data) { + visit((ASTMethodOrConstructorDeclaration) node, data); } // TODO delegation - public void visit(ASTAnyTypeDeclaration node, T data) { visit((JavaNode) node, data); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SymbolDeclaratorNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SymbolDeclaratorNode.java new file mode 100644 index 0000000000..eb9bd01e7e --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/SymbolDeclaratorNode.java @@ -0,0 +1,17 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.ast; + +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; + +/** + * @author Clément Fournier + */ +public interface SymbolDeclaratorNode extends JavaNode { + + /** Returns the symbol this node declares. */ + JElementSymbol getSymbol(); + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TypeParamOwnerNode.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TypeParamOwnerNode.java index c4c7f86db5..d2e2ade35e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TypeParamOwnerNode.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/TypeParamOwnerNode.java @@ -6,10 +6,15 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; + /** * @author Clément Fournier */ -public interface TypeParamOwnerNode extends JavaNode { +public interface TypeParamOwnerNode extends SymbolDeclaratorNode { + + @Override + JTypeParameterOwnerSymbol getSymbol(); /** * Returns the type parameter declaration of this node, or null if diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaProcessingStage.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaProcessingStage.java index d26082bb69..980cd1a89a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaProcessingStage.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaProcessingStage.java @@ -23,17 +23,16 @@ import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.JavaParser; import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker; -import net.sourceforge.pmd.lang.java.dfa.DataFlowFacade; import net.sourceforge.pmd.lang.java.multifile.MultifileVisitorFacade; import net.sourceforge.pmd.lang.java.qname.QualifiedNameResolver; import net.sourceforge.pmd.lang.java.symbols.SymbolResolver; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.ast.AstSymFactory; import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ClasspathSymbolResolver; import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectionSymFactory; import net.sourceforge.pmd.lang.java.symbols.table.internal.SemanticChecksLogger; import net.sourceforge.pmd.lang.java.symbols.table.internal.SymbolTableResolver; import net.sourceforge.pmd.lang.java.symboltable.SymbolFacade; import net.sourceforge.pmd.lang.java.typeresolution.PMDASMClassLoader; -import net.sourceforge.pmd.lang.java.typeresolution.TypeResolutionFacade; /** @@ -55,7 +54,7 @@ public enum JavaProcessingStage implements AstProcessingStage new QualifiedNameResolver().initializeWith(classLoader, acu)); + () -> new QualifiedNameResolver(astSymFactory, configuration.getTypeResolutionClassLoader()).traverse(acu)); SymbolResolver symResolver = new ClasspathSymbolResolver(classLoader, new ReflectionSymFactory()); @@ -93,6 +93,7 @@ public enum JavaProcessingStage implements AstProcessingStageIn fact, populates symbols on declaration nodes. + * TODO in the near future we'll get rid of qualified names, and can + * reuse this class just to build symbols (moving it to symbols.impl.ast). + * * @author Clément Fournier * @since 6.1.0 * @deprecated Is internal API @@ -65,6 +77,9 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { private final Stack innermostEnclosingTypeName = new Stack<>(); + private final Deque enclosingSymbols = new ArrayDeque<>(); + private final AstSymFactory symFactory; + /** * Package list of the current file. * The qualified names of classes and methods declared @@ -102,16 +117,16 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { */ private ClassLoader classLoader; + public QualifiedNameResolver(AstSymFactory symFactory, ClassLoader classLoader) { + this.symFactory = symFactory; + this.classLoader = classLoader; + } + /** - * Initialises the visitor and starts it. - * - * @param classLoader The classloader that will be used by type qualified names - * to load their type. - * @param rootNode The root hierarchy + * Traverse the compilation unit. */ - public void initializeWith(ClassLoader classLoader, ASTCompilationUnit rootNode) { - this.classLoader = PMDASMClassLoader.getInstance(classLoader); - rootNode.jjtAccept(this, null); + public void traverse(ASTCompilationUnit root) { + root.jjtAccept(this, null); } @@ -193,6 +208,23 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { return getLongestPackagePrefix(acc.substring(0, acc.lastIndexOf('.')), i - 1); } + @Override + public Object visit(ASTVariableDeclaratorId node, Object data) { + + if (isTrueLocalVar(node)) { + symFactory.setLocalVarSymbol(node); + } else { + // in the other cases, building the method/ctor/class symbols already set the symbols + assert node.getSymbol() != null : "Symbol was null for " + node; + } + + return super.visit(node, data); + } + + private boolean isTrueLocalVar(ASTVariableDeclaratorId node) { + return !(node.isField() || node.isEnumConstant() || node.getParent() instanceof ASTFormalParameter); + } + @Override public Object visit(ASTAnyTypeDeclaration node, Object data) { @@ -203,26 +235,13 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { updateClassContext(node.getSimpleName(), localIndex); - InternalApiBridge.setQname(node, contextClassQName()); - - super.visit(node, data); - - // go back to previous context - rollbackClassContext(); - - return data; + return recurseOnClass(node); } - @Override public Object visit(ASTAnonymousClassDeclaration node, Object data) { - updateContextForAnonymousClass(); - InternalApiBridge.setQname(node, contextClassQName()); - super.visit(node, data); - rollbackClassContext(); - - return data; + return recurseOnClass(node); } @@ -230,7 +249,7 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { public Object visit(ASTMethodDeclaration node, Object data) { String opname = getOperationName(node.getName(), node.getFirstDescendantOfType(ASTFormalParameters.class)); InternalApiBridge.setQname(node, contextOperationQName(opname, false)); - return super.visit(node, data); + return recurseOnExecutable(node); } @@ -238,7 +257,31 @@ public class QualifiedNameResolver extends JavaParserVisitorAdapter { public Object visit(ASTConstructorDeclaration node, Object data) { String opname = getOperationName(classNames.head(), node.getFirstDescendantOfType(ASTFormalParameters.class)); InternalApiBridge.setQname(node, contextOperationQName(opname, false)); - return super.visit(node, data); + return recurseOnExecutable(node); + } + + + public Object recurseOnExecutable(ASTMethodOrConstructorDeclaration node) { + JExecutableSymbol sym = node.getSymbol(); + enclosingSymbols.addLast(sym); + + super.visit(node, null); + + enclosingSymbols.removeLast(); + return null; + } + + public Object recurseOnClass(ASTAnyTypeDeclaration node) { + InternalApiBridge.setQname(node, contextClassQName()); + + JClassSymbol sym = symFactory.setClassSymbol(enclosingSymbols.peekLast(), node); + enclosingSymbols.addLast(sym); + + super.visit(node, null); + + rollbackClassContext(); + enclosingSymbols.removeLast(); + return null; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java index a6df69870f..81d3335341 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractIgnoredAnnotationRule.java @@ -38,6 +38,6 @@ public abstract class AbstractIgnoredAnnotationRule extends AbstractJavaRule { * @return true if the annotation has been found, otherwise false */ protected boolean hasIgnoredAnnotation(Annotatable node) { - return node.isAnyAnnotationPresent(getProperty(ignoredAnnotationsDescriptor)); + return getProperty(ignoredAnnotationsDescriptor).stream().anyMatch(node::isAnnotationPresent); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java index a62b725ef9..63ff42aaaf 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJavaRule.java @@ -16,7 +16,6 @@ import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTAndExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTArguments; import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; @@ -107,9 +106,6 @@ public abstract class AbstractJavaRule extends AbstractRule implements JavaParse // FIXME those are not in sync with JavaParserVisitorAdapter // See #1786 - public Object visit(ASTAnnotation node, Object data) { - return JavaParserVisitor.super.visit(node, data); - } public Object visit(ASTExpression node, Object data) { return JavaParserVisitor.super.visit(node, data); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java index f6883b3196..2ba13ebc45 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractLombokAwareRule.java @@ -136,6 +136,6 @@ public class AbstractLombokAwareRule extends AbstractIgnoredAnnotationRule { * @return true if a lombok annotation has been found */ protected boolean hasLombokAnnotation(Annotatable node) { - return node.isAnyAnnotationPresent(LOMBOK_ANNOTATIONS); + return LOMBOK_ANNOTATIONS.stream().anyMatch(node::isAnnotationPresent); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 2952d14069..b0a92688a6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -9,13 +9,11 @@ import java.util.List; import java.util.Map; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTMemberValuePair; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTNormalAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; @@ -41,7 +39,7 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { @Override public Object visit(ASTMethodDeclaration method, Object data) { if (isJUnitMethod(method, data)) { - if (!isExpectAnnotated(method.getParent())) { + if (!isExpectAnnotated(method)) { Map variables = getVariables(method); Scope classScope = method.getScope().getParent(); @@ -98,10 +96,9 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { for (Map.Entry> entry : decls.entrySet()) { Node parent = entry.getKey().getNode().getParent().getParent().getParent(); - if (parent.hasDescendantOfType(ASTMarkerAnnotation.class) - && parent.getFirstChildOfType(ASTFieldDeclaration.class) != null) { - String annot = parent.getFirstDescendantOfType(ASTMarkerAnnotation.class).getChild(0).getImage(); - if (!"Rule".equals(annot) && !"org.junit.Rule".equals(annot)) { + if (parent.getFirstChildOfType(ASTFieldDeclaration.class) != null) { + ASTAnnotation annot = parent.getFirstDescendantOfType(ASTAnnotation.class); + if (annot == null || !TypeHelper.isA(annot, "org.junit.Rule")) { continue; } @@ -118,20 +115,12 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { /** * Tells if the node contains a Test annotation with an expected exception. */ - private boolean isExpectAnnotated(Node methodParent) { - List annotations = methodParent.findDescendantsOfType(ASTNormalAnnotation.class); - for (ASTNormalAnnotation annotation : annotations) { - ASTName name = annotation.getFirstChildOfType(ASTName.class); - if (name != null && TypeHelper.isA(name, JUNIT4_CLASS_NAME)) { - List memberValues = annotation.findDescendantsOfType(ASTMemberValuePair.class); - for (ASTMemberValuePair pair : memberValues) { - if ("expected".equals(pair.getImage())) { - return true; - } - } - } - } - return false; + private boolean isExpectAnnotated(ASTMethodDeclaration method) { + return method.getDeclaredAnnotations() + .filter(it -> TypeHelper.isA(it, JUNIT4_CLASS_NAME)) + .flatMap(ASTAnnotation::getMembers) + .any(it -> "expected".equals(it.getName())); + } /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java index 38eb211aa9..f85eecc207 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LooseCouplingRule.java @@ -5,14 +5,12 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTResultType; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.util.CollectionUtil; @@ -44,17 +42,8 @@ public class LooseCouplingRule extends AbstractJavaRule { return data; } - private boolean methodHasOverride(Node node) { - ASTClassOrInterfaceBodyDeclaration method = node.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class); - if (method != null && method.getNumChildren() > 0 && method.getChild(0) instanceof ASTAnnotation) { - ASTMarkerAnnotation marker = method.getFirstDescendantOfType(ASTMarkerAnnotation.class); - if (marker != null && marker.getFirstChildOfType(ASTName.class) != null) { - ASTName name = marker.getFirstChildOfType(ASTName.class); - if (name.getType() == Override.class) { - return true; - } - } - } - return false; + private boolean methodHasOverride(JavaNode node) { + ASTMethodDeclaration method = node.ancestors(ASTMethodDeclaration.class).first(); + return method != null && method.isAnnotationPresent(Override.class); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java index 9076e635de..1b00d0f84d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java @@ -18,7 +18,6 @@ import java.util.Stack; import java.util.logging.Logger; import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; @@ -213,11 +212,9 @@ public class MissingOverrideRule extends AbstractJavaRule { return super.visit(node, data); } - for (ASTAnnotation annot : node.getDeclaredAnnotations()) { - if (Override.class.equals(annot.getType())) { - // we assume the compiler has already checked it, so it's correct - return super.visit(node, data); - } + if (node.isAnnotationPresent(Override.class)) { + // we assume the compiler has already checked it, so it's correct + return super.visit(node, data); } try { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java index 0111277c61..a04cfbfbf9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedFormalParameterRule.java @@ -18,10 +18,8 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTThrowsList; import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; @@ -125,17 +123,6 @@ public class UnusedFormalParameterRule extends AbstractJavaRule { } private boolean hasOverrideAnnotation(ASTMethodDeclaration node) { - int childIndex = node.getIndexInParent(); - for (int i = 0; i < childIndex; i++) { - Node previousSibling = node.getParent().getChild(i); - List annotations = previousSibling.findDescendantsOfType(ASTMarkerAnnotation.class); - for (ASTMarkerAnnotation annotation : annotations) { - ASTName name = annotation.getFirstChildOfType(ASTName.class); - if (name != null && (name.hasImageEqualTo("Override") || name.hasImageEqualTo("java.lang.Override"))) { - return true; - } - } - } - return false; + return node.isAnnotationPresent(Override.class); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java index b805a0cabc..0fab28c792 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java @@ -73,7 +73,7 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule memberValuePairs = annotation.findDescendantsOfType(ASTMemberValuePair.class); - - for (ASTMemberValuePair memberValuePair : memberValuePairs) { - // to set the access level of a constructor in lombok, you set the access property on the annotation - if ("access".equals(memberValuePair.getImage())) { - List names = memberValuePair.findDescendantsOfType(ASTName.class); - - for (ASTName name : names) { - // check to see if the value of the member value pair ends PRIVATE. This is from the AccessLevel enum in Lombok - if (name.getImage().endsWith("PRIVATE")) { - // if the constructor is found and the accesslevel is private no need to check anything else - return true; - } - } - } - } - } - - return false; + return parent.getDeclaredAnnotations() + .filter(t -> TypeHelper.isA(t, "lombok.NoArgsConstructor")) + .flatMap(ASTAnnotation::getMembers) + // to set the access level of a constructor in lombok, you set the access property on the annotation + .filterMatching(ASTMemberValuePair::getName, "access") + .map(ASTMemberValuePair::getValue) + // This is from the AccessLevel enum in Lombok + // if the constructor is found and the accesslevel is private no need to check anything else + .any(it -> it.getImage().equals("PRIVATE")); } private Node skipAnnotations(Node p) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java index fd5b262987..fab4558260 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UselessOverridingMethodRule.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArguments; import net.sourceforge.pmd.lang.java.ast.ASTBlock; @@ -19,7 +18,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; import net.sourceforge.pmd.lang.java.ast.ASTName; @@ -31,6 +29,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTResultType; import net.sourceforge.pmd.lang.java.ast.ASTStatement; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -175,20 +174,8 @@ public class UselessOverridingMethodRule extends AbstractJavaRule { return super.visit(node, data); } - if (!ignoreAnnotations) { - ASTClassOrInterfaceBodyDeclaration parent = (ASTClassOrInterfaceBodyDeclaration) node.getParent(); - for (int i = 0; i < parent.getNumChildren(); i++) { - Node n = parent.getChild(i); - if (n instanceof ASTAnnotation) { - if (n.getChild(0) instanceof ASTMarkerAnnotation) { - // @Override is ignored - if ("Override".equals(((ASTName) n.getChild(0).getChild(0)).getImage())) { - continue; - } - } - return super.visit(node, data); - } - } + if (!ignoreAnnotations && node.getDeclaredAnnotations().any(it -> !TypeHelper.isExactlyA(it, Override.class.getName()))) { + return super.visit(node, data); } if (arguments.getNumChildren() == 0) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java index 60dcafd286..f9af7da0ef 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java @@ -19,9 +19,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.multifile.signature.JavaOperationSignature; @@ -178,13 +176,7 @@ public class CommentRequiredRule extends AbstractCommentRule { private boolean isAnnotatedOverride(ASTMethodDeclaration decl) { - List annotations = decl.getParent().findDescendantsOfType(ASTMarkerAnnotation.class); - for (ASTMarkerAnnotation ann : annotations) { // TODO consider making a method to get the annotations of a method - if (ann.getFirstChildOfType(ASTName.class).getImage().equals("Override")) { - return true; - } - } - return false; + return decl.isAnnotationPresent(Override.class); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java index eb210b9f8b..30ebdd4b95 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AnnotationSuppressionUtil.java @@ -100,12 +100,7 @@ final class AnnotationSuppressionUtil { } private static boolean hasSuppressWarningsAnnotationFor(final Annotatable node, Rule rule) { - for (ASTAnnotation a : node.getDeclaredAnnotations()) { - if (annotationSuppresses(a, rule)) { - return true; - } - } - return false; + return node.getDeclaredAnnotations().any(it -> annotationSuppresses(it, rule)); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java index f49b1767b7..7b408cc08d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java @@ -218,4 +218,9 @@ public interface JClassSymbol extends JTypeDeclSymbol, return !isInterface() && !isArray() && !isPrimitive(); } + + @Override + default R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitClass(this, param); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JConstructorSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JConstructorSymbol.java index 60fc77dc6b..7f8037e582 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JConstructorSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JConstructorSymbol.java @@ -24,4 +24,11 @@ public interface JConstructorSymbol extends JExecutableSymbol, BoundToNode R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitCtor(this, param); + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JElementSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JElementSymbol.java index 53745b7c08..1465417183 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JElementSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JElementSymbol.java @@ -67,7 +67,7 @@ public interface JElementSymbol { * * @param o Comparand * - * @return True if the other is a symbol of the same type and + * @return True if the other is a symbol for the same program element */ @Override boolean equals(Object o); @@ -80,4 +80,11 @@ public interface JElementSymbol { // We should be able to create a type definition from a java.lang.reflect.Type, // paying attention to type variables of enclosing methods and types. // We should also be able to do so from an ASTType, with support from a JSymbolTable. + + + /** + * Dispatch to the appropriate visit method of the visitor and returns its result. + */ + R acceptVisitor(SymbolVisitor visitor, P param); + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java index 7b1955a0d7..40aa12b0c3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java @@ -37,4 +37,9 @@ public interface JFieldSymbol extends JAccessibleElementSymbol, JVariableSymbol return getEnclosingClass().getPackageName(); } + + @Override + default R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitField(this, param); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFormalParamSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFormalParamSymbol.java index 8db15905fe..93bddc3661 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFormalParamSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFormalParamSymbol.java @@ -15,4 +15,9 @@ public interface JFormalParamSymbol extends JLocalVariableSymbol { /** Returns the symbol declaring this parameter. */ JExecutableSymbol getDeclaringSymbol(); + + @Override + default R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitFormal(this, param); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JLocalVariableSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JLocalVariableSymbol.java index 3d4f1d9992..c6de6d327b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JLocalVariableSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JLocalVariableSymbol.java @@ -18,4 +18,10 @@ public interface JLocalVariableSymbol extends JVariableSymbol { // todo maybe add isParameter, isLocalVariable, isCatchParameter, etc. + + + @Override + default R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitLocal(this, param); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JMethodSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JMethodSymbol.java index 1be7deca03..598f20bcb3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JMethodSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JMethodSymbol.java @@ -25,4 +25,10 @@ public interface JMethodSymbol extends JExecutableSymbol, BoundToNode R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitMethod(this, param); + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterSymbol.java index 55d0df12ed..8ff630bc28 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterSymbol.java @@ -8,7 +8,6 @@ package net.sourceforge.pmd.lang.java.symbols; import java.lang.reflect.Modifier; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter; @@ -42,11 +41,16 @@ public interface JTypeParameterSymbol extends JTypeDeclSymbol, BoundToNode R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitTypeParam(this, param); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolVisitor.java new file mode 100644 index 0000000000..5922392d4a --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolVisitor.java @@ -0,0 +1,69 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols; + +/** + * Visitor over symbols. + */ +public interface SymbolVisitor { + + + R visitSymbol(JElementSymbol sym, P p); + + default R visitTypeDecl(JTypeDeclSymbol sym, P param) { + return visitSymbol(sym, param); + } + + /** Delegates to {@link #visitTypeDecl(JTypeDeclSymbol, Object) visitTypeDecl}. */ + default R visitClass(JClassSymbol sym, P param) { + return visitTypeDecl(sym, param); + } + + /** Delegates to {@link #visitClass(JClassSymbol, Object) visitClass}. */ + default R visitArray(JClassSymbol sym, JTypeDeclSymbol component, P param) { + return visitClass(sym, param); + } + + /** Delegates to {@link #visitTypeDecl(JTypeDeclSymbol, Object) visitTypeDecl}. */ + default R visitTypeParam(JTypeParameterSymbol sym, P param) { + return visitTypeDecl(sym, param); + } + + + default R visitExecutable(JExecutableSymbol sym, P param) { + return visitSymbol(sym, param); + } + + /** Delegates to {@link #visitExecutable(JExecutableSymbol, Object) visitExecutable}. */ + default R visitCtor(JConstructorSymbol sym, P param) { + return visitExecutable(sym, param); + } + + /** Delegates to {@link #visitExecutable(JExecutableSymbol, Object) visitExecutable}. */ + default R visitMethod(JMethodSymbol sym, P param) { + return visitExecutable(sym, param); + } + + + default R visitVariable(JVariableSymbol sym, P param) { + return visitSymbol(sym, param); + } + + /** Delegates to {@link #visitVariable(JVariableSymbol, Object) visitVariable}. */ + default R visitField(JFieldSymbol sym, P param) { + return visitVariable(sym, param); + } + + /** Delegates to {@link #visitVariable(JVariableSymbol, Object) visitVariable}. */ + default R visitLocal(JLocalVariableSymbol sym, P param) { + return visitVariable(sym, param); + } + + /** Delegates to {@link #visitLocal(JLocalVariableSymbol, Object) visitLocal}. */ + default R visitFormal(JFormalParamSymbol sym, P param) { + return visitLocal(sym, param); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ArraySymbolImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ArraySymbolImpl.java index aa1fe51d1e..e39f8b9553 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ArraySymbolImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ArraySymbolImpl.java @@ -17,10 +17,11 @@ import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; -import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; +import net.sourceforge.pmd.lang.java.symbols.SymbolVisitor; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals; /** * Generic implementation for array symbols, which does not rely on @@ -83,22 +84,22 @@ class ArraySymbolImpl implements JClassSymbol { @Override public List getDeclaredMethods() { - return Collections.singletonList(new ArrayCloneMethod(this)); + return Collections.singletonList(ImplicitMemberSymbols.arrayClone(this)); } @Override public List getDeclaredFields() { - return Collections.singletonList(new ArrayLengthField(this)); + return Collections.singletonList(ImplicitMemberSymbols.arrayLengthField(this)); } @Override public @Nullable JClassSymbol getSuperclass() { - return SymbolFactory.OBJECT_SYM; + return ReflectSymInternals.OBJECT_SYM; } @Override public List getSuperInterfaces() { - return SymbolFactory.ARRAY_SUPER_INTERFACES; + return ReflectSymInternals.ARRAY_SUPER_INTERFACES; } @Override @@ -106,22 +107,19 @@ class ArraySymbolImpl implements JClassSymbol { return component; } + @Override + public R acceptVisitor(SymbolVisitor visitor, P param) { + return visitor.visitArray(this, component, param); + } @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ArraySymbolImpl that = (ArraySymbolImpl) o; - return Objects.equals(component, that.component); + return SymbolEquality.equals(this, o); } @Override public int hashCode() { - return Objects.hash(component); + return SymbolEquality.hash(this); } @Override @@ -131,7 +129,7 @@ class ArraySymbolImpl implements JClassSymbol { @Override public List getConstructors() { - return Collections.singletonList(new ArrayConstructor(this)); + return Collections.singletonList(ImplicitMemberSymbols.arrayConstructor(this)); } @Override @@ -200,187 +198,7 @@ class ArraySymbolImpl implements JClassSymbol { @Override public String toString() { - return "array(" + component.toString() + ")"; + return SymbolToStrings.SHARED.toString(this); } - private static class ArrayLengthField implements JFieldSymbol { - - private final JClassSymbol arraySymbol; - - ArrayLengthField(JClassSymbol arraySymbol) { - this.arraySymbol = arraySymbol; - } - - @Override - public String getSimpleName() { - return "length"; - } - - @Override - public int getModifiers() { - return Modifier.PUBLIC | Modifier.FINAL; - } - - @Override - public boolean isEnumConstant() { - return false; - } - - @Override - public @NonNull JClassSymbol getEnclosingClass() { - return arraySymbol; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ArrayLengthField that = (ArrayLengthField) o; - return arraySymbol.equals(that.arraySymbol); - } - - @Override - public int hashCode() { - return Objects.hash(arraySymbol); - } - } - - private static class ArrayCloneMethod implements JMethodSymbol { - - private final ArraySymbolImpl arraySymbol; - - ArrayCloneMethod(ArraySymbolImpl arraySymbol) { - this.arraySymbol = arraySymbol; - } - - @Override - public String getSimpleName() { - return "clone"; - } - - @Override - public List getFormalParameters() { - return Collections.emptyList(); - } - - @Override - public boolean isVarargs() { - return false; - } - - @Override - public int getArity() { - return 0; - } - - @Override - public int getModifiers() { - return Modifier.PUBLIC; - } - - @Override - public @NonNull JClassSymbol getEnclosingClass() { - return arraySymbol; - } - - @Override - public List getTypeParameters() { - return Collections.emptyList(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ArrayCloneMethod that = (ArrayCloneMethod) o; - return Objects.equals(arraySymbol, that.arraySymbol); - } - - @Override - public int hashCode() { - return Objects.hash(arraySymbol); - } - } - - private static class ArrayConstructor implements JConstructorSymbol { - - private final ArraySymbolImpl arraySymbol; - - ArrayConstructor(ArraySymbolImpl arraySymbol) { - this.arraySymbol = arraySymbol; - } - - @Override - public List getFormalParameters() { - return Collections.singletonList(new JFormalParamSymbol() { - - @Override - public JExecutableSymbol getDeclaringSymbol() { - return ArrayConstructor.this; - } - - @Override - public String getSimpleName() { - return "arg0"; - } - - @Override - public boolean isFinal() { - return false; - } - - // TODO equals/hashcode? - }); - } - - @Override - public boolean isVarargs() { - return false; - } - - @Override - public int getArity() { - return 1; - } - - @Override - public int getModifiers() { - return Modifier.PUBLIC | Modifier.FINAL; - } - - @Override - public @NonNull JClassSymbol getEnclosingClass() { - return arraySymbol; - } - - @Override - public List getTypeParameters() { - return Collections.emptyList(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ArrayConstructor that = (ArrayConstructor) o; - return Objects.equals(arraySymbol, that.arraySymbol); - } - - @Override - public int hashCode() { - return Objects.hash(arraySymbol); - } - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ImplicitMemberSymbols.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ImplicitMemberSymbols.java new file mode 100644 index 0000000000..62c606ee86 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ImplicitMemberSymbols.java @@ -0,0 +1,268 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.function.Function; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; +import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; +import net.sourceforge.pmd.util.CollectionUtil; + +/** + * Members inserted by the compiler, eg default constructor, etc. They + * would be absent from the source, but are reflected by the {@link Class}. + */ +public final class ImplicitMemberSymbols { + + private static final int VISIBILITY_MASK = Modifier.PRIVATE | Modifier.PUBLIC | Modifier.PROTECTED; + + private ImplicitMemberSymbols() { + + } + + public static JMethodSymbol enumValueOf(JClassSymbol enumSym) { + assert enumSym.isEnum() : "Not an enum symbol " + enumSym; + + return new FakeMethodSym( + enumSym, + "valueOf", + Modifier.PUBLIC | Modifier.STATIC, + singletonList(t -> new FakeFormalParamSym(t, "name")) + ); + } + + public static JMethodSymbol enumValues(JClassSymbol enumSym) { + assert enumSym.isEnum() : "Not an enum symbol " + enumSym; + + return new FakeMethodSym( + enumSym, + "values", + Modifier.PUBLIC | Modifier.STATIC, + emptyList() + ); + } + + + public static JConstructorSymbol defaultCtor(JClassSymbol sym) { + assert sym != null; + + // Enum constructors have 2 additional implicit parameters, for the name and ordinal + // Inner classes have 1 additional implicit param, for the outer instance + // They are not reflected by the symbol + + int modifiers = sym.isEnum() ? Modifier.PRIVATE + : sym.getModifiers() & VISIBILITY_MASK; + + return new FakeCtorSym(sym, modifiers, emptyList()); + } + + + public static JMethodSymbol arrayClone(JClassSymbol arraySym) { + assert arraySym.isArray() : "Not an array symbol " + arraySym; + + return new FakeMethodSym( + arraySym, + "clone", + Modifier.PUBLIC | Modifier.FINAL, + emptyList() + ); + } + + public static JConstructorSymbol arrayConstructor(JClassSymbol arraySym) { + assert arraySym.isArray() : "Not an array symbol " + arraySym; + + return new FakeCtorSym( + arraySym, + Modifier.PUBLIC | Modifier.FINAL, + singletonList(c -> new FakeFormalParamSym(c, "arg0")) + ); + } + + public static JFieldSymbol arrayLengthField(JClassSymbol arraySym) { + assert arraySym.isArray() : "Not an array symbol " + arraySym; + + return new FakeFieldSym( + arraySym, + "length", + Modifier.PUBLIC | Modifier.FINAL + ); + } + + private abstract static class FakeExecutableSymBase implements JExecutableSymbol { + + private final JClassSymbol owner; + private final String name; + private final int modifiers; + private final List formals; + + FakeExecutableSymBase(JClassSymbol owner, + String name, + int modifiers, + List> formals) { + this.owner = owner; + this.name = name; + this.modifiers = modifiers; + this.formals = CollectionUtil.map(formals, f -> f.apply((T) this)); + } + + @Override + public String getSimpleName() { + return name; + } + + @Override + public List getFormalParameters() { + return formals; + } + + @Override + public boolean isVarargs() { + return false; + } + + @Override + public int getArity() { + return formals.size(); + } + + @Override + public int getModifiers() { + return modifiers; + } + + @Override + public @NonNull JClassSymbol getEnclosingClass() { + return owner; + } + + @Override + public List getTypeParameters() { + return emptyList(); + } + + } + + private static final class FakeMethodSym extends FakeExecutableSymBase implements JMethodSymbol { + + FakeMethodSym(JClassSymbol owner, + String name, + int modifiers, + List> formals) { + super(owner, name, modifiers, formals); + } + + @Override + public boolean equals(Object o) { + return SymbolEquality.METHOD.equals(this, o); + } + + @Override + public int hashCode() { + return SymbolEquality.METHOD.hash(this); + } + } + + private static final class FakeCtorSym extends FakeExecutableSymBase implements JConstructorSymbol { + + FakeCtorSym(JClassSymbol owner, + int modifiers, + List> formals) { + super(owner, JConstructorSymbol.CTOR_NAME, modifiers, formals); + } + + @Override + public boolean equals(Object o) { + return SymbolEquality.CONSTRUCTOR.equals(this, o); + } + + @Override + public int hashCode() { + return SymbolEquality.CONSTRUCTOR.hash(this); + } + } + + private static final class FakeFormalParamSym implements JFormalParamSymbol { + + private final JExecutableSymbol owner; + private final String name; + + private FakeFormalParamSym(JExecutableSymbol owner, String name) { + this.owner = owner; + this.name = name; + } + + @Override + public JExecutableSymbol getDeclaringSymbol() { + return owner; + } + + @Override + public boolean isFinal() { + return false; + } + + @Override + public String getSimpleName() { + return name; + } + } + + + private static final class FakeFieldSym implements JFieldSymbol { + + private final JClassSymbol owner; + private final String name; + private final int modifiers; + + FakeFieldSym(JClassSymbol owner, String name, int modifiers) { + this.owner = owner; + this.name = name; + this.modifiers = modifiers; + } + + @Override + public String getSimpleName() { + return name; + } + + @Override + public int getModifiers() { + return modifiers; + } + + @Override + public boolean isEnumConstant() { + return false; + } + + @Override + public @NonNull JClassSymbol getEnclosingClass() { + return owner; + } + + @Override + public boolean equals(Object o) { + return SymbolEquality.FIELD.equals(this, o); + } + + @Override + public int hashCode() { + return SymbolEquality.FIELD.hash(this); + } + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolEquality.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolEquality.java index 5def8a0679..8c929e5c93 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolEquality.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolEquality.java @@ -11,8 +11,10 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; +import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; +import net.sourceforge.pmd.lang.java.symbols.SymbolVisitor; /** * Routines to share logic for equality, respecting the contract of @@ -157,6 +159,18 @@ public final class SymbolEquality { } }; + private static final EqAndHash IDENTITY = new EqAndHash() { + @Override + public int hash(Object t1) { + return System.identityHashCode(t1); + } + + @Override + public boolean equals(Object t1, Object t2) { + return t1 == t2; + } + }; + /** * Strategy to perform equals/hashcode for a type T. There are libraries * for that, whatever. @@ -168,7 +182,64 @@ public final class SymbolEquality { public abstract boolean equals(T t1, Object t2); + } + public static boolean equals(T e1, Object e2) { + @SuppressWarnings("unchecked") + EqAndHash eqAndHash = (EqAndHash) e1.acceptVisitor(EqAndHashVisitor.INSTANCE, null); + return eqAndHash.equals(e1, e2); + } + + public static int hash(T e1) { + @SuppressWarnings("unchecked") + EqAndHash eqAndHash = (EqAndHash) e1.acceptVisitor(EqAndHashVisitor.INSTANCE, null); + return eqAndHash.hash(e1); + } + + + private static final class EqAndHashVisitor implements SymbolVisitor, Void> { + + static final EqAndHashVisitor INSTANCE = new EqAndHashVisitor(); + + @Override + public EqAndHash visitSymbol(JElementSymbol sym, Void aVoid) { + throw new IllegalStateException("Unknown symbol " + sym.getClass()); + } + + @Override + public EqAndHash visitClass(JClassSymbol sym, Void param) { + return CLASS; + } + + @Override + public EqAndHash visitTypeParam(JTypeParameterSymbol sym, Void param) { + return TYPE_PARAM; + } + + @Override + public EqAndHash visitCtor(JConstructorSymbol sym, Void param) { + return CONSTRUCTOR; + } + + @Override + public EqAndHash visitMethod(JMethodSymbol sym, Void param) { + return METHOD; + } + + @Override + public EqAndHash visitField(JFieldSymbol sym, Void param) { + return FIELD; + } + + @Override + public EqAndHash visitLocal(JLocalVariableSymbol sym, Void param) { + return IDENTITY; + } + + @Override + public EqAndHash visitFormal(JFormalParamSymbol sym, Void param) { + return FORMAL_PARAM; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolFactory.java index d7d4bec210..ef3a2a2c4b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolFactory.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolFactory.java @@ -5,18 +5,12 @@ package net.sourceforge.pmd.lang.java.symbols.internal.impl; -import java.io.Serializable; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals; /** * Builds symbols. @@ -29,46 +23,9 @@ import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInt */ public interface SymbolFactory { - - // object - JClassSymbol OBJECT_SYM = ReflectSymInternals.createSharedSym(Object.class); // unresolved symbol JClassSymbol UNRESOLVED_CLASS_SYM = new UnresolvedClassImpl("/*unresolved*/"); - // primitives - JClassSymbol BOOLEAN_SYM = ReflectSymInternals.createSharedSym(boolean.class); - JClassSymbol BYTE_SYM = ReflectSymInternals.createSharedSym(byte.class); - JClassSymbol CHAR_SYM = ReflectSymInternals.createSharedSym(char.class); - JClassSymbol DOUBLE_SYM = ReflectSymInternals.createSharedSym(double.class); - JClassSymbol FLOAT_SYM = ReflectSymInternals.createSharedSym(float.class); - JClassSymbol INT_SYM = ReflectSymInternals.createSharedSym(int.class); - JClassSymbol LONG_SYM = ReflectSymInternals.createSharedSym(long.class); - JClassSymbol SHORT_SYM = ReflectSymInternals.createSharedSym(short.class); - JClassSymbol VOID_SYM = ReflectSymInternals.createSharedSym(void.class); - - // primitive wrappers - JClassSymbol BOXED_BOOLEAN_SYM = ReflectSymInternals.createSharedSym(Boolean.class); - JClassSymbol BOXED_BYTE_SYM = ReflectSymInternals.createSharedSym(Byte.class); - JClassSymbol BOXED_CHAR_SYM = ReflectSymInternals.createSharedSym(Character.class); - JClassSymbol BOXED_DOUBLE_SYM = ReflectSymInternals.createSharedSym(Double.class); - JClassSymbol BOXED_FLOAT_SYM = ReflectSymInternals.createSharedSym(Float.class); - JClassSymbol BOXED_INT_SYM = ReflectSymInternals.createSharedSym(Integer.class); - JClassSymbol BOXED_LONG_SYM = ReflectSymInternals.createSharedSym(Long.class); - JClassSymbol BOXED_SHORT_SYM = ReflectSymInternals.createSharedSym(Short.class); - JClassSymbol BOXED_VOID_SYM = ReflectSymInternals.createSharedSym(Void.class); - - // array supertypes - JClassSymbol CLONEABLE_SYM = ReflectSymInternals.createSharedSym(Cloneable.class); - JClassSymbol SERIALIZABLE_SYM = ReflectSymInternals.createSharedSym(Serializable.class); - List ARRAY_SUPER_INTERFACES = Collections.unmodifiableList(Arrays.asList(CLONEABLE_SYM, SERIALIZABLE_SYM)); - - // other important/common types - JClassSymbol CLASS_SYM = ReflectSymInternals.createSharedSym(Class.class); - JClassSymbol ITERABLE_SYM = ReflectSymInternals.createSharedSym(Iterable.class); - JClassSymbol ENUM_SYM = ReflectSymInternals.createSharedSym(Enum.class); - JClassSymbol STRING_SYM = ReflectSymInternals.createSharedSym(String.class); - - /** * Produces an unresolved class symbol from the given canonical name. * @@ -83,6 +40,7 @@ public interface SymbolFactory { } + /** * Produces an array symbol from the given component symbol (one dimension). * The component can naturally be another array symbol, but cannot be an diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolToStrings.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolToStrings.java new file mode 100644 index 0000000000..0d455e3a1d --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolToStrings.java @@ -0,0 +1,121 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl; + +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; +import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; +import net.sourceforge.pmd.lang.java.symbols.SymbolVisitor; + +public class SymbolToStrings { + + public static final SymbolToStrings SHARED = new SymbolToStrings(""); + public static final SymbolToStrings REFLECT = new SymbolToStrings("reflect"); + public static final SymbolToStrings AST = new SymbolToStrings("ast"); + + private final ToStringVisitor visitor; + + public SymbolToStrings(String impl) { + this.visitor = new ToStringVisitor(impl); + } + + public String toString(JElementSymbol symbol) { + return symbol.acceptVisitor(visitor, new StringBuilder()).toString(); + } + + private static class ToStringVisitor implements SymbolVisitor { + + private final String impl; + + private ToStringVisitor(String impl) { + this.impl = impl; + } + + private StringBuilder withImpl(StringBuilder builder, String kind, Object first, Object... rest) { + if (!impl.isEmpty()) { + builder.append(impl).append(':'); + } + builder.append(kind).append('('); + appendArg(builder, first); + for (Object s : rest) { + builder.append(", "); + appendArg(builder, s); + } + return builder.append(')'); + } + + private void appendArg(StringBuilder builder, Object s) { + if (s instanceof JElementSymbol) { + ((JElementSymbol) s).acceptVisitor(this, builder); + } else { + builder.append(s); + } + } + + @Override + public StringBuilder visitSymbol(JElementSymbol sym, StringBuilder builder) { + throw new IllegalStateException("Unknown symbol " + sym.getClass()); + } + + @Override + public StringBuilder visitClass(JClassSymbol sym, StringBuilder param) { + String kind; + if (sym.isUnresolved()) { + kind = "unresolved"; + } else if (sym.isEnum()) { + kind = "enum"; + } else if (sym.isAnnotation()) { + kind = "annot"; + } else { + kind = "class"; + } + + return withImpl(param, kind, sym.getBinaryName()); + } + + @Override + public StringBuilder visitArray(JClassSymbol sym, JTypeDeclSymbol component, StringBuilder param) { + param.append("array("); + return component.acceptVisitor(this, param).append(")"); + } + + @Override + public StringBuilder visitTypeParam(JTypeParameterSymbol sym, StringBuilder param) { + return withImpl(param, "tparam", sym.getSimpleName(), sym.getDeclaringSymbol()); + } + + @Override + public StringBuilder visitCtor(JConstructorSymbol sym, StringBuilder param) { + return withImpl(param, "ctor", sym.getEnclosingClass()); + } + + @Override + public StringBuilder visitMethod(JMethodSymbol sym, StringBuilder param) { + return withImpl(param, "method", sym.getSimpleName(), sym.getEnclosingClass()); + } + + @Override + public StringBuilder visitField(JFieldSymbol sym, StringBuilder param) { + return withImpl(param, "field", sym.getSimpleName(), sym.getEnclosingClass()); + } + + @Override + public StringBuilder visitLocal(JLocalVariableSymbol sym, StringBuilder param) { + return withImpl(param, "local", sym.getSimpleName()); + } + + @Override + public StringBuilder visitFormal(JFormalParamSymbol sym, StringBuilder param) { + return withImpl(param, "formal", sym.getSimpleName()); + } + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/UnresolvedClassImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/UnresolvedClassImpl.java index 3d30d78996..0e454ffaa8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/UnresolvedClassImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/UnresolvedClassImpl.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.symbols.internal.impl; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.List; -import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -19,6 +18,7 @@ import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals; /** * Unresolved external reference to a class. @@ -95,7 +95,7 @@ class UnresolvedClassImpl implements JClassSymbol { @Nullable @Override public JClassSymbol getSuperclass() { - return SymbolFactory.OBJECT_SYM; + return ReflectSymInternals.OBJECT_SYM; } @@ -171,32 +171,25 @@ class UnresolvedClassImpl implements JClassSymbol { return Collections.emptyList(); } - @Override - public String toString() { - return "unresolved(" + canonicalName + ")"; - } - @Override public List getTypeParameters() { return Collections.emptyList(); } + @Override + public String toString() { + return SymbolToStrings.SHARED.toString(this); + } + @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof JClassSymbol)) { - return false; - } - JClassSymbol that = (JClassSymbol) o; - return Objects.equals(getBinaryName(), that.getBinaryName()); + return SymbolEquality.equals(this, o); } @Override public int hashCode() { - return Objects.hash(this.getSimpleName()); + return SymbolEquality.hash(this); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstBackedSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstBackedSymbol.java new file mode 100644 index 0000000000..e2211cfa35 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstBackedSymbol.java @@ -0,0 +1,41 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; +import net.sourceforge.pmd.lang.java.ast.SymbolDeclaratorNode; +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolToStrings; + +/** + * @author Clément Fournier + */ +abstract class AbstractAstBackedSymbol implements JElementSymbol { + + protected final T node; + protected final AstSymFactory factory; + + protected AbstractAstBackedSymbol(T node, AstSymFactory factory) { + this.node = node; + this.factory = factory; + InternalApiBridge.setSymbol(node, this); + } + + @Override + public String toString() { + return SymbolToStrings.AST.toString(this); + } + + @Override + public boolean equals(Object o) { + return SymbolEquality.equals(this, o); + } + + @Override + public int hashCode() { + return SymbolEquality.hash(this); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstExecSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstExecSymbol.java new file mode 100644 index 0000000000..cff5957e3d --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstExecSymbol.java @@ -0,0 +1,58 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import java.util.List; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; +import net.sourceforge.pmd.util.CollectionUtil; + +/** + * @author Clément Fournier + */ +abstract class AbstractAstExecSymbol + extends AbstractAstTParamOwner + implements JExecutableSymbol { + + private final JClassSymbol owner; + private List formals; + + protected AbstractAstExecSymbol(T node, AstSymFactory factory, JClassSymbol owner) { + super(node, factory); + this.owner = owner; + formals = CollectionUtil.map( + node.getFormalParameters(), + p -> new AstFormalParamSym(p.getVarId(), factory, this) + ); + } + + @Override + public List getFormalParameters() { + return formals; + } + + @Override + public @NonNull JClassSymbol getEnclosingClass() { + return owner; + } + + + @Override + public boolean isVarargs() { + return node.isVarargs(); + } + + @Override + public int getArity() { + return formals.size(); + } + + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstTParamOwner.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstTParamOwner.java new file mode 100644 index 0000000000..120aad1551 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstTParamOwner.java @@ -0,0 +1,70 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import static net.sourceforge.pmd.util.CollectionUtil.map; + +import java.util.Collections; +import java.util.List; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTList; +import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters; +import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.ast.TypeParamOwnerNode; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; + +/** + * @author Clément Fournier + */ +abstract class AbstractAstTParamOwner + extends AbstractAstBackedSymbol implements JTypeParameterOwnerSymbol { + + private final List tparams; + private final int modifiers; + + + AbstractAstTParamOwner(T node, AstSymFactory factory) { + super(node, factory); + this.modifiers = JModifier.toReflect(node.getModifiers().getEffectiveModifiers()); + + List result = map( + ASTList.orEmpty(node.getTypeParameters()), + it -> new AstTypeParamSym(it, factory, this) + ); + + // this needs to be set before calling computeBounds + this.tparams = Collections.unmodifiableList(result); + + } + + @Override + public int getModifiers() { + return modifiers; + } + + @Override + public List getTypeParameters() { + return tparams; + } + + + @Override + public @NonNull String getPackageName() { + return node.getRoot().getPackageName(); + } + + @Override + public int getTypeParameterCount() { + if (tparams != null) { + return tparams.size(); + } + ASTTypeParameters ps = node.getTypeParameters(); + return ps == null ? 0 : ps.jjtGetNumChildren(); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstVariableSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstVariableSym.java new file mode 100644 index 0000000000..3dce2dce80 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AbstractAstVariableSym.java @@ -0,0 +1,31 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; + +/** + * @author Clément Fournier + */ +abstract class AbstractAstVariableSym + extends AbstractAstBackedSymbol + implements JVariableSymbol { + + AbstractAstVariableSym(ASTVariableDeclaratorId node, AstSymFactory factory) { + super(node, factory); + } + + @Override + public boolean isFinal() { + return node.isFinal(); + } + + @Override + public String getSimpleName() { + return node.getVariableName(); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstClassSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstClassSym.java new file mode 100644 index 0000000000..11d737c977 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstClassSym.java @@ -0,0 +1,217 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; +import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.ImplicitMemberSymbols; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals; + + +final class AstClassSym + extends AbstractAstTParamOwner + implements JClassSymbol { + + private final @Nullable JTypeParameterOwnerSymbol enclosing; + private final List declaredClasses; + private final List declaredMethods; + private final List declaredCtors; + private final List declaredFields; + + AstClassSym(ASTAnyTypeDeclaration node, + AstSymFactory factory, + @Nullable JTypeParameterOwnerSymbol enclosing) { + super(node, factory); + this.enclosing = enclosing; + + // evaluate everything strictly + // this populates symbols on the relevant AST nodes + + List myClasses = new ArrayList<>(); + List myMethods = new ArrayList<>(); + List myCtors = new ArrayList<>(); + List myFields = new ArrayList<>(); + + if (node instanceof ASTEnumDeclaration) { + node.getEnumConstants().forEach(constant -> myFields.add(new AstFieldSym(constant.getVarId(), factory, this))); + } + + for (ASTAnyTypeBodyDeclaration decl : node.getDeclarations()) { + + JavaNode dnode = decl.getDeclarationNode(); + + if (dnode instanceof ASTAnyTypeDeclaration) { + myClasses.add(new AstClassSym((ASTAnyTypeDeclaration) dnode, factory, this)); + } else if (dnode instanceof ASTMethodDeclaration) { + myMethods.add(new AstMethodSym((ASTMethodDeclaration) dnode, factory, this)); + } else if (dnode instanceof ASTConstructorDeclaration) { + myCtors.add(new AstCtorSym((ASTConstructorDeclaration) dnode, factory, this)); + } else if (dnode instanceof ASTFieldDeclaration) { + for (ASTVariableDeclaratorId varId : ((ASTFieldDeclaration) dnode).getVarIds()) { + myFields.add(new AstFieldSym(varId, factory, this)); + } + } + } + + if (myCtors.isEmpty() && isClass()) { + myCtors.add(ImplicitMemberSymbols.defaultCtor(this)); + } + + if (this.isEnum()) { + myMethods.add(ImplicitMemberSymbols.enumValues(this)); + myMethods.add(ImplicitMemberSymbols.enumValueOf(this)); + } + + this.declaredClasses = Collections.unmodifiableList(myClasses); + this.declaredMethods = Collections.unmodifiableList(myMethods); + this.declaredCtors = Collections.unmodifiableList(myCtors); + this.declaredFields = Collections.unmodifiableList(myFields); + } + + @Override + public @NonNull String getSimpleName() { + return node.getSimpleName(); + } + + + @Override + public @NonNull String getBinaryName() { + return node.getBinaryName(); + } + + @Override + public @Nullable String getCanonicalName() { + return node.getCanonicalName(); + } + + @Override + public boolean isUnresolved() { + return false; + } + + @Override + public @Nullable JClassSymbol getEnclosingClass() { + if (enclosing instanceof JClassSymbol) { + return (JClassSymbol) enclosing; + } else if (enclosing instanceof JExecutableSymbol) { + return enclosing.getEnclosingClass(); + } + assert enclosing == null; + return null; + } + + @Override + public @Nullable JExecutableSymbol getEnclosingMethod() { + return enclosing instanceof JExecutableSymbol ? (JExecutableSymbol) enclosing : null; + } + + @Override + public List getDeclaredClasses() { + return declaredClasses; + } + + @Override + public List getDeclaredMethods() { + return declaredMethods; + } + + @Override + public List getConstructors() { + return declaredCtors; + } + + @Override + public List getDeclaredFields() { + return declaredFields; + } + + + @Override + public @Nullable JClassSymbol getSuperclass() { + if (isEnum()) { + return ReflectSymInternals.ENUM_SYM; + } else if (node instanceof ASTClassOrInterfaceDeclaration) { + // This is TODO, needs symbol table + } + return null; + } + + // those casts only succeed if the program compiles, it relies on + // the fact that only classes can be super interfaces, ie not String[] + // or some type var + + @Override + public List getSuperInterfaces() { + // TODO needs symbol table + return Collections.emptyList(); + } + + @Override + public @Nullable JTypeDeclSymbol getArrayComponent() { + return null; + } + + @Override + public boolean isArray() { + return false; + } + + @Override + public boolean isPrimitive() { + return false; + } + + @Override + public boolean isInterface() { + return node.isInterface(); + } + + @Override + public boolean isEnum() { + return node.isEnum(); + } + + @Override + public boolean isAnnotation() { + return node.isAnnotation(); + } + + @Override + public boolean isLocalClass() { + return node.isLocal(); + } + + @Override + public boolean isAnonymousClass() { + return node.isAnonymous(); + } + + @Override + public @Nullable Class getJvmRepr() { + return null; + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstCtorSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstCtorSym.java new file mode 100644 index 0000000000..07eb879f26 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstCtorSym.java @@ -0,0 +1,20 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; + +/** + * @author Clément Fournier + */ +final class AstCtorSym extends AbstractAstExecSymbol implements JConstructorSymbol { + + AstCtorSym(ASTConstructorDeclaration node, AstSymFactory factory, JClassSymbol owner) { + super(node, factory, owner); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstFieldSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstFieldSym.java new file mode 100644 index 0000000000..2cbc025351 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstFieldSym.java @@ -0,0 +1,41 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; + +final class AstFieldSym extends AbstractAstVariableSym implements JFieldSymbol { + + + private final JClassSymbol owner; + + AstFieldSym(ASTVariableDeclaratorId node, + AstSymFactory factory, + JClassSymbol owner) { + super(node, factory); + this.owner = owner; + } + + @Override + public int getModifiers() { + return JModifier.toReflect(node.getModifiers().getEffectiveModifiers()); + } + + @Override + public boolean isEnumConstant() { + return node.isEnumConstant(); + } + + @Override + public @NonNull JClassSymbol getEnclosingClass() { + return owner; + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstFormalParamSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstFormalParamSym.java new file mode 100644 index 0000000000..cb32bb0c44 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstFormalParamSym.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; + +/** + * @author Clément Fournier + */ +final class AstFormalParamSym extends AbstractAstVariableSym implements JFormalParamSymbol { + + private final AbstractAstExecSymbol owner; + + AstFormalParamSym(ASTVariableDeclaratorId node, AstSymFactory factory, AbstractAstExecSymbol owner) { + super(node, factory); + this.owner = owner; + } + + @Override + public JExecutableSymbol getDeclaringSymbol() { + return owner; + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstLocalVarSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstLocalVarSym.java new file mode 100644 index 0000000000..768bc96a68 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstLocalVarSym.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; + +/** + * @author Clément Fournier + */ +final class AstLocalVarSym extends AbstractAstVariableSym implements JLocalVariableSymbol { + + AstLocalVarSym(ASTVariableDeclaratorId node, AstSymFactory factory) { + super(node, factory); + } + + @Override + public boolean equals(Object o) { + return node.equals(o); + } + + @Override + public int hashCode() { + return node.hashCode(); + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstMethodSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstMethodSym.java new file mode 100644 index 0000000000..733f9da9d2 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstMethodSym.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; + +/** + * @author Clément Fournier + */ +final class AstMethodSym + extends AbstractAstExecSymbol + implements JMethodSymbol { + + + AstMethodSym(ASTMethodDeclaration node, AstSymFactory factory, JClassSymbol owner) { + super(node, factory, owner); + } + + @Override + public String getSimpleName() { + return node.getName(); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstSymFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstSymFactory.java new file mode 100644 index 0000000000..35ddd7977b --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstSymFactory.java @@ -0,0 +1,39 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; + + +public final class AstSymFactory { + + // keep in mind, creating a symbol sets it on the node (see constructor of AbstractAstBackedSymbol) + + /** + * Builds, sets and returns the symbol for the given local variable. + */ + public JLocalVariableSymbol setLocalVarSymbol(ASTVariableDeclaratorId id) { + assert !id.isField() && !id.isEnumConstant() : "Local var symbol is not appropriate for fields"; + assert !id.isFormalParameter() + || id.isLambdaParameter() + || id.isExceptionBlockParameter() : "Local var symbol is not appropriate for method parameters"; + + return new AstLocalVarSym(id, this); + } + + /** + * Builds, sets and returns the symbol for the given class. + */ + public JClassSymbol setClassSymbol(@Nullable JTypeParameterOwnerSymbol enclosing, ASTAnyTypeDeclaration klass) { + return new AstClassSym(klass, this, enclosing); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstTypeParamSym.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstTypeParamSym.java new file mode 100644 index 0000000000..a17909242b --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ast/AstTypeParamSym.java @@ -0,0 +1,43 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.impl.ast; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; + +final class AstTypeParamSym + extends AbstractAstBackedSymbol + implements JTypeParameterSymbol { + + private final AbstractAstTParamOwner owner; + // private final FreshTypeVar typeVar; + + AstTypeParamSym(ASTTypeParameter node, AstSymFactory factory, AbstractAstTParamOwner owner) { + super(node, factory); + this.owner = owner; + // this.typeVar = factory.types().newTypeVar(this); + } + + @Override + public JTypeParameterOwnerSymbol getDeclaringSymbol() { + return owner; + } + + @Override + public @Nullable Class getJvmRepr() { + return null; + } + + @NonNull + @Override + public String getSimpleName() { + return node.getParameterName(); + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedExecutableSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedExecutableSymbol.java index e52fb10483..cab98372b2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedExecutableSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedExecutableSymbol.java @@ -52,6 +52,9 @@ abstract class AbstractReflectedExecutableSymbol extends A public final List getFormalParameters() { if (params == null) { this.params = Arrays.stream(reflected.getParameters()) + // implicit parameters are filtered out + // this affects eg params of enum constructors + .filter(it -> !it.isImplicit()) .map(p -> new ReflectedMethodParamImpl(this, p)) .collect(Collectors.toList()); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedSymbol.java index 2176bd6fbc..5f00cc1664 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedSymbol.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedSymbol.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect; import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; +import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolToStrings; /** * @@ -17,4 +19,18 @@ abstract class AbstractReflectedSymbol implements JElementSymbol { this.symFactory = symFactory; } + @Override + public String toString() { + return SymbolToStrings.REFLECT.toString(this); + } + + @Override + public boolean equals(Object o) { + return SymbolEquality.equals(this, o); + } + + @Override + public int hashCode() { + return SymbolEquality.hash(this); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectSymInternals.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectSymInternals.java index d619f62619..d745593da3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectSymInternals.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectSymInternals.java @@ -4,6 +4,11 @@ package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory; @@ -12,7 +17,38 @@ import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory; */ public final class ReflectSymInternals { - private static final ReflectionSymFactory STATIC_FACTORY = new ReflectionSymFactory(); + public static final ReflectionSymFactory STATIC_FACTORY = new ReflectionSymFactory(); + // object + public static final JClassSymbol OBJECT_SYM = createSharedSym(Object.class); + // primitives + public static final JClassSymbol BOOLEAN_SYM = createSharedSym(boolean.class); + public static final JClassSymbol BYTE_SYM = createSharedSym(byte.class); + public static final JClassSymbol CHAR_SYM = createSharedSym(char.class); + public static final JClassSymbol DOUBLE_SYM = createSharedSym(double.class); + public static final JClassSymbol FLOAT_SYM = createSharedSym(float.class); + public static final JClassSymbol INT_SYM = createSharedSym(int.class); + public static final JClassSymbol LONG_SYM = createSharedSym(long.class); + public static final JClassSymbol SHORT_SYM = createSharedSym(short.class); + public static final JClassSymbol VOID_SYM = createSharedSym(void.class); + // primitive wrappers + public static final JClassSymbol BOXED_BOOLEAN_SYM = createSharedSym(Boolean.class); + public static final JClassSymbol BOXED_BYTE_SYM = createSharedSym(Byte.class); + public static final JClassSymbol BOXED_CHAR_SYM = createSharedSym(Character.class); + public static final JClassSymbol BOXED_DOUBLE_SYM = createSharedSym(Double.class); + public static final JClassSymbol BOXED_FLOAT_SYM = createSharedSym(Float.class); + public static final JClassSymbol BOXED_INT_SYM = createSharedSym(Integer.class); + public static final JClassSymbol BOXED_LONG_SYM = createSharedSym(Long.class); + public static final JClassSymbol BOXED_SHORT_SYM = createSharedSym(Short.class); + public static final JClassSymbol BOXED_VOID_SYM = createSharedSym(Void.class); + // array supertypes + public static final JClassSymbol CLONEABLE_SYM = createSharedSym(Cloneable.class); + public static final JClassSymbol SERIALIZABLE_SYM = createSharedSym(Serializable.class); + public static final List ARRAY_SUPER_INTERFACES = Collections.unmodifiableList(Arrays.asList(CLONEABLE_SYM, SERIALIZABLE_SYM)); + // other important/common types + public static final JClassSymbol CLASS_SYM = createSharedSym(Class.class); + public static final JClassSymbol ITERABLE_SYM = createSharedSym(Iterable.class); + public static final JClassSymbol ENUM_SYM = createSharedSym(Enum.class); + public static final JClassSymbol STRING_SYM = createSharedSym(String.class); private ReflectSymInternals() { // util class diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedClassImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedClassImpl.java index c7b1bd43ec..a0a4ec22d4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedClassImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedClassImpl.java @@ -18,8 +18,6 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory; final class ReflectedClassImpl extends AbstractTypeParamOwnerSymbol> implements JClassSymbol { @@ -107,7 +105,7 @@ final class ReflectedClassImpl extends AbstractTypeParamOwnerSymbol> im @Override public List getSuperInterfaces() { if (superInterfaces == null) { - superInterfaces = myClass.isArray() ? SymbolFactory.ARRAY_SUPER_INTERFACES + superInterfaces = myClass.isArray() ? ReflectSymInternals.ARRAY_SUPER_INTERFACES : Arrays.stream(myClass.getInterfaces()).map(symFactory::getClassSymbol).collect(toList()); } return superInterfaces; @@ -204,22 +202,6 @@ final class ReflectedClassImpl extends AbstractTypeParamOwnerSymbol> im return declaredFields; } - @Override - public String toString() { - return getBinaryName(); - } - - @Override - public boolean equals(Object o) { - return SymbolEquality.CLASS.equals(this, o); - } - - @Override - public int hashCode() { - return SymbolEquality.CLASS.hash(this); - } - - static ReflectedClassImpl createWithEnclosing(ReflectionSymFactory symbolFactory, @Nullable JClassSymbol enclosing, Class myClass) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedCtorImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedCtorImpl.java index a8f83252e9..02ea37e955 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedCtorImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedCtorImpl.java @@ -9,7 +9,6 @@ import java.lang.reflect.Constructor; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; class ReflectedCtorImpl extends AbstractReflectedExecutableSymbol> implements JConstructorSymbol { @@ -18,13 +17,4 @@ class ReflectedCtorImpl extends AbstractReflectedExecutableSymbol super(owner, myConstructor); } - @Override - public boolean equals(Object obj) { - return SymbolEquality.CONSTRUCTOR.equals(this, obj); - } - - @Override - public int hashCode() { - return SymbolEquality.CONSTRUCTOR.hash(this); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedFieldImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedFieldImpl.java index 58df439d81..93ccab2168 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedFieldImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedFieldImpl.java @@ -10,7 +10,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; class ReflectedFieldImpl extends AbstractReflectedSymbol implements JFieldSymbol { @@ -44,14 +43,4 @@ class ReflectedFieldImpl extends AbstractReflectedSymbol implements JFieldSymbol return myField.getModifiers(); } - - @Override - public boolean equals(Object o) { - return SymbolEquality.FIELD.equals(this, o); - } - - @Override - public int hashCode() { - return SymbolEquality.FIELD.hash(this); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodImpl.java index e415f9866a..10a0f7cbac 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodImpl.java @@ -9,7 +9,6 @@ import java.lang.reflect.Method; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; class ReflectedMethodImpl extends AbstractReflectedExecutableSymbol implements JMethodSymbol { @@ -24,14 +23,4 @@ class ReflectedMethodImpl extends AbstractReflectedExecutableSymbol impl return reflected.getName(); } - - @Override - public boolean equals(Object o) { - return SymbolEquality.METHOD.equals(this, o); - } - - @Override - public int hashCode() { - return SymbolEquality.METHOD.hash(this); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodParamImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodParamImpl.java index 64c335d27f..2e6d120bd0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodParamImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodParamImpl.java @@ -9,7 +9,6 @@ import java.lang.reflect.Parameter; import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; final class ReflectedMethodParamImpl extends AbstractReflectedSymbol implements JFormalParamSymbol { @@ -38,13 +37,4 @@ final class ReflectedMethodParamImpl extends AbstractReflectedSymbol implements return reflected.getName(); } - @Override - public boolean equals(Object o) { - return SymbolEquality.FORMAL_PARAM.equals(this, o); - } - - @Override - public int hashCode() { - return SymbolEquality.FORMAL_PARAM.hash(this); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedTypeParamImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedTypeParamImpl.java index 1eae5f6789..375c21400d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedTypeParamImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedTypeParamImpl.java @@ -11,7 +11,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality; @SuppressWarnings("PMD") // yeah this looks weird for now @@ -47,13 +46,4 @@ class ReflectedTypeParamImpl implements JTypeParameterSymbol { return name; } - @Override - public boolean equals(Object o) { - return SymbolEquality.TYPE_PARAM.equals(this, o); - } - - @Override - public int hashCode() { - return SymbolEquality.TYPE_PARAM.hash(this); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectionSymFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectionSymFactory.java index 7402fdd362..a92b339fe3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectionSymFactory.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectionSymFactory.java @@ -5,6 +5,30 @@ package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOOLEAN_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_BOOLEAN_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_BYTE_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_CHAR_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_DOUBLE_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_FLOAT_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_INT_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_LONG_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_SHORT_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BOXED_VOID_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.BYTE_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.CHAR_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.CLONEABLE_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.DOUBLE_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.ENUM_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.FLOAT_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.INT_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.ITERABLE_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.LONG_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.OBJECT_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.SERIALIZABLE_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.SHORT_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.STRING_SYM; +import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.VOID_SYM; import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectedClassImpl.createOuterClass; import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectedClassImpl.createWithEnclosing; @@ -31,7 +55,6 @@ public final class ReflectionSymFactory implements SymbolFactory> { */ private static Map, JClassSymbol> commonSymbols; - @Override @Nullable public JClassSymbol getClassSymbol(@Nullable Class klass) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/EmptySymbolTable.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/EmptySymbolTable.java index ddadc2a962..b1512fcf5f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/EmptySymbolTable.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/EmptySymbolTable.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.java.symbols.table.internal; import java.util.stream.Stream; -import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; @@ -37,13 +37,13 @@ final class EmptySymbolTable implements JSymbolTable { @Override - public @NonNull ResolveResult resolveTypeName(String simpleName) { + public @Nullable ResolveResult resolveTypeName(String simpleName) { return ResolveResultImpl.failed(); } @Override - public @NonNull ResolveResult resolveValueName(String simpleName) { + public @Nullable ResolveResult resolveValueName(String simpleName) { return ResolveResultImpl.failed(); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SamePackageSymbolTable.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SamePackageSymbolTable.java index d4ce8bf05c..d8b05474b6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SamePackageSymbolTable.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SamePackageSymbolTable.java @@ -4,9 +4,12 @@ package net.sourceforge.pmd.lang.java.symbols.table.internal; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.table.JSymbolTable; @@ -22,11 +25,12 @@ import net.sourceforge.pmd.lang.java.symbols.table.internal.ResolveResultImpl.Cl */ final class SamePackageSymbolTable extends AbstractSymbolTable { - private final @Nullable ASTPackageDeclaration packageDeclaration; + private final @NonNull JavaNode contributor; - SamePackageSymbolTable(JSymbolTable parent, SymbolTableHelper helper, @Nullable ASTPackageDeclaration packageDeclaration) { + SamePackageSymbolTable(JSymbolTable parent, SymbolTableHelper helper, ASTCompilationUnit acu) { super(parent, helper); - this.packageDeclaration = packageDeclaration; + ASTPackageDeclaration pdecl = acu.getPackageDeclaration(); + this.contributor = pdecl == null ? acu : pdecl; } @@ -38,6 +42,6 @@ final class SamePackageSymbolTable extends AbstractSymbolTable { // or if the type was never in this package in the first place JClassSymbol jClassSymbol = loadClassIgnoreFailure(helper.prependPackageName(simpleName)); return jClassSymbol == null ? null - : new ClassResolveResult(jClassSymbol, this, packageDeclaration); + : new ClassResolveResult(jClassSymbol, this, contributor); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java index c648982f36..c571dae134 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTModifierList; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.SideEffectingVisitorAdapter; @@ -61,6 +62,7 @@ public final class SymbolTableResolver { : "Unbalanced stack push/pop! Top is " + myStackTop; } + /** * Create a new symbol table using {@link TableLinker#createAndLink(JSymbolTable, SymbolTableHelper, Object)}, * linking it to the top of the stack as its parent. @@ -98,7 +100,6 @@ public final class SymbolTableResolver { return this.myStackTop; } - @FunctionalInterface private interface TableLinker { @@ -120,6 +121,10 @@ public final class SymbolTableResolver { // The parameter on the visit methods is unnecessary // TODO introduce another visitor with `void visit(Node);` signature + @Override + public void visit(ASTModifierList node, Void data) { + // do nothing + } @Override public void visit(ASTCompilationUnit node, Void data) { @@ -129,7 +134,7 @@ public final class SymbolTableResolver { int pushed = 0; pushed += pushOnStack(ImportOnDemandSymbolTable::new, isImportOnDemand.get(true)); pushed += pushOnStack(JavaLangSymbolTable::new, node); - pushed += pushOnStack(SamePackageSymbolTable::new, node.getPackageDeclaration()); + pushed += pushOnStack(SamePackageSymbolTable::new, node); pushed += pushOnStack(SingleImportSymbolTable::new, isImportOnDemand.get(false)); // types declared inside the compilation unit // pushed += pushOnStack(MemberTypeSymTable::new, node); @@ -140,8 +145,12 @@ public final class SymbolTableResolver { } - private void setTopSymbolTableAndRecurse(JavaNode node) { + private void setTopSymbolTable(JavaNode node) { InternalApiBridge.setSymbolTable(node, peekStack()); + } + + private void setTopSymbolTableAndRecurse(JavaNode node) { + setTopSymbolTable(node); for (JavaNode child : node.children()) { child.jjtAccept(this, null); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/package-info.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/package-info.java index 18ea325008..799e501bce 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/package-info.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/package-info.java @@ -21,7 +21,7 @@ * and static method or field names imported from a type by a static-import-on-demand; *
  • {@link net.sourceforge.pmd.lang.java.symbols.table.internal.JavaLangSymbolTable}: Top-level types implicitly imported from {@code java.lang}; *
  • {@link net.sourceforge.pmd.lang.java.symbols.table.internal.SamePackageSymbolTable}: Top-level types from the same package, which are implicitly imported - *
  • {@link net.sourceforge.pmd.lang.java.symbols.table.internal.SingleImportSymbolTable}: types imported by single-type-imports, and static methods and + *
  • {@link net.sourceforge.pmd.lang.java.symbols.table.internal.SingleImportSymbolTable}: Types imported by single-type-imports, and static methods and * fields imported by a single-static-import. * * These dominate the whole compilation unit and thus are all linked to the {@link net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit}. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java index e9b67a934d..d72065a677 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java @@ -7,6 +7,8 @@ package net.sourceforge.pmd.lang.java; import java.util.ArrayList; import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; @@ -23,6 +25,7 @@ public class JavaParsingHelper extends BaseParsingHelper { + child { it::getAnnotationName shouldBe "F" it::getSimpleName shouldBe "F" + + it::getMemberList shouldBe null } } "@java.lang.Override" should parseAs { - child { + child { it::getAnnotationName shouldBe "java.lang.Override" it::getSimpleName shouldBe "Override" + + it::getMemberList shouldBe null } } } @@ -51,45 +55,63 @@ class ASTAnnotationTest : ParserTestSpec({ inContext(AnnotationParsingCtx) { "@F(\"ohio\")" should parseAs { - child { + child { it::getAnnotationName shouldBe "F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe stringLit("\"ohio\"") + it::getMemberList shouldBe child { + shorthandMemberValue { + stringLit("\"ohio\"") + } + } } } "@org.F({java.lang.Math.PI})" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe child { - child { - it::getFieldName shouldBe "PI" - ambiguousName("java.lang.Math") + it::getMemberList shouldBe child { + shorthandMemberValue { + child { + child { + it::getFieldName shouldBe "PI" + ambiguousName("java.lang.Math") + } + } } } } } "@org.F({@Aha, @Oh})" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe child { - annotation("Aha") - annotation("Oh") + + it::getMemberList shouldBe child { + shorthandMemberValue { + child { + annotation("Aha") + annotation("Oh") + } + } } } } "@org.F(@Oh)" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - it::getMemberValue shouldBe annotation("Oh") + + it::getMemberList shouldBe child { + shorthandMemberValue { + annotation("Oh") + } + } } } } @@ -101,44 +123,37 @@ class ASTAnnotationTest : ParserTestSpec({ inContext(AnnotationParsingCtx) { "@F(a=\"ohio\")" should parseAs { - child { + child { it::getAnnotationName shouldBe "F" it::getSimpleName shouldBe "F" - memberValuePair("a") { - stringLit("\"ohio\"") + + it::getMemberList shouldBe child { + memberValuePair("a") { + stringLit("\"ohio\"") + } } } } "@org.F(a={java.lang.Math.PI}, b=2)" should parseAs { - child { + child { it::getAnnotationName shouldBe "org.F" it::getSimpleName shouldBe "F" - memberValuePair("a") { - child { - child { - it::getFieldName shouldBe "PI" - ambiguousName("java.lang.Math") + + it::getMemberList shouldBe child { + memberValuePair("a") { + child { + fieldAccess("PI") { + ambiguousName("java.lang.Math") + } } } - } - memberValuePair("b") { - number() - } - } - } - - "@org.F({@Aha, @Oh})" should parseAs { - child { - it::getAnnotationName shouldBe "org.F" - it::getSimpleName shouldBe "F" - - it::getMemberValue shouldBe child { - annotation("Aha") - annotation("Oh") + memberValuePair("b") { + number() + } } } } @@ -146,28 +161,45 @@ class ASTAnnotationTest : ParserTestSpec({ """ @TestAnnotation({@SuppressWarnings({}), - @SuppressWarnings({"Beware the ides of March.",}), + @SuppressWarnings(value = {"Beware the ides of March.",}), @SuppressWarnings({"Look both ways", "Before Crossing",}), }) """ should parseAs { - child { + child { - it::getMemberValue shouldBe child { - child { + it::getMemberList shouldBe child { - it::getMemberValue shouldBe child {} - } - child { + shorthandMemberValue { - it::getMemberValue shouldBe child { - stringLit("\"Beware the ides of March.\"") - } - } - child { + child { + annotation { - it::getMemberValue shouldBe child { - stringLit("\"Look both ways\"") - stringLit("\"Before Crossing\"") + it::getMemberList shouldBe child { + shorthandMemberValue { + child {} + } + } + } + annotation { + it::getMemberList shouldBe child { + memberValuePair("value") { + it::isShorthand shouldBe false + child { + stringLit("\"Beware the ides of March.\"") + } + } + } + } + annotation { + it::getMemberList shouldBe child { + shorthandMemberValue { + child { + stringLit("\"Look both ways\"") + stringLit("\"Before Crossing\"") + } + } + } + } } } } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt index f347079cc6..a67e981a01 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTArrayTypeTest.kt @@ -36,7 +36,7 @@ class ASTArrayTypeTest : ParserTestSpec({ arrayType { it::getElementType shouldBe classType("ArrayTypes") - it::getDeclaredAnnotations shouldBe fromChild> { + it::declaredAnnotationsList shouldBe fromChild> { arrayDim { } arrayDim { } @@ -44,7 +44,7 @@ class ASTArrayTypeTest : ParserTestSpec({ val lst = listOf(annotation("A")) - it::getDeclaredAnnotations shouldBe lst + it::declaredAnnotationsList shouldBe lst lst } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt index d2f47d13d3..b6e12c936f 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTCastExpressionTest.kt @@ -56,11 +56,11 @@ class ASTCastExpressionTest : ParserTestSpec({ castExpr { it::getCastType shouldBe child { - it::getDeclaredAnnotations shouldBe emptyList() + it::declaredAnnotationsList shouldBe emptyList() classType("Foo") { // annotations nest on the inner node - it::getDeclaredAnnotations shouldBe listOf(annotation("F")) + it::declaredAnnotationsList shouldBe listOf(annotation("F")) } classType("Bar") @@ -76,15 +76,15 @@ class ASTCastExpressionTest : ParserTestSpec({ castExpr { it::getCastType shouldBe child { - it::getDeclaredAnnotations shouldBe emptyList() + it::declaredAnnotationsList shouldBe emptyList() classType("Foo") { // annotations nest on the inner node - it::getDeclaredAnnotations shouldBe listOf(annotation("F")) + it::declaredAnnotationsList shouldBe listOf(annotation("F")) } classType("Bar") { - it::getDeclaredAnnotations shouldBe listOf(annotation("B"), annotation("C")) + it::declaredAnnotationsList shouldBe listOf(annotation("B"), annotation("C")) } } @@ -140,3 +140,6 @@ class ASTCastExpressionTest : ParserTestSpec({ }) + +val Annotatable.declaredAnnotationsList: List + get() = declaredAnnotations.toList() diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt index 7d3f766039..018b289557 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclarationTest.kt @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.java.ast +import io.kotlintest.shouldBe import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType @@ -52,7 +53,7 @@ class ASTConstructorDeclarationTest : ParserTestSpec({ } } - it::toList shouldBe listOf( + it.toList() shouldBe listOf( child { localVarModifiers { } primitiveType(PrimitiveType.INT) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt index 0f13861daf..2fbab99481 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTEnumConstantTest.kt @@ -168,7 +168,7 @@ class ASTEnumConstantTest : ParserTestSpec({ val c = it it::getModifiers shouldBe modifiers { - c::getDeclaredAnnotations shouldBe listOf(annotation("C")) + c::declaredAnnotationsList shouldBe listOf(annotation("C")) } @@ -184,7 +184,7 @@ class ASTEnumConstantTest : ParserTestSpec({ val c = it it::getModifiers shouldBe modifiers { - c::getDeclaredAnnotations shouldBe listOf(annotation("A"), annotation("a")) + c::declaredAnnotationsList shouldBe listOf(annotation("A"), annotation("a")) } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt index c2f6373742..6b6786b3d7 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclarationTest.kt @@ -1,6 +1,7 @@ package net.sourceforge.pmd.lang.java.ast import io.kotlintest.should +import io.kotlintest.shouldBe import io.kotlintest.shouldNot import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType.PrimitiveType @@ -399,7 +400,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ it::getResultType shouldBe voidResult() it::getFormalParameters shouldBe formalsList(0) { - it::toList shouldBe emptyList() + it.toList() shouldBe emptyList() it::getReceiverParameter shouldBe child { classType("Foo") { @@ -434,7 +435,7 @@ class ASTMethodDeclarationTest : ParserTestSpec({ } } - it::toList shouldBe listOf( + it.toList() shouldBe listOf( child { localVarModifiers { } primitiveType(PrimitiveType.INT) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParserTestSpec.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParserTestSpec.kt index d3b80fb0fb..e10e474da1 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParserTestSpec.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/ParserTestSpec.kt @@ -125,6 +125,13 @@ abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : AbstractSpec(), inner class VersionedTestCtx(private val context: TestContext, javaVersion: JavaVersion) : ParserTestCtx(javaVersion) { + + fun doTest(name: String, assertions: VersionedTestCtx.() -> Unit) { + containedParserTestImpl(context, name, javaVersion = javaVersion) { + assertions() + } + } + infix fun String.should(matcher: Assertions) { containedParserTestImpl(context, "'$this'", javaVersion = javaVersion) { this@should kotlintestShould matcher diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt index 324448d9b5..016fbeecb9 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TestExtensions.kt @@ -485,8 +485,14 @@ fun TreeNodeWrapper.ambiguousName(image: String, contents: NodeSpec.memberValuePair(name: String, contents: ValuedNodeSpec) = child { - it::getMemberName shouldBe name - it::getMemberValue shouldBe contents() + it::getName shouldBe name + it::getValue shouldBe contents() + } + +fun TreeNodeWrapper.shorthandMemberValue(contents: ValuedNodeSpec) = + memberValuePair("value") { + it::isShorthand shouldBe true + contents() } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ArraySymbolTests.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ArraySymbolTests.kt index 88b29c7d9d..3ca2ac404b 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ArraySymbolTests.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ArraySymbolTests.kt @@ -8,8 +8,7 @@ import net.sourceforge.pmd.lang.java.ast.JavaNode import net.sourceforge.pmd.lang.java.symbols.JAccessibleElementSymbol.PRIMITIVE_PACKAGE import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory.INT_SYM -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory.STRING_SYM +import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.* /** * @author Clément Fournier diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/AstSymbolTests.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/AstSymbolTests.kt new file mode 100644 index 0000000000..48ef837a64 --- /dev/null +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/AstSymbolTests.kt @@ -0,0 +1,409 @@ +package net.sourceforge.pmd.lang.java.symbols.internal + +import io.kotlintest.matchers.collections.shouldBeEmpty +import io.kotlintest.matchers.collections.shouldHaveSize +import io.kotlintest.shouldBe +import net.sourceforge.pmd.lang.ast.test.* +import net.sourceforge.pmd.lang.ast.test.shouldBe +import net.sourceforge.pmd.lang.java.ast.* +import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol +import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol +import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals +import java.lang.reflect.Modifier + +/** + * @author Clément Fournier + * @since 7.0.0 + */ +class AstSymbolTests : ParserTestSpec({ + + parserTest("Parsed class symbols") { + + val acu = parser.withProcessing().parse(""" + + package com.foo; + + public final class Foo extends java.util.List { + + void bar() { + + } + + private void ohio() { + + } + + private interface Inner {} + + + enum EE { + A, + B + } + } + """) + + val (fooClass, innerItf, innerEnum) = acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.symbol } + + val (barM, ohioM) = acu.descendants(ASTMethodDeclaration::class.java).toList { it.symbol } + + + + doTest("should reflect their modifiers") { + fooClass::getModifiers shouldBe (Modifier.PUBLIC or Modifier.FINAL) + innerItf::getModifiers shouldBe (Modifier.PRIVATE or Modifier.ABSTRACT or Modifier.STATIC) + innerEnum::getModifiers shouldBe (Modifier.FINAL or Modifier.STATIC) + } + + doTest("should reflect their simple names properly") { + fooClass::getSimpleName shouldBe "Foo" + innerItf::getSimpleName shouldBe "Inner" + innerEnum::getSimpleName shouldBe "EE" + } + + doTest("should reflect their canonical names properly") { + fooClass::getCanonicalName shouldBe "com.foo.Foo" + innerItf::getCanonicalName shouldBe "com.foo.Foo.Inner" + innerEnum::getCanonicalName shouldBe "com.foo.Foo.EE" + } + + doTest("should reflect their methods") { + fooClass.declaredMethods shouldBe listOf(barM, ohioM) + } + + doTest("should fetch their methods properly") { + fooClass.getDeclaredMethods("ohio") shouldBe listOf(ohioM) + } + + doTest("should reflect their super class") { + // Postponed + // fooClass::getSuperclass shouldBe testSymFactory.getClassSymbol(java.util.List::class.java) + + innerEnum::getSuperclass shouldBe ReflectSymInternals.ENUM_SYM + } + + doTest("should reflect their member types") { + fooClass.declaredClasses shouldBe listOf(innerItf, innerEnum) + innerEnum.declaredClasses shouldBe emptyList() + innerItf.declaredClasses shouldBe emptyList() + } + + doTest("should reflect their declaring type") { + fooClass::getEnclosingClass shouldBe null + innerEnum::getEnclosingClass shouldBe fooClass + innerItf::getEnclosingClass shouldBe fooClass + } + + doTest("(enums) should reflect enum constants as fields") { + val constants = + acu.descendants(ASTEnumDeclaration::class.java) + .take(1) + .flatMap { it.enumConstants } + .toList { it.varId.symbol as JFieldSymbol } + + constants.shouldHaveSize(2) + + innerEnum.declaredFields.shouldBe(constants) + + constants.forEach { + it::isEnumConstant shouldBe true + it::getModifiers shouldBe (Modifier.PUBLIC or Modifier.STATIC or Modifier.FINAL) + } + } + } + + + parserTest("Default/implicit constructors") { + + val acu = parser.withProcessing().parse(""" + + package com.foo; + + public final class Foo extends java.util.List { + + private interface Inner {} + private @interface Annot {} + + + enum EE { A, B } + enum E2 { + A, B; + E2(String s) {} + } + + class BClass { + protected BClass() { } + } + class CClass { + // default, package private + } + } + """) + + val (fooClass, innerItf, innerAnnot, e1, e2, bClass, cClass) = + acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.symbol } + + doTest("Annotations/itfs should have no constructors") { + innerItf.constructors.shouldBeEmpty() + innerAnnot.constructors.shouldBeEmpty() + } + + doTest("Classes should have a default constructor if there are none") { + fooClass.constructors[0].shouldBeA { + it.formalParameters.shouldBeEmpty() + it.modifiers shouldBe Modifier.PUBLIC + } + + cClass.constructors[0].shouldBeA { + it.formalParameters.shouldBeEmpty() + it.modifiers shouldBe 0 + } + } + + doTest("Classes should have no default constructor if there are explicit ones") { + + bClass.constructors[0].shouldBeA { + it.formalParameters.shouldBeEmpty() + it.modifiers shouldBe Modifier.PROTECTED + } + } + + doTest("Enums should have an implicit private constructor") { + e1.constructors.shouldHaveSize(1) + e1.constructors[0].shouldBeA { + it::getModifiers shouldBe Modifier.PRIVATE + it::getFormalParameters shouldBe emptyList() + } + } + + doTest("Enums should have no implicit constructor if there are explicit ones") { + e2.constructors.shouldHaveSize(1) + e2.constructors[0].shouldBeA { ctor -> + ctor::getModifiers shouldBe Modifier.PRIVATE + ctor.formalParameters.shouldHaveSize(1) + ctor.formalParameters[0].shouldBeA { + it::getDeclaringSymbol shouldBe ctor + it::getSimpleName shouldBe "s" + } + } + } + } + + parserTest("Enum details") { + + val acu = parser.withProcessing().parse(""" + enum EE { A, B } + enum E2 { A { } /* anon */ } + """.trimIndent()) + + val (enum, enum2, enumAnon) = + acu.descendants(ASTAnyTypeDeclaration::class.java).toList { it.symbol } + + doTest("Enums should have a values() method") { + val values = enum.getDeclaredMethods("values") + values.shouldHaveSize(1) + values[0].shouldBeA { + it::getEnclosingClass shouldBe enum + it::getModifiers shouldBe (Modifier.PUBLIC or Modifier.STATIC) + it::getSimpleName shouldBe "values" + } + } + + doTest("Enums should have a valueOf() method") { + val values = enum.getDeclaredMethods("valueOf") + values.shouldHaveSize(1) + values[0].shouldBeA { + it::getEnclosingClass shouldBe enum + it::getModifiers shouldBe (Modifier.PUBLIC or Modifier.STATIC) + it::getSimpleName shouldBe "valueOf" + } + } + + doTest("Enum constants with bodies should be static") { + enumAnon::getModifiers shouldBe Modifier.STATIC + } + } + + + parserTest("Local class symbols") { + + val acu = parser.withProcessing().parse(""" + + package com.foo; + + public final class Foo { // fooClass + + Foo() { + abstract class Loc1 {} // ctorLoc + } + + void bar() { + final class Locc {} // barLoc + } + + private void ohio() { + class Locc {} // ohioLoc + new Runnable() { // anon + { + class Locc {} // anonLoc + } + }; + } + + { + class Locc {} // initLoc + } + + static { + class Loc4 { // staticInitLoc + class LocMember {} + } + } + + } + """) + + val allTypes = acu.descendants(ASTAnyTypeDeclaration::class.java).crossFindBoundaries().toList { it.symbol } + val locals = allTypes.filter { it.isLocalClass } + val (fooClass, ctorLoc, barLoc, ohioLoc, anon, anonLoc, initLoc, staticInitLoc, locMember) = allTypes + val (ctor) = acu.descendants(ASTConstructorDeclaration::class.java).toList { it.symbol } + val (barM, ohioM) = acu.descendants(ASTMethodDeclaration::class.java).toList { it.symbol } + + + locals shouldBe listOf(ctorLoc, barLoc, ohioLoc, anonLoc, initLoc, staticInitLoc) + + doTest("should reflect their modifiers") { + ctorLoc::getModifiers shouldBe (Modifier.ABSTRACT) + barLoc::getModifiers shouldBe (Modifier.FINAL) + (locals - barLoc - ctorLoc).forEach { + it::getModifiers shouldBe 0 + } + } + + doTest("should reflect their simple names properly") { + ctorLoc::getSimpleName shouldBe "Loc1" + barLoc::getSimpleName shouldBe "Locc" + ohioLoc::getSimpleName shouldBe "Locc" + anonLoc::getSimpleName shouldBe "Locc" + initLoc::getSimpleName shouldBe "Locc" + staticInitLoc::getSimpleName shouldBe "Loc4" + } + + doTest("should have no canonical name") { + locals.forEach { + it::getCanonicalName shouldBe null + } + locMember::getCanonicalName shouldBe null + } + + doTest("should reflect their enclosing type") { + ctorLoc::getEnclosingClass shouldBe fooClass + barLoc::getEnclosingClass shouldBe fooClass + ohioLoc::getEnclosingClass shouldBe fooClass + anonLoc::getEnclosingClass shouldBe anon + initLoc::getEnclosingClass shouldBe fooClass + staticInitLoc::getEnclosingClass shouldBe fooClass + } + + doTest("should reflect their enclosing method") { + ctorLoc::getEnclosingMethod shouldBe ctor + barLoc::getEnclosingMethod shouldBe barM + ohioLoc::getEnclosingMethod shouldBe ohioM + anonLoc::getEnclosingMethod shouldBe null + initLoc::getEnclosingMethod shouldBe null + staticInitLoc::getEnclosingMethod shouldBe null + } + + } + + parserTest("Anonymous class symbols") { + + val acu = parser.withProcessing().parse(""" + + package com.foo; + + public final class Foo { // fooClass + + final Foo v = new Foo() {}; // fieldAnon + + static final Void v2 = new Foo() {}; // staticFieldAnon + + Foo() { + new Runnable() {}; // ctorAnon + } + + void bar() { + new Runnable() {}; // barAnon + } + + static void bar2() { + new Runnable() {}; // staticBarAnon + } + + { + new Runnable() {}; // initAnon + } + + static { + new Runnable() { // staticInitAnon + { + new Runnable() {}; // anonAnon + } + + class AnonMember {} // anonMember + }; + } + + } + """) + + val allTypes = acu.descendants(ASTAnyTypeDeclaration::class.java).crossFindBoundaries().toList { it.symbol } + val allAnons = allTypes.filter { it.isAnonymousClass } + val (fooClass, fieldAnon, staticFieldAnon, ctorAnon, barAnon, staticBarAnon, initAnon, staticInitAnon, anonAnon, anonMember) = allTypes + val (ctor) = acu.descendants(ASTConstructorDeclaration::class.java).toList { it.symbol } + val (barM, bar2M) = acu.descendants(ASTMethodDeclaration::class.java).toList { it.symbol } + + allAnons shouldBe (allTypes - fooClass - anonMember) + + doTest("should be static in static contexts") { + listOf(staticBarAnon, staticInitAnon, staticFieldAnon).forEach { + it::getModifiers shouldBe Modifier.STATIC + } + + listOf(ctorAnon, barAnon, initAnon, anonAnon, fieldAnon).forEach { + it::getModifiers shouldBe 0 // nonstatic + } + } + + doTest("should use the empty string as their simple name") { + allAnons.forEach { + it::getSimpleName shouldBe "" + } + } + + doTest("should have no canonical name") { + allAnons.forEach { + it::getCanonicalName shouldBe null + } + anonMember::getCanonicalName shouldBe null + } + + doTest("should reflect their enclosing type") { + (allAnons - anonAnon).forEach { + it::getEnclosingClass shouldBe fooClass + } + anonAnon::getEnclosingClass shouldBe staticInitAnon + } + + doTest("should reflect their enclosing method") { + ctorAnon::getEnclosingMethod shouldBe ctor + barAnon::getEnclosingMethod shouldBe barM + staticBarAnon::getEnclosingMethod shouldBe bar2M + anonAnon::getEnclosingMethod shouldBe null + initAnon::getEnclosingMethod shouldBe null + staticInitAnon::getEnclosingMethod shouldBe null + } + } +}) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ReflectedClassSymbolTests.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ReflectedClassSymbolTests.kt index 10aeecb383..dbe9074f67 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ReflectedClassSymbolTests.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/ReflectedClassSymbolTests.kt @@ -3,7 +3,7 @@ package net.sourceforge.pmd.lang.java.symbols.internal import io.kotlintest.shouldBe import io.kotlintest.specs.WordSpec import net.sourceforge.pmd.lang.ast.test.shouldBe -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory.INT_SYM +import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.INT_SYM /** * @author Clément Fournier diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/Utils.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/Utils.kt index 4adee6f166..209781d747 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/Utils.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/internal/Utils.kt @@ -8,8 +8,9 @@ import io.kotlintest.matchers.haveSize import io.kotlintest.properties.Gen import io.kotlintest.should import net.sourceforge.pmd.lang.java.symbols.JClassSymbol -import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory.* +import net.sourceforge.pmd.lang.java.symbols.internal.impl.ast.AstSymFactory import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ClasspathSymbolResolver +import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals.* import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectionSymFactory import net.sourceforge.pmd.lang.java.symbols.table.internal.HeaderScopesTest import java.io.File @@ -23,6 +24,7 @@ import java.util.stream.Stream fun Stream.firstOrNull(): T? = findFirst().orElse(null) val testSymFactory = ReflectionSymFactory() +val testAstSymFactory = AstSymFactory() val testSymResolver = ClasspathSymbolResolver(HeaderScopesTest::class.java.classLoader, testSymFactory) fun classSym(klass: Class<*>?) = testSymFactory.getClassSymbol(klass) diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/Utils.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/Utils.kt index d0ac8a813f..f892789bcf 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/Utils.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/symbols/table/internal/Utils.kt @@ -19,3 +19,4 @@ class TestCheckLogger : SemanticChecksLogger { } } + diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 34c928b408..651d2988d6 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -54,12 +54,16 @@ abstract class BaseParsingHelper, T : RootNode return if (version == null) language.defaultVersion else language.getVersion(version) } - val defaultVersion: LanguageVersion + val defaultVersion: LanguageVersion get() = getVersion(params.defaultVerString) protected abstract fun clone(params: Params): Self + @JvmOverloads + fun withProcessing(boolean: Boolean = true): Self = + clone(params.copy(doProcess = boolean)) + /** * Returns an instance of [Self] for which all parsing methods * default their language version to the provided [version] diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt index d3b6544c78..05e57e24eb 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt @@ -65,7 +65,7 @@ infix fun KCallable.shouldBe(expected: V?) = this.shouldEqual(expe infix fun KCallable.shouldMatch(expected: T.() -> Unit) = assertWrapper(this, expected) { n, v -> n should v } -inline fun Any?.shouldBeA(f: (T) -> Unit = {}): T { +inline fun Any?.shouldBeA(f: (T) -> Unit = {}): T { if (this is T) { f(this) return this @@ -75,3 +75,10 @@ inline fun Any?.shouldBeA(f: (T) -> Unit = {}): T { fun Stream<*>.shouldHaveSize(i: Int) { toList() should haveSize(i) } + +operator fun List.component6() = get(5) +operator fun List.component7() = get(6) +operator fun List.component8() = get(7) +operator fun List.component9() = get(8) +operator fun List.component10() = get(9) +operator fun List.component11() = get(10) diff --git a/pmd-test/.gitignore b/pmd-test/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/pmd-test/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/pmd-vm/.gitignore b/pmd-vm/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/pmd-vm/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/pmd-xml/.gitignore b/pmd-xml/.gitignore new file mode 100644 index 0000000000..b83d22266a --- /dev/null +++ b/pmd-xml/.gitignore @@ -0,0 +1 @@ +/target/