Merge branch 'master' into pmd/7.0.x

This commit is contained in:
Andreas Dangel
2020-10-24 12:23:45 +02:00
48 changed files with 1556 additions and 220 deletions

View File

@ -6,6 +6,7 @@ jobs:
build:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
if: "!contains(github.event.head_commit.message, '[skip ci]')"
strategy:
matrix:
os: [ ubuntu-latest , windows-latest , macos-latest ]

View File

@ -2,7 +2,7 @@ repository: pmd/pmd
pmd:
version: 7.0.0-SNAPSHOT
previous_version: 6.28.0
previous_version: 6.29.0
date: ??-?????-2020
release_type: major

View File

@ -246,6 +246,10 @@ the breaking API changes will be performed in 7.0.0.
an API is tagged as `@Deprecated` or not in the latest minor release. During the development of 7.0.0,
we may decide to remove some APIs that were not tagged as deprecated, though we'll try to avoid it." %}
#### 6.29.0
No changes.
#### 6.28.0
##### Deprecated API

View File

@ -19,27 +19,11 @@ This is a {{ site.pmd.release_type }} release.
### New and noteworthy
#### Renamed Rules
* The Java rule [`DoNotCallSystemExit`](https://pmd.github.io/latest/pmd_rules_java_errorprone.html#donotcallsystemexit) has been renamed to
{% rule "java/errorprone/DoNotTerminateVM" %}, since it checks for all the following calls:
`System.exit(int)`, `Runtime.exit(int)`, `Runtime.halt(int)`. All these calls terminate
the Java VM, which is bad, if the VM runs an application server which many independent applications.
### Fixed Issues
* java-errorprone
* [#2157](https://github.com/pmd/pmd/issues/2157): \[java] Improve DoNotCallSystemExit: permit call in main(), flag System.halt
### API Changes
### External Contributions
* [#2803](https://github.com/pmd/pmd/pull/2803): \[java] Improve DoNotCallSystemExit (Fixes #2157) - [Vitaly Polonetsky](https://github.com/mvitaly)
* [#2809](https://github.com/pmd/pmd/pull/2809): \[java] Move test config from file to test class - [Stefan Birkner](https://github.com/stefanbirkner)
* [#2810](https://github.com/pmd/pmd/pull/2810): \[core] Move method "renderTempFile" to XMLRendererTest - [Stefan Birkner](https://github.com/stefanbirkner)
* [#2813](https://github.com/pmd/pmd/pull/2813): \[core] Use JUnit's TemporaryFolder rule - [Stefan Birkner](https://github.com/stefanbirkner)
* [#2829](https://github.com/pmd/pmd/pull/2829): \[doc] Small correction in pmd\_report\_formats.md - [Gustavo Krieger](https://github.com/gustavopcassol)
{% endtocmaker %}

View File

@ -5,6 +5,88 @@ permalink: pmd_release_notes_old.html
Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases
## 24-October-2020 - 6.29.0
The PMD team is pleased to announce PMD 6.29.0.
This is a minor release.
### Table Of Contents
* [New and noteworthy](#new-and-noteworthy)
* [Updated Apex Support](#updated-apex-support)
* [New Rules](#new-rules)
* [Renamed Rules](#renamed-rules)
* [Deprecated Rules](#deprecated-rules)
* [Fixed Issues](#fixed-issues)
* [External Contributions](#external-contributions)
* [Stats](#stats)
### New and noteworthy
#### Updated Apex Support
* The Apex language support has been bumped to version 50 (Winter '21). All new language features are now properly
parsed and processed. Especially the [Safe Navigation Operator](https://releasenotes.docs.salesforce.com/en-us/winter21/release-notes/rn_apex_SafeNavigationOperator.htm) is now supported.
See also [Salesforce Winter '21 Release Notes](https://releasenotes.docs.salesforce.com/en-us/winter21/release-notes/rn_apex.htm)
#### New Rules
* The new Apex rule [`OperationWithLimitsInLoop`](https://pmd.github.io/pmd-6.29.0/pmd_rules_apex_performance.html#operationwithlimitsinloop) (`apex-performance`)
finds operations in loops that may hit governor limits such as DML operations, SOQL
queries and more. The rule replaces the three rules "AvoidDmlStatementsInLoops", "AvoidSoqlInLoops",
and "AvoidSoslInLoops".
#### Renamed Rules
* The Java rule [`DoNotCallSystemExit`](https://pmd.github.io/pmd-6.29.0/pmd_rules_java_errorprone.html#donotcallsystemexit) has been renamed to
[`DoNotTerminateVM`](https://pmd.github.io/pmd-6.29.0/pmd_rules_java_errorprone.html#donotterminatevm), since it checks for all the following calls:
`System.exit(int)`, `Runtime.exit(int)`, `Runtime.halt(int)`. All these calls terminate
the Java VM, which is bad, if the VM runs an application server which many independent applications.
#### Deprecated Rules
* The Apex rules [`AvoidDmlStatementsInLoops`](https://pmd.github.io/pmd-6.29.0/pmd_rules_apex_performance.html#avoiddmlstatementsinloops),
[`AvoidSoqlInLoops`](https://pmd.github.io/pmd-6.29.0/pmd_rules_apex_performance.html#avoidsoqlinloops) and [`AvoidSoslInLoops`](https://pmd.github.io/pmd-6.29.0/pmd_rules_apex_performance.html#avoidsoslinloops)
(`apex-performance`) are deprecated in favour of the new rule
[`OperationWithLimitsInLoop`](https://pmd.github.io/pmd-6.29.0/pmd_rules_apex_performance.html#operationwithlimitsinloop). The deprecated rules will be removed
with PMD 7.0.0.
### Fixed Issues
* apex
* [#2839](https://github.com/pmd/pmd/issues/2839): \[apex] Apex classes with safe navigation operator from Winter 21 (50.0) are skipped
* apex-performance
* [#1713](https://github.com/pmd/pmd/issues/1713): \[apex] Mark Database DML statements in For Loop
* core
* [#2831](https://github.com/pmd/pmd/pull/2831): \[core] Fix XMLRenderer newlines when running under IBM Java
* java-errorprone
* [#2157](https://github.com/pmd/pmd/issues/2157): \[java] Improve DoNotCallSystemExit: permit call in main(), flag System.halt
* [#2764](https://github.com/pmd/pmd/issues/2764): \[java] CloseResourceRule does not recognize multiple assignment done to resource
* miscellaneous
* [#2823](https://github.com/pmd/pmd/issues/2823): \[doc] Renamed/Moved rules are missing in documentation
* vf (Salesforce VisualForce)
* [#2765](https://github.com/pmd/pmd/issues/2765): \[vf] Attributes with dot cause a VfParseException
### External Contributions
* [#2803](https://github.com/pmd/pmd/pull/2803): \[java] Improve DoNotCallSystemExit (Fixes #2157) - [Vitaly Polonetsky](https://github.com/mvitaly)
* [#2809](https://github.com/pmd/pmd/pull/2809): \[java] Move test config from file to test class - [Stefan Birkner](https://github.com/stefanbirkner)
* [#2810](https://github.com/pmd/pmd/pull/2810): \[core] Move method "renderTempFile" to XMLRendererTest - [Stefan Birkner](https://github.com/stefanbirkner)
* [#2811](https://github.com/pmd/pmd/pull/2811): \[java] CloseResource - Fix #2764: False-negative when re-assigning variable - [Andi Pabst](https://github.com/andipabst)
* [#2813](https://github.com/pmd/pmd/pull/2813): \[core] Use JUnit's TemporaryFolder rule - [Stefan Birkner](https://github.com/stefanbirkner)
* [#2816](https://github.com/pmd/pmd/pull/2816): \[apex] Detect 'Database' method invocations inside loops - [Jeff Bartolotta](https://github.com/jbartolotta-sfdc)
* [#2829](https://github.com/pmd/pmd/pull/2829): \[doc] Small correction in pmd\_report\_formats.md - [Gustavo Krieger](https://github.com/gustavopcassol)
* [#2834](https://github.com/pmd/pmd/pull/2834): \[vf] Allow attributes with dot in Visualforce - [rmohan20](https://github.com/rmohan20)
* [#2842](https://github.com/pmd/pmd/pull/2842): \[core] Bump antlr4 from 4.7 to 4.7.2 - [Adrien Lecharpentier](https://github.com/alecharp)
* [#2865](https://github.com/pmd/pmd/pull/2865): \[java] (doc) Update ExcessiveImports example code for clarity - [Gustavo Krieger](https://github.com/gustavopcassol)
* [#2866](https://github.com/pmd/pmd/pull/2866): \[java] (doc) Fix example for CouplingBetweenObjects - [Gustavo Krieger](https://github.com/gustavopcassol)
### Stats
* 50 commits
* 23 closed tickets & PRs
* Days since last release: 27
## 26-September-2020 - 6.28.0
The PMD team is pleased to announce PMD 6.28.0.

View File

@ -13,7 +13,7 @@
</parent>
<properties>
<apex.jorje.version>2020-06-04-ba31c0</apex.jorje.version>
<apex.jorje.version>2020-09-10-5a5192</apex.jorje.version>
</properties>
<build>

View File

@ -4,6 +4,6 @@
<modelVersion>4.0.0</modelVersion>
<groupId>apex</groupId>
<artifactId>apex-jorje-lsp-minimized</artifactId>
<version>2020-06-04-ba31c0</version>
<version>2020-09-10-5a5192</version>
<description>POM was created from install:install-file</description>
</project>

View File

@ -3,10 +3,10 @@
<groupId>apex</groupId>
<artifactId>apex-jorje-lsp-minimized</artifactId>
<versioning>
<release>2020-06-04-ba31c0</release>
<release>2020-09-10-5a5192</release>
<versions>
<version>2020-06-04-ba31c0</version>
<version>2020-09-10-5a5192</version>
</versions>
<lastUpdated>20200724082242</lastUpdated>
<lastUpdated>20201022163714</lastUpdated>
</versioning>
</metadata>

View File

@ -0,0 +1,27 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.ast;
import apex.jorje.semantic.ast.compilation.InvalidDependentCompilation;
public final class ASTInvalidDependentCompilation extends AbstractApexNode<InvalidDependentCompilation> {
ASTInvalidDependentCompilation(InvalidDependentCompilation userClass) {
super(userClass);
}
@Override
protected <P, R> R acceptApexVisitor(ApexVisitor<? super P, ? extends R> visitor, P data) {
return visitor.visit(this, data);
}
@Override
public String getImage() {
String apexName = getDefiningType();
return apexName.substring(apexName.lastIndexOf('.') + 1);
}
}

View File

@ -52,4 +52,8 @@ public final class ASTReferenceExpression extends AbstractApexNode<ReferenceExpr
}
return Collections.emptyList();
}
public boolean isSafeNav() {
return node.isSafeNav();
}
}

View File

@ -16,6 +16,7 @@ import apex.jorje.data.Location;
import apex.jorje.data.Locations;
import apex.jorje.semantic.ast.AstNode;
import apex.jorje.semantic.exception.UnexpectedCodePathException;
import apex.jorje.semantic.symbol.type.TypeInfo;
abstract class AbstractApexNode<T extends AstNode> extends AbstractNodeWithTextCoordinates<AbstractApexNode<?>, ApexNode<?>> implements ApexNode<T> {
@ -166,18 +167,28 @@ abstract class AbstractApexNode<T extends AstNode> extends AbstractNodeWithTextC
}
}
private TypeInfo getDefiningTypeOrNull() {
try {
return node.getDefiningType();
} catch (UnsupportedOperationException e) {
return null;
}
}
@Override
public String getDefiningType() {
if (node.getDefiningType() != null) {
return node.getDefiningType().getApexName();
TypeInfo definingType = getDefiningTypeOrNull();
if (definingType != null) {
return definingType.getApexName();
}
return null;
}
@Override
public String getNamespace() {
if (node.getDefiningType() != null) {
return node.getDefiningType().getNamespace().toString();
TypeInfo definingType = getDefiningTypeOrNull();
if (definingType != null) {
return definingType.getNamespace().toString();
}
return null;
}

View File

@ -24,6 +24,7 @@ import apex.jorje.parser.impl.ApexLexer;
import apex.jorje.semantic.ast.AstNode;
import apex.jorje.semantic.ast.compilation.AnonymousClass;
import apex.jorje.semantic.ast.compilation.ConstructorPreamble;
import apex.jorje.semantic.ast.compilation.InvalidDependentCompilation;
import apex.jorje.semantic.ast.compilation.UserClass;
import apex.jorje.semantic.ast.compilation.UserClassMethods;
import apex.jorje.semantic.ast.compilation.UserEnum;
@ -152,6 +153,8 @@ final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> {
register(DmlUpdateStatement.class, ASTDmlUpdateStatement.class);
register(DmlUpsertStatement.class, ASTDmlUpsertStatement.class);
register(DoLoopStatement.class, ASTDoLoopStatement.class);
register(ElseWhenBlock.class, ASTElseWhenBlock.class);
register(EmptyReferenceExpression.class, ASTEmptyReferenceExpression.class);
register(Expression.class, ASTExpression.class);
register(ExpressionStatement.class, ASTExpressionStatement.class);
register(Field.class, ASTField.class);
@ -159,12 +162,15 @@ final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> {
register(FieldDeclarationStatements.class, ASTFieldDeclarationStatements.class);
register(ForEachStatement.class, ASTForEachStatement.class);
register(ForLoopStatement.class, ASTForLoopStatement.class);
register(IdentifierCase.class, ASTIdentifierCase.class);
register(IfBlockStatement.class, ASTIfBlockStatement.class);
register(IfElseBlockStatement.class, ASTIfElseBlockStatement.class);
register(IllegalStoreExpression.class, ASTIllegalStoreExpression.class);
register(InstanceOfExpression.class, ASTInstanceOfExpression.class);
register(InvalidDependentCompilation.class, ASTInvalidDependentCompilation.class);
register(JavaMethodCallExpression.class, ASTJavaMethodCallExpression.class);
register(JavaVariableExpression.class, ASTJavaVariableExpression.class);
register(LiteralCase.class, ASTLiteralCase.class);
register(LiteralExpression.class, ASTLiteralExpression.class);
register(MapEntryNode.class, ASTMapEntryNode.class);
register(Method.class, ASTMethod.class);
@ -199,29 +205,25 @@ final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> {
register(StatementExecuted.class, ASTStatementExecuted.class);
register(SuperMethodCallExpression.class, ASTSuperMethodCallExpression.class);
register(SuperVariableExpression.class, ASTSuperVariableExpression.class);
register(SwitchStatement.class, ASTSwitchStatement.class);
register(TernaryExpression.class, ASTTernaryExpression.class);
register(ThisMethodCallExpression.class, ASTThisMethodCallExpression.class);
register(ThisVariableExpression.class, ASTThisVariableExpression.class);
register(ThrowStatement.class, ASTThrowStatement.class);
register(TriggerVariableExpression.class, ASTTriggerVariableExpression.class);
register(TryCatchFinallyBlockStatement.class, ASTTryCatchFinallyBlockStatement.class);
register(TypeWhenBlock.class, ASTTypeWhenBlock.class);
register(UserClass.class, ASTUserClass.class);
register(UserClassMethods.class, ASTUserClassMethods.class);
register(UserExceptionMethods.class, ASTUserExceptionMethods.class);
register(UserEnum.class, ASTUserEnum.class);
register(UserInterface.class, ASTUserInterface.class);
register(UserTrigger.class, ASTUserTrigger.class);
register(ValueWhenBlock.class, ASTValueWhenBlock.class);
register(VariableDeclaration.class, ASTVariableDeclaration.class);
register(VariableDeclarationStatements.class, ASTVariableDeclarationStatements.class);
register(VariableExpression.class, ASTVariableExpression.class);
register(WhileLoopStatement.class, ASTWhileLoopStatement.class);
register(SwitchStatement.class, ASTSwitchStatement.class);
register(ElseWhenBlock.class, ASTElseWhenBlock.class);
register(TypeWhenBlock.class, ASTTypeWhenBlock.class);
register(ValueWhenBlock.class, ASTValueWhenBlock.class);
register(LiteralCase.class, ASTLiteralCase.class);
register(IdentifierCase.class, ASTIdentifierCase.class);
register(EmptyReferenceExpression.class, ASTEmptyReferenceExpression.class);
}
private static <T extends AstNode> void register(Class<T> nodeType, Class<? extends AbstractApexNode<T>> nodeAdapterType) {

View File

@ -168,6 +168,10 @@ public interface ApexVisitor<P, R> extends AstVisitor<P, R> {
return visitApexNode(node, data);
}
default R visit(ASTInvalidDependentCompilation node, P data) {
return visitApexNode(node, data);
}
default R visit(ASTJavaMethodCallExpression node, P data) {
return visitApexNode(node, data);
}

View File

@ -353,4 +353,9 @@ class TestAccessEvaluator implements AccessEvaluator {
public boolean hasNamespaceGuardedAccess(Namespace namespace, String arg1) {
return false;
}
@Override
public boolean isNamespaceGuardNamespace(Namespace arg0) {
return false;
}
}

View File

@ -38,6 +38,7 @@ import net.sourceforge.pmd.lang.apex.ast.ApexNode;
@InternalApi
public final class Helper {
public static final String ANY_METHOD = "*";
private static final String DATABASE_CLASS_NAME = "Database";
private Helper() {
throw new AssertionError("Can't instantiate helper classes");
@ -165,10 +166,10 @@ public final class Helper {
public static boolean isSystemLevelClass(ASTUserClass node) {
List<String> interfaces = node.getInterfaceNames();
return interfaces.stream().anyMatch(Helper::isWhitelisted);
return interfaces.stream().anyMatch(Helper::isAllowed);
}
private static boolean isWhitelisted(String identifier) {
private static boolean isAllowed(String identifier) {
switch (identifier.toLowerCase(Locale.ROOT)) {
case "queueable":
case "database.batchable":
@ -186,4 +187,10 @@ public final class Helper {
return sb.toString();
}
/**
* @return true if {@code node} is an invocation of a {@code Database} method.
*/
public static boolean isAnyDatabaseMethodCall(ASTMethodCallExpression node) {
return isMethodName(node, DATABASE_CLASS_NAME, ANY_METHOD);
}
}

View File

@ -0,0 +1,62 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule.performance;
import net.sourceforge.pmd.lang.apex.ast.ASTBlockStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDoLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForEachStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTReturnStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ApexNode;
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
import net.sourceforge.pmd.lang.ast.Node;
/**
* Base class for any rules that detect operations contained within a loop that could be more efficiently executed by
* refactoring the code into a batched execution.
*/
abstract class AbstractAvoidNodeInLoopsRule extends AbstractApexRule {
/**
* Adds a violation if any parent of {@code node} is a looping construct that would cause {@code node} to execute
* multiple times and {@code node} is not part of a return statement that short circuits the loop.
*/
protected Object checkForViolation(ApexNode<?> node, Object data) {
if (insideLoop(node) && parentNotReturn(node)) {
addViolation(data, node);
}
return data;
}
/**
* @return false if {@code node} is a direct child of a return statement. Children of return statements should not
* result in a violation because the return short circuits the loop's execution.
*/
private boolean parentNotReturn(ApexNode<?> node) {
return !(node.getParent() instanceof ASTReturnStatement);
}
/**
* @return true if any parent of {@code node} is a construct that would cause {@code node} to execute multiple
* times.
*/
private boolean insideLoop(Node node) {
Node n = node.getParent();
while (n != null) {
if (n instanceof ASTBlockStatement && n.getParent() instanceof ASTForEachStatement) {
// only consider the block of the for-each statement, not the iterator
return true;
}
if (n instanceof ASTDoLoopStatement || n instanceof ASTWhileLoopStatement
|| n instanceof ASTForLoopStatement) {
return true;
}
n = n.getParent();
}
return false;
}
}

View File

@ -10,74 +10,40 @@ import net.sourceforge.pmd.lang.apex.ast.ASTDmlMergeStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlUndeleteStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDoLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForEachStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement;
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
import net.sourceforge.pmd.lang.ast.Node;
public class AvoidDmlStatementsInLoopsRule extends AbstractApexRule {
/**
* @deprecated use {@link OperationWithLimitsInLoopRule}
*/
@Deprecated
public class AvoidDmlStatementsInLoopsRule extends AbstractAvoidNodeInLoopsRule {
@Override
public Object visit(ASTDmlDeleteStatement node, Object data) {
if (insideLoop(node)) {
addViolation(data, node);
}
return data;
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlInsertStatement node, Object data) {
if (insideLoop(node)) {
addViolation(data, node);
}
return data;
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlMergeStatement node, Object data) {
if (insideLoop(node)) {
addViolation(data, node);
}
return data;
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlUndeleteStatement node, Object data) {
if (insideLoop(node)) {
addViolation(data, node);
}
return data;
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlUpdateStatement node, Object data) {
if (insideLoop(node)) {
addViolation(data, node);
}
return data;
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlUpsertStatement node, Object data) {
if (insideLoop(node)) {
addViolation(data, node);
}
return data;
}
private boolean insideLoop(Node node) {
Node n = node.getParent();
while (n != null) {
if (n instanceof ASTDoLoopStatement || n instanceof ASTWhileLoopStatement
|| n instanceof ASTForLoopStatement || n instanceof ASTForEachStatement) {
return true;
}
n = n.getParent();
}
return false;
return checkForViolation(node, data);
}
}

View File

@ -4,45 +4,16 @@
package net.sourceforge.pmd.lang.apex.rule.performance;
import net.sourceforge.pmd.lang.apex.ast.ASTBlockStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDoLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForEachStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTReturnStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTSoqlExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement;
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
import net.sourceforge.pmd.lang.ast.Node;
public class AvoidSoqlInLoopsRule extends AbstractApexRule {
/**
* @deprecated use {@link OperationWithLimitsInLoopRule}
*/
@Deprecated
public class AvoidSoqlInLoopsRule extends AbstractAvoidNodeInLoopsRule {
@Override
public Object visit(ASTSoqlExpression node, Object data) {
if (insideLoop(node) && parentNotReturn(node)) {
addViolation(data, node);
}
return data;
}
private boolean parentNotReturn(ASTSoqlExpression node) {
return !(node.getParent() instanceof ASTReturnStatement);
}
private boolean insideLoop(ASTSoqlExpression node) {
Node n = node.getParent();
while (n != null) {
if (n instanceof ASTBlockStatement && n.getParent() instanceof ASTForEachStatement) {
// only consider the block of the for-each statement, not the iterator
return true;
}
if (n instanceof ASTDoLoopStatement || n instanceof ASTWhileLoopStatement
|| n instanceof ASTForLoopStatement) {
return true;
}
n = n.getParent();
}
return false;
return checkForViolation(node, data);
}
}

View File

@ -4,44 +4,16 @@
package net.sourceforge.pmd.lang.apex.rule.performance;
import net.sourceforge.pmd.lang.apex.ast.ASTDoLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForEachStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTForLoopStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTReturnStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTSoslExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTWhileLoopStatement;
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
import net.sourceforge.pmd.lang.ast.Node;
public class AvoidSoslInLoopsRule extends AbstractApexRule {
/**
* @deprecated use {@link OperationWithLimitsInLoopRule}
*/
@Deprecated
public class AvoidSoslInLoopsRule extends AbstractAvoidNodeInLoopsRule {
@Override
public Object visit(ASTSoslExpression node, Object data) {
if (insideLoop(node) && parentNotReturn(node) && parentNotForEach(node)) {
addViolation(data, node);
}
return data;
}
private boolean parentNotReturn(ASTSoslExpression node) {
return !(node.getParent() instanceof ASTReturnStatement);
}
private boolean parentNotForEach(ASTSoslExpression node) {
return !(node.getParent() instanceof ASTForEachStatement);
}
private boolean insideLoop(ASTSoslExpression node) {
Node n = node.getParent();
while (n != null) {
if (n instanceof ASTDoLoopStatement || n instanceof ASTWhileLoopStatement
|| n instanceof ASTForLoopStatement || n instanceof ASTForEachStatement) {
return true;
}
n = n.getParent();
}
return false;
return checkForViolation(node, data);
}
}

View File

@ -0,0 +1,101 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule.performance;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlDeleteStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlInsertStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlMergeStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlUndeleteStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement;
import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTSoqlExpression;
import net.sourceforge.pmd.lang.apex.ast.ASTSoslExpression;
import net.sourceforge.pmd.lang.apex.rule.internal.Helper;
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
/**
* Warn users when code that could trigger governor limits is executing within a looping construct.
*/
public class OperationWithLimitsInLoopRule extends AbstractAvoidNodeInLoopsRule {
@Override
protected @NonNull RuleTargetSelector buildTargetSelector() {
return RuleTargetSelector.forTypes(
// DML
ASTDmlDeleteStatement.class,
ASTDmlInsertStatement.class,
ASTDmlMergeStatement.class,
ASTDmlUndeleteStatement.class,
ASTDmlUpdateStatement.class,
ASTDmlUpsertStatement.class,
// Database methods
ASTMethodCallExpression.class,
// SOQL
ASTSoqlExpression.class,
// SOSL
ASTSoslExpression.class
);
}
// Begin DML Statements
@Override
public Object visit(ASTDmlDeleteStatement node, Object data) {
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlInsertStatement node, Object data) {
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlMergeStatement node, Object data) {
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlUndeleteStatement node, Object data) {
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlUpdateStatement node, Object data) {
return checkForViolation(node, data);
}
@Override
public Object visit(ASTDmlUpsertStatement node, Object data) {
return checkForViolation(node, data);
}
// End DML Statements
// Begin Database method invocations
@Override
public Object visit(ASTMethodCallExpression node, Object data) {
if (Helper.isAnyDatabaseMethodCall(node)) {
return checkForViolation(node, data);
} else {
return data;
}
}
// End Database method invocations
// Begin SOQL method invocations
@Override
public Object visit(ASTSoqlExpression node, Object data) {
return checkForViolation(node, data);
}
// End SOQL method invocations
// Begin SOSL method invocations
@Override
public Object visit(ASTSoslExpression node, Object data) {
return checkForViolation(node, data);
}
// End SOSL method invocations
}

View File

@ -103,7 +103,7 @@ public class ApexSharingViolationsRule extends AbstractApexRule {
@Override
public Object visit(ASTMethodCallExpression node, Object data) {
if (Helper.isMethodName(node, "Database", Helper.ANY_METHOD)) {
if (Helper.isAnyDatabaseMethodCall(node)) {
checkForViolation(node, data);
}

View File

@ -12,11 +12,15 @@ Rules that flag suboptimal code.
<rule name="AvoidDmlStatementsInLoops"
language="apex"
since="5.5.0"
deprecated="true"
message="Avoid DML statements inside loops"
class="net.sourceforge.pmd.lang.apex.rule.performance.AvoidDmlStatementsInLoopsRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#avoiddmlstatementsinloops">
<description>
Avoid DML statements inside loops to avoid hitting the DML governor limit. Instead, try to batch up the data into a list and invoke your DML once on that list of data outside the loop.
This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced
by the more general rule {% rule "apex/performance/OperationWithLimitsInLoop" %}.
</description>
<priority>3</priority>
<example>
@ -37,11 +41,15 @@ public class Something {
<rule name="AvoidSoqlInLoops"
language="apex"
since="5.5.0"
deprecated="true"
message="Avoid Soql queries inside loops"
class="net.sourceforge.pmd.lang.apex.rule.performance.AvoidSoqlInLoopsRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#avoidsoqlinloops">
<description>
New objects created within loops should be checked to see if they can created outside them and reused.
This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced
by the more general rule {% rule "apex/performance/OperationWithLimitsInLoop" %}.
</description>
<priority>3</priority>
<example>
@ -60,11 +68,15 @@ public class Something {
<rule name="AvoidSoslInLoops"
language="apex"
since="6.0.0"
deprecated="true"
message="Avoid Sosl queries inside loops"
class="net.sourceforge.pmd.lang.apex.rule.performance.AvoidSoslInLoopsRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#avoidsoslinloops">
<description>
Sosl calls within loops can cause governor limit exceptions.
This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced
by the more general rule {% rule "apex/performance/OperationWithLimitsInLoop" %}.
</description>
<priority>3</priority>
<example>
@ -80,4 +92,46 @@ public class Something {
</example>
</rule>
<rule name="OperationWithLimitsInLoop"
language="apex"
since="6.29.0"
message="Avoid operations in loops that may hit governor limits"
class="net.sourceforge.pmd.lang.apex.rule.performance.OperationWithLimitsInLoopRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#operationwithlimitsinloop">
<description>
Database class methods, DML operations, SOQL queries, or SOSL queries within loops can cause governor limit exceptions. Instead, try to batch up the data into a list and invoke the operation once on that list of data outside the loop.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Something {
public void databaseMethodInsideOfLoop(List<Account> accounts) {
for (Account a : accounts) {
Database.insert(a);
}
}
public void dmlInsideOfLoop() {
for (Integer i = 0; i < 151; i++) {
Account account;
// ...
insert account;
}
}
public void soqlInsideOfLoop() {
for (Integer i = 0; i < 10; i++) {
List<Account> accounts = [SELECT Id FROM Account];
}
}
public void soslInsideOfLoop() {
for (Integer i = 0; i < 10; i++) {
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
}
}
}
]]>
</example>
</rule>
</ruleset>

View File

@ -66,13 +66,7 @@
<!-- <rule ref="category/apex/design.xml/CognitiveComplexity"/> -->
<!-- PERFORMANCE -->
<rule ref="category/apex/performance.xml/AvoidSoqlInLoops" message="Avoid Soql queries inside loops">
<priority>3</priority>
</rule>
<rule ref="category/apex/performance.xml/AvoidSoslInLoops" message="Avoid Sosl queries inside loops">
<priority>3</priority>
</rule>
<rule ref="category/apex/performance.xml/AvoidDmlStatementsInLoops" message="Avoid DML Statements inside loops">
<rule ref="category/apex/performance.xml/OperationWithLimitsInLoop" message="Avoid operations in loops that may hit governor limits">
<priority>3</priority>
</rule>
<rule ref="category/apex/errorprone.xml/AvoidDirectAccessTriggerMap" message="Avoid directly accessing Trigger.old and Trigger.new">

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Default ruleset for Salesforce.com Apex" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>Default ruleset for Salesforce.com Apex</description>
<ruleset xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Default ruleset used by the CodeClimate Engine for Salesforce.com Apex" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Default ruleset used by the Code Climate Engine for Salesforce.com Apex
Note: This ruleset is deprecated. Use "rulesets/apex/quickstart.xml" instead.
</description>
<rule ref="rulesets/apex/quickstart.xml/ExcessiveClassLength" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/ExcessiveParameterList" deprecated="true" />
@ -12,9 +16,9 @@
<rule ref="rulesets/apex/quickstart.xml/TooManyFields" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/AvoidDeeplyNestedIfStmts" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/CyclomaticComplexity" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/AvoidSoqlInLoops" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/AvoidSoslInLoops" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/AvoidDmlStatementsInLoops" deprecated="true" />
<rule ref="category/apex/performance.xml/AvoidSoqlInLoops" deprecated="true" />
<rule ref="category/apex/performance.xml/AvoidSoslInLoops" deprecated="true" />
<rule ref="category/apex/performance.xml/AvoidDmlStatementsInLoops" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/AvoidDirectAccessTriggerMap" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/AvoidLogicInTrigger" deprecated="true" />
<rule ref="rulesets/apex/quickstart.xml/AvoidGlobalModifier" deprecated="true" />

View File

@ -17,13 +17,13 @@ import org.junit.contrib.java.lang.system.SystemErrRule;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.util.ResourceLoader;
import net.sourceforge.pmd.RulesetsFactoryUtils;
public class DefaultRulesetTest {
@Rule
public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests();
private RuleSetFactory factory = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, false);
private RuleSetFactory factory = RulesetsFactoryUtils.createFactory(RulePriority.LOW, true, false);
@Test
public void loadDefaultRuleset() throws Exception {

View File

@ -0,0 +1,29 @@
/*
* 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.test.BaseParsingHelper;
import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest;
import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter;
public class ApexTreeDumpTest extends BaseTreeDumpTest {
public ApexTreeDumpTest() {
super(new RelevantAttributePrinter(), ".cls");
}
@Override
public BaseParsingHelper<?, ?> getParser() {
return ApexParsingHelper.DEFAULT;
}
@Test
public void safeNavigationOperator() throws Exception {
doTest("SafeNavigationOperator");
}
}

View File

@ -0,0 +1,11 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.apex.rule.performance;
import net.sourceforge.pmd.testframework.PmdRuleTst;
public class OperationWithLimitsInLoopTest extends PmdRuleTst {
// no additional unit tests
}

View File

@ -0,0 +1,25 @@
// See https://github.com/pmd/pmd/issues/2839
// and https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_SafeNavigationOperator.htm
public class Foo {
Integer x = anObject?.anIntegerField; // The expression is of type Integer because the field is of type Integer
// New code using the safe navigation operator
String profileUrl = user.getProfileUrl()?.toExternalForm();
public void bar1(Object a) {
a?.b; // Evaluates to: a == null ? null : a.b
((T)a1?.b1)?.c1();
}
public void bar2(Object[] a, int x) {
a[x]?.aMethod().aField; // Evaluates to null if a[x] == null
a[x].aMethod()?.aField;
}
public String getName(int accId) {
String s = contact.Account?.BillingCity;
// New code using the safe navigation operator
return [SELECT Name FROM Account WHERE Id = :accId]?.Name;
}
}

View File

@ -0,0 +1,99 @@
+- ApexFile[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(4, 14, 180, 183)", @Namespace = "", @RealLoc = "true"]
+- UserClass[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "Foo", @Location = "(4, 14, 180, 183)", @Namespace = "", @RealLoc = "true", @SuperClassName = "", @TypeKind = "CLASS"]
+- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(4, 14, 180, 183)", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
+- Field[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Location = "(5, 13, 198, 199)", @Name = "x", @Namespace = "", @RealLoc = "true", @Type = "Integer", @Value = null]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(5, 13, 198, 199)", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
+- Field[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "profileUrl", @Location = "(8, 12, 365, 375)", @Name = "profileUrl", @Namespace = "", @RealLoc = "true", @Type = "String", @Value = null]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(8, 12, 365, 375)", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
+- FieldDeclarationStatements[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(5, 5, 190, 199)", @Namespace = "", @RealLoc = "true", @TypeName = "Integer"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "no location", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- FieldDeclaration[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "anIntegerField", @Location = "(5, 13, 198, 199)", @Name = "anIntegerField", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "anIntegerField", @Location = "(5, 27, 212, 226)", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "anObject", @Location = "(5, 17, 202, 210)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Location = "(5, 13, 198, 199)", @Namespace = "", @RealLoc = "true"]
| +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
+- FieldDeclarationStatements[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(8, 5, 358, 375)", @Namespace = "", @RealLoc = "true", @TypeName = "String"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "no location", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- FieldDeclaration[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "profileUrl", @Location = "(8, 12, 365, 375)", @Name = "profileUrl", @Namespace = "", @RealLoc = "true"]
| +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "toExternalForm", @InputParametersSize = "0", @Location = "(8, 47, 400, 414)", @MethodName = "toExternalForm", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"]
| | +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "user.getProfileUrl", @InputParametersSize = "0", @Location = "(8, 30, 383, 396)", @MethodName = "getProfileUrl", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Image = "user", @Location = "(8, 25, 378, 382)", @Namespace = "", @RealLoc = "true", @ReferenceType = "METHOD", @SafeNav = "false"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "profileUrl", @Location = "(8, 12, 365, 375)", @Namespace = "", @RealLoc = "true"]
| +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
+- Method[@ApexVersion = "51.0", @Arity = "1", @CanonicalName = "bar1", @Constructor = "false", @DefiningType = "Foo", @Image = "bar1", @Location = "(10, 17, 435, 439)", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(10, 17, 435, 439)", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Location = "(10, 29, 447, 448)", @Namespace = "", @RealLoc = "true", @Type = "Object"]
| | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(10, 29, 447, 448)", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- BlockStatement[@ApexVersion = "51.0", @CurlyBrace = "true", @DefiningType = "Foo", @Location = "(10, 32, 450, 538)", @Namespace = "", @RealLoc = "true"]
| +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(11, 12, 463, 465)", @Namespace = "", @RealLoc = "true"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "b", @Location = "(11, 12, 463, 464)", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Location = "(11, 9, 460, 461)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(12, 22, 527, 532)", @Namespace = "", @RealLoc = "true"]
| +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "c1", @InputParametersSize = "0", @Location = "(12, 22, 527, 529)", @MethodName = "c1", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"]
| +- CastExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(12, 10, 515, 518)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "b1", @Location = "(12, 17, 522, 524)", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a1", @Location = "(12, 13, 518, 520)", @Namespace = "", @RealLoc = "true"]
| +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
+- Method[@ApexVersion = "51.0", @Arity = "2", @CanonicalName = "bar2", @Constructor = "false", @DefiningType = "Foo", @Image = "bar2", @Location = "(15, 17, 556, 560)", @Namespace = "", @RealLoc = "true", @ReturnType = "void", @Synthetic = "false"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(15, 17, 556, 560)", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Location = "(15, 31, 570, 571)", @Namespace = "", @RealLoc = "true", @Type = "List<Object>"]
| | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(15, 31, 570, 571)", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Location = "(15, 38, 577, 578)", @Namespace = "", @RealLoc = "true", @Type = "int"]
| | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(15, 38, 577, 578)", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- BlockStatement[@ApexVersion = "51.0", @CurlyBrace = "true", @DefiningType = "Foo", @Location = "(15, 41, 580, 688)", @Namespace = "", @RealLoc = "true"]
| +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(16, 25, 606, 613)", @Namespace = "", @RealLoc = "true"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "aField", @Location = "(16, 25, 606, 612)", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "false"]
| | +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @Location = "(16, 15, 596, 603)", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"]
| | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "true"]
| | +- ArrayLoadExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(16, 9, 590, 591)", @Namespace = "", @RealLoc = "true"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Location = "(16, 9, 590, 591)", @Namespace = "", @RealLoc = "true"]
| | | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Location = "(16, 11, 592, 593)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- ExpressionStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(17, 25, 675, 682)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "aField", @Location = "(17, 25, 675, 681)", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| +- MethodCallExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @FullMethodName = "aMethod", @InputParametersSize = "0", @Location = "(17, 14, 664, 671)", @MethodName = "aMethod", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "METHOD", @SafeNav = "false"]
| +- ArrayLoadExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(17, 9, 659, 660)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "a", @Location = "(17, 9, 659, 660)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "x", @Location = "(17, 11, 661, 662)", @Namespace = "", @RealLoc = "true"]
| +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
+- Method[@ApexVersion = "51.0", @Arity = "1", @CanonicalName = "getName", @Constructor = "false", @DefiningType = "Foo", @Image = "getName", @Location = "(20, 19, 708, 715)", @Namespace = "", @RealLoc = "true", @ReturnType = "String", @Synthetic = "false"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(20, 19, 708, 715)", @Modifiers = "1", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "true", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- Parameter[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "accId", @Location = "(20, 31, 720, 725)", @Namespace = "", @RealLoc = "true", @Type = "int"]
| | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(20, 31, 720, 725)", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| +- BlockStatement[@ApexVersion = "51.0", @CurlyBrace = "true", @DefiningType = "Foo", @Location = "(20, 38, 727, 905)", @Namespace = "", @RealLoc = "true"]
| +- VariableDeclarationStatements[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(21, 9, 737, 745)", @Namespace = "", @RealLoc = "true"]
| | +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "no location", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
| | +- VariableDeclaration[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "s", @Location = "(21, 16, 744, 745)", @Namespace = "", @RealLoc = "true", @Type = "String"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "BillingCity", @Location = "(21, 37, 765, 776)", @Namespace = "", @RealLoc = "true"]
| | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| | | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "Account", @Location = "(21, 28, 756, 763)", @Namespace = "", @RealLoc = "true"]
| | | +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Image = "contact", @Location = "(21, 20, 748, 755)", @Namespace = "", @RealLoc = "true", @ReferenceType = "LOAD", @SafeNav = "false"]
| | +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "s", @Location = "(21, 16, 744, 745)", @Namespace = "", @RealLoc = "true"]
| | +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
| +- ReturnStatement[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(23, 9, 841, 899)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "Name", @Location = "(23, 62, 894, 898)", @Namespace = "", @RealLoc = "true"]
| +- ReferenceExpression[@ApexVersion = "51.0", @Context = null, @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReferenceType = "LOAD", @SafeNav = "true"]
| +- SoqlExpression[@ApexVersion = "51.0", @CanonicalQuery = "SELECT Name FROM Account WHERE Id = :tmpVar1", @DefiningType = "Foo", @Location = "(23, 16, 848, 892)", @Namespace = "", @Query = "SELECT Name FROM Account WHERE Id = :accId", @RealLoc = "true"]
| +- BindExpressions[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "(23, 16, 848, 892)", @Namespace = "", @RealLoc = "true"]
| +- VariableExpression[@ApexVersion = "51.0", @DefiningType = "Foo", @Image = "accId", @Location = "(23, 54, 886, 891)", @Namespace = "", @RealLoc = "true"]
| +- EmptyReferenceExpression[@ApexVersion = "51.0", @DefiningType = null, @Location = "no location", @Namespace = null, @RealLoc = "false"]
+- Method[@ApexVersion = "51.0", @Arity = "0", @CanonicalName = "<clinit>", @Constructor = "false", @DefiningType = "Foo", @Image = "<clinit>", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReturnType = "void", @Synthetic = "true"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "false", @InheritedSharing = "false", @Location = "(4, 14, 180, 183)", @Modifiers = "8", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "true", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
+- Method[@ApexVersion = "51.0", @Arity = "0", @CanonicalName = "clone", @Constructor = "false", @DefiningType = "Foo", @Image = "clone", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReturnType = "Object", @Synthetic = "true"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "true", @InheritedSharing = "false", @Location = "no location", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "false", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
+- UserClassMethods[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false"]
| +- Method[@ApexVersion = "51.0", @Arity = "0", @CanonicalName = "<init>", @Constructor = "true", @DefiningType = "Foo", @Image = "<init>", @Location = "no location", @Namespace = "", @RealLoc = "false", @ReturnType = "void", @Synthetic = "true"]
| +- ModifierNode[@Abstract = "false", @ApexVersion = "51.0", @DefiningType = "Foo", @Final = "false", @Global = "true", @InheritedSharing = "false", @Location = "(4, 14, 180, 183)", @Modifiers = "0", @Namespace = "", @Override = "false", @Private = "false", @Protected = "false", @Public = "false", @RealLoc = "true", @Static = "false", @Test = "false", @TestOrTestSetup = "false", @Transient = "false", @WebService = "false", @WithSharing = "false", @WithoutSharing = "false"]
+- BridgeMethodCreator[@ApexVersion = "51.0", @DefiningType = "Foo", @Location = "no location", @Namespace = "", @RealLoc = "false"]

Some files were not shown because too many files have changed in this diff Show More