Skip to content

Commit

Permalink
Merge branch '637' into pmd#635
Browse files Browse the repository at this point in the history
# Conflicts:
#	pmd-apex/src/main/resources/rulesets/apex/ruleset.xml
  • Loading branch information
Jan Aertgeerts committed Oct 10, 2017
2 parents 6c1110c + 96de171 commit f381a30
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 1 deletion.
36 changes: 35 additions & 1 deletion docs/pages/pmd/rules/apex/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ permalink: pmd_rules_apex_performance.html
folder: pmd/rules/apex
sidebaractiveurl: /pmd_rules_apex.html
editmepath: ../pmd-apex/src/main/resources/rulesets/apex/performance.xml
keywords: Performance, AvoidSoqlInLoops, AvoidDmlStatementsInLoops
keywords: Performance, AvoidSoqlInLoops, AvoidSoslInLoops, AvoidDmlStatementsInLoops
---
## AvoidDmlStatementsInLoops

Expand Down Expand Up @@ -112,3 +112,37 @@ public class Something {
<rule ref="rulesets/apex/performance.xml/AvoidSoqlInLoops" />
```

## AvoidSoslInLoops

**Since:** PMD 6.0.0

**Priority:** Medium (3)

Sosl calls within loops can cause governor limit exceptions.

**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.performance.AvoidSoslInLoopsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidSoslInLoopsRule.java)

**Example(s):**

``` java
public class Something {
public static void main( String as[] ) {
for (Integer i = 0; i < 10; i++) {
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
}
}
}
```

**This rule has the following properties:**

|Name|Default Value|Description|
|----|-------------|-----------|
|cc_categories|[Style]|Code Climate Categories|
|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier|
|cc_block_highlighting|false|Code Climate Block Highlighting|

**Use this rule by referencing it:**
``` xml
<rule ref="rulesets/apex/performance.xml/AvoidSoslInLoops" />
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* 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.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 {

public AvoidSoslInLoopsRule() {
setProperty(CODECLIMATE_CATEGORIES, "Performance");
// Note: Often more complicated as just moving the SOSL a few lines.
// Involves Maps...
setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 150);
setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
}

@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.jjtGetParent() instanceof ASTReturnStatement);
}

private boolean parentNotForEach(ASTSoslExpression node) {
return !(node.jjtGetParent() instanceof ASTForEachStatement);
}

private boolean insideLoop(ASTSoslExpression node) {
Node n = node.jjtGetParent();

while (n != null) {
if (n instanceof ASTDoLoopStatement || n instanceof ASTWhileLoopStatement
|| n instanceof ASTForLoopStatement || n instanceof ASTForEachStatement) {
return true;
}
n = n.jjtGetParent();
}

return false;
}
}
22 changes: 22 additions & 0 deletions pmd-apex/src/main/resources/rulesets/apex/performance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ public class Something {
</example>
</rule>

<rule name="AvoidSoslInLoops"
since="6.0.0"
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.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Something {
public static void main( String as[] ) {
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>

<rule name="AvoidDmlStatementsInLoops"
since="5.5.0"
message="Avoid DML statements inside loops"
Expand Down
9 changes: 9 additions & 0 deletions pmd-apex/src/main/resources/rulesets/apex/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@
<property name="cc_block_highlighting" value="false" />
</properties>
</rule>
<rule ref="rulesets/apex/performance.xml/AvoidSoslInLoops" message="Avoid Sosl queries inside loops">
<priority>3</priority>
<properties>
<!-- relevant for Code Climate output only -->
<property name="cc_categories" value="Performance" />
<property name="cc_remediation_points_multiplier" value="150" />
<property name="cc_block_highlighting" value="false" />
</properties>
</rule>
<rule ref="rulesets/apex/performance.xml/AvoidNonRestrictiveQueries" message="Avoid SOQL queries without a where or limit statement">
<priority>3</priority>
<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class PerformanceRulesTest extends SimpleAggregatorTst {
public void setUp() {
addRule(RULESET, "AvoidNonRestrictiveQueries");
addRule(RULESET, "AvoidSoqlInLoops");
addRule(RULESET, "AvoidSoslInLoops");
addRule(RULESET, "AvoidDmlStatementsInLoops");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>

<test-data
xmlns="http://pmd.sourceforge.net/rule-tests"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">

<test-code>
<description>Problematic Sosl in for each</description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {
public void test1() {
for(Integer i : new List<Integer>{1,2}) {
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
}
}
}
]]></code>
</test-code>

<test-code>
<description>Problematic Sosl in for loop</description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {
public void test1() {
for(;;) {
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
}
}
}
]]></code>
</test-code>

<test-code>
<description>Problematic Sosl in While loop</description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {
public void test1() {
while(true) {
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
}
}
}
]]></code>
</test-code>

<test-code>
<description>Problematic Sosl in do loop</description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {
public void test1() {
do{
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
}while(true) ;
}
}
]]></code>
</test-code>

<test-code>
<description>Multiple problematic Sosl expressions</description>
<expected-problems>2</expected-problems>
<code><![CDATA[
public class Foo {
public void test1() {
do{
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name)];
}while(true) ;
}
}
]]></code>
</test-code>

<test-code>
<description>Return Sosl is even ok in loop</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public List<Account> test1() {
for(;;) {
return [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead];
}
}
}
]]></code>
</test-code>

<test-code>
<description>#29 SOSL For Loops should not throw an Avoid Sosl queries inside loops issue</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public void test1() {
for(List<sObject> a : [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity]) {
}
}
}
]]></code>
</test-code>

</test-data>

0 comments on commit f381a30

Please sign in to comment.