[apex] Reorganize rules into categories
This commit is contained in:
140
pmd-apex/src/main/resources/category/apex/bestpractices.xml
Normal file
140
pmd-apex/src/main/resources/category/apex/bestpractices.xml
Normal file
@ -0,0 +1,140 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Best Practices"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>
|
||||
Rules which enforce generally accepted best practices.
|
||||
</description>
|
||||
|
||||
<rule name="ApexUnitTestClassShouldHaveAsserts"
|
||||
since="5.5.1"
|
||||
message="Apex unit tests should System.assert() or assertEquals() or assertNotEquals()"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestClassShouldHaveAssertsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#apexunittestclassshouldhaveasserts">
|
||||
<description>
|
||||
Apex unit tests should include at least one assertion. This makes the tests more robust, and using assert
|
||||
with messages provide the developer a clearer idea of what the test does.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
@isTest
|
||||
public class Foo {
|
||||
public static testMethod void testSomething() {
|
||||
Account a = null;
|
||||
// This is better than having a NullPointerException
|
||||
// System.assertNotEquals(a, null, 'account not found');
|
||||
a.toString();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexUnitTestShouldNotUseSeeAllDataTrue"
|
||||
since="5.5.1"
|
||||
message="Apex unit tests should not use @isTest(seeAllData = true)"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestShouldNotUseSeeAllDataTrueRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#apexunittestshouldnotuseseealldatatrue">
|
||||
<description>
|
||||
Apex unit tests should not use @isTest(seeAllData=true) because it opens up the existing database data for unexpected modification by tests.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
@isTest(seeAllData = true)
|
||||
public class Foo {
|
||||
public static testMethod void testSomething() {
|
||||
Account a = null;
|
||||
// This is better than having a NullPointerException
|
||||
// System.assertNotEquals(a, null, 'account not found');
|
||||
a.toString();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidGlobalModifier"
|
||||
since="5.5.0"
|
||||
message="Avoid using global modifier"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.AvoidGlobalModifierRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#avoidglobalmodifier">
|
||||
<description>
|
||||
Global classes should be avoided (especially in managed packages) as they can never be deleted or changed in signature. Always check twice if something needs to be global.
|
||||
Many interfaces (e.g. Batch) required global modifiers in the past but don't require this anymore. Don't lock yourself in.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
global class Unchangeable {
|
||||
global UndeletableType unchangable(UndeletableType param) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidHardcodingId"
|
||||
since="6.0.0"
|
||||
message="Hardcoding Id's is bound to break when changing environments."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.AvoidHardcodingIdRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#avoidhardcodingid">
|
||||
<description>
|
||||
When deploying Apex code between sandbox and production environments, or installing Force.com AppExchange packages,
|
||||
it is essential to avoid hardcoding IDs in the Apex code. By doing so, if the record IDs change between environments,
|
||||
the logic can dynamically identify the proper data to operate against and not fail.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
void foo() {
|
||||
//Error - hardcoded the record type id
|
||||
if(a.RecordTypeId == '012500000009WAr'){
|
||||
//do some logic here.....
|
||||
} else if(a.RecordTypeId == '0123000000095Km'){
|
||||
//do some logic here for a different record type...
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidLogicInTrigger"
|
||||
since="5.5.0"
|
||||
message="Avoid logic in triggers"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.AvoidLogicInTriggerRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#avoidlogicintrigger">
|
||||
<description>
|
||||
As triggers do not allow methods like regular classes they are less flexible and suited to apply good encapsulation style.
|
||||
Therefore delegate the triggers work to a regular class (often called Trigger handler class).
|
||||
|
||||
See more here: https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
trigger Accounts on Account (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
|
||||
for(Account acc : Trigger.new) {
|
||||
if(Trigger.isInsert) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
if(Trigger.isDelete) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
207
pmd-apex/src/main/resources/category/apex/codestyle.xml
Normal file
207
pmd-apex/src/main/resources/category/apex/codestyle.xml
Normal file
@ -0,0 +1,207 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Codestyle"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>
|
||||
Rules which enforce a specific coding style.
|
||||
</description>
|
||||
|
||||
<rule name="ClassNamingConventions"
|
||||
since="5.5.0"
|
||||
message="Class names should begin with an uppercase character"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.ClassNamingConventionsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#classnamingconventions">
|
||||
<description>
|
||||
Class names should always begin with an upper case character.
|
||||
</description>
|
||||
<priority>1</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="IfElseStmtsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using 'if...else' statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#ifelsestmtsmustusebraces">
|
||||
<description>
|
||||
Avoid using if..else statements without using surrounding braces. If the code formatting
|
||||
or indentation is lost then it becomes difficult to separate the code being controlled
|
||||
from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//IfBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0]
|
||||
|
|
||||
//IfElseBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
// this is OK
|
||||
if (foo) x++;
|
||||
|
||||
// but this is not
|
||||
if (foo)
|
||||
x = x+1;
|
||||
else
|
||||
x = x-1;
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="IfStmtsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using if statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#ifstmtsmustusebraces">
|
||||
<description>
|
||||
Avoid using if statements without using braces to surround the code block. If the code
|
||||
formatting or indentation is lost then it becomes difficult to separate the code being
|
||||
controlled from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//IfBlockStatement/BlockStatement[@CurlyBrace='false']
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
if (foo) // not recommended
|
||||
x++;
|
||||
|
||||
if (foo) { // preferred approach
|
||||
x++;
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ForLoopsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using 'for' statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#forloopsmustusebraces">
|
||||
<description>
|
||||
Avoid using 'for' statements without using surrounding braces. If the code formatting or
|
||||
indentation is lost then it becomes difficult to separate the code being controlled
|
||||
from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//ForLoopStatement/BlockStatement[@CurlyBrace='false']
|
||||
|
|
||||
//ForEachStatement/BlockStatement[@CurlyBrace='false']
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
for (int i = 0; i < 42; i++) // not recommended
|
||||
foo();
|
||||
|
||||
for (int i = 0; i < 42; i++) { // preferred approach
|
||||
foo();
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="MethodNamingConventions"
|
||||
since="5.5.0"
|
||||
message="Method name does not begin with a lower case character."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.MethodNamingConventionsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#methodnamingconventions">
|
||||
<description>
|
||||
Method names should always begin with a lower case character, and should not contain underscores.
|
||||
</description>
|
||||
<priority>1</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void fooStuff() {
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="VariableNamingConventions"
|
||||
since="5.5.0"
|
||||
message="{0} variable {1} should begin with {2}"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.VariableNamingConventionsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#variablenamingconventions">
|
||||
<description>
|
||||
A variable naming conventions rule - customize this to your liking. Currently, it
|
||||
checks for final variables that should be fully capitalized and non-final variables
|
||||
that should not include underscores.
|
||||
</description>
|
||||
<priority>1</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public static final Integer MY_NUM = 0;
|
||||
public String myTest = '';
|
||||
DataModule dmTest = new DataModule();
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="WhileLoopsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using 'while' statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#whileloopsmustusebraces">
|
||||
<description>
|
||||
Avoid using 'while' statements without using braces to surround the code block. If the code
|
||||
formatting or indentation is lost then it becomes difficult to separate the code being
|
||||
controlled from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//WhileLoopStatement/BlockStatement[@CurlyBrace='false']
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
while (true) // not recommended
|
||||
x++;
|
||||
|
||||
while (true) { // preferred approach
|
||||
x++;
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
342
pmd-apex/src/main/resources/category/apex/design.xml
Normal file
342
pmd-apex/src/main/resources/category/apex/design.xml
Normal file
File diff suppressed because it is too large
Load Diff
11
pmd-apex/src/main/resources/category/apex/documentation.xml
Normal file
11
pmd-apex/src/main/resources/category/apex/documentation.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Documentation"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>
|
||||
Rules that are related to code documentation.
|
||||
</description>
|
||||
</ruleset>
|
243
pmd-apex/src/main/resources/category/apex/errorprone.xml
Normal file
243
pmd-apex/src/main/resources/category/apex/errorprone.xml
Normal file
@ -0,0 +1,243 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Error Prone"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>
|
||||
Rules to detect constructs that are either broken, extremely confusing or prone to runtime errors.
|
||||
</description>
|
||||
|
||||
<rule name="AvoidDirectAccessTriggerMap"
|
||||
since="6.0.0"
|
||||
message="Avoid directly accessing Trigger.old and Trigger.new"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#avoiddirectaccesstriggermap">
|
||||
<description>
|
||||
Avoid directly accessing Trigger.old and Trigger.new as it can lead to a bug. Triggers should be bulkified and iterate through the map to handle the actions for each item separately.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//ArrayLoadExpression/TriggerVariableExpression | //ArrayLoadExpression/LiteralExpression
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
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 ){
|
||||
//Good: Iterate through the trigger.new array instead.
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="EmptyCatchBlock"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty catch blocks"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptycatchblock">
|
||||
<description>
|
||||
Empty Catch Block finds instances where an exception is caught, but nothing is done.
|
||||
In most circumstances, this swallows an exception which should either be acted on
|
||||
or reported.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//CatchBlockStatement[./BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public void doSomething() {
|
||||
...
|
||||
try {
|
||||
insert accounts;
|
||||
} catch (DmlException dmle) {
|
||||
// not good
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="EmptyIfStmt"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty 'if' statements"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptyifstmt">
|
||||
<description>
|
||||
Empty If Statement finds instances where a condition is checked but nothing is done about it.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//IfBlockStatement
|
||||
[BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void bar(Integer x) {
|
||||
if (x == 0) {
|
||||
// empty!
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="EmptyStatementBlock"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty block statements."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptystatementblock">
|
||||
<description>
|
||||
Empty block statements serve no purpose and should be removed.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//Method/ModifierNode[@Abstract!='true' and ../BlockStatement[count(*) = 0]]
|
||||
| //Method/BlockStatement//BlockStatement[count(*) = 0]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
|
||||
private int _bar;
|
||||
|
||||
public void setBar(int bar) {
|
||||
// empty
|
||||
}
|
||||
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="EmptyTryOrFinallyBlock"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty try or finally blocks"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptytryorfinallyblock">
|
||||
<description>
|
||||
Avoid empty try or finally blocks - what's the point?
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//TryCatchFinallyBlockStatement[./BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void bar() {
|
||||
try {
|
||||
// empty !
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Foo {
|
||||
public void bar() {
|
||||
try {
|
||||
int x=2;
|
||||
} finally {
|
||||
// empty!
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="EmptyWhileStmt"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty 'while' statements"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptywhilestmt">
|
||||
<description>
|
||||
Empty While Statement finds all instances where a while statement does nothing.
|
||||
If it is a timing loop, then you should use Thread.sleep() for it; if it is
|
||||
a while loop that does a lot in the exit expression, rewrite it to make it clearer.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//WhileLoopStatement[./BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public void bar(Integer a, Integer b) {
|
||||
while (a == b) {
|
||||
// empty!
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="MethodWithSameNameAsEnclosingClass"
|
||||
since="5.5.0"
|
||||
message="Classes should not have non-constructor methods with the same name as the class"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.MethodWithSameNameAsEnclosingClassRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#methodwithsamenameasenclosingclass">
|
||||
<description>
|
||||
Non-constructor methods should not have the same name as the enclosing class.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class MyClass {
|
||||
// this is OK because it is a constructor
|
||||
public MyClass() {}
|
||||
// this is bad because it is a method
|
||||
public void MyClass() {}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
11
pmd-apex/src/main/resources/category/apex/multithreading.xml
Normal file
11
pmd-apex/src/main/resources/category/apex/multithreading.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Multithreading"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>
|
||||
Rules that flag issues when dealing with multiple threads of execution.
|
||||
</description>
|
||||
</ruleset>
|
80
pmd-apex/src/main/resources/category/apex/performance.xml
Normal file
80
pmd-apex/src/main/resources/category/apex/performance.xml
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Performance"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>
|
||||
Rules that flag suboptimal code.
|
||||
</description>
|
||||
|
||||
<rule name="AvoidDmlStatementsInLoops"
|
||||
since="5.5.0"
|
||||
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.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Something {
|
||||
public void foo() {
|
||||
for (Integer i = 0; i < 151; i++) {
|
||||
Account account;
|
||||
// ...
|
||||
insert account;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidSoqlInLoops"
|
||||
since="5.5.0"
|
||||
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.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Something {
|
||||
public static void main( String as[] ) {
|
||||
for (Integer i = 0; i < 10; i++) {
|
||||
List<Account> accounts = [SELECT Id FROM Account];
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</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>
|
||||
|
||||
</ruleset>
|
274
pmd-apex/src/main/resources/category/apex/security.xml
Normal file
274
pmd-apex/src/main/resources/category/apex/security.xml
Normal file
@ -0,0 +1,274 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Security"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>
|
||||
Rules that flag potential security flaws.
|
||||
</description>
|
||||
|
||||
<rule name="ApexBadCrypto"
|
||||
since="5.5.3"
|
||||
message="Apex classes should use random IV/key"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexBadCryptoRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexbadcrypto">
|
||||
<description>
|
||||
The rule makes sure you are using randomly generated IVs and keys for `Crypto` calls.
|
||||
Hard-wiring these values greatly compromises the security of encrypted data.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
Blob hardCodedIV = Blob.valueOf('Hardcoded IV 123');
|
||||
Blob hardCodedKey = Blob.valueOf('0000000000000000');
|
||||
Blob data = Blob.valueOf('Data to be encrypted');
|
||||
Blob encrypted = Crypto.encrypt('AES128', hardCodedKey, hardCodedIV, data);
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexCRUDViolation"
|
||||
since="5.5.3"
|
||||
message="Validate CRUD permission before SOQL/DML operation"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexCRUDViolationRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexcrudviolation">
|
||||
<description>
|
||||
The rule validates you are checking for access permissions before a SOQL/SOSL/DML operation.
|
||||
Since Apex runs in system mode not having proper permissions checks results in escalation of
|
||||
privilege and may produce runtime errors. This check forces you to handle such scenarios.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public Contact foo(String status, String ID) {
|
||||
Contact c = [SELECT Status__c FROM Contact WHERE Id=:ID];
|
||||
|
||||
// Make sure we can update the database before even trying
|
||||
if (!Schema.sObjectType.Contact.fields.Name.isUpdateable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
c.Status__c = status;
|
||||
update c;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexCSRF"
|
||||
since="5.5.3"
|
||||
message="Avoid making DML operations in Apex class constructor/init method"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexCSRFRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexcsrf">
|
||||
<description>
|
||||
Check to avoid making DML operations in Apex class constructor/init method. This prevents
|
||||
modification of the database just by accessing a page.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public init() {
|
||||
insert data;
|
||||
}
|
||||
|
||||
public Foo() {
|
||||
insert data;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexDangerousMethods"
|
||||
since="5.5.3"
|
||||
message="Calling potentially dangerous method"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexDangerousMethodsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexdangerousmethods">
|
||||
<description>
|
||||
Checks against calling dangerous methods.
|
||||
|
||||
For the time being, it reports:
|
||||
|
||||
* Against `FinancialForce`'s `Configuration.disableTriggerCRUDSecurity()`. Disabling CRUD security
|
||||
opens the door to several attacks and requires manual validation, which is unreliable.
|
||||
* Calling `System.debug` passing sensitive data as parameter, which could lead to exposure
|
||||
of private data.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public Foo() {
|
||||
Configuration.disableTriggerCRUDSecurity();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexInsecureEndpoint"
|
||||
since="5.5.3"
|
||||
message="Apex callouts should use encrypted communication channels"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexInsecureEndpointRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexinsecureendpoint">
|
||||
<description>
|
||||
Checks against accessing endpoints under plain **http**. You should always use
|
||||
**https** for security.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
void foo() {
|
||||
HttpRequest req = new HttpRequest();
|
||||
req.setEndpoint('http://localhost:com');
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexOpenRedirect"
|
||||
since="5.5.3"
|
||||
message="Apex classes should safely redirect to a known location"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexOpenRedirectRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexopenredirect">
|
||||
<description>
|
||||
Checks against redirects to user-controlled locations. This prevents attackers from
|
||||
redirecting users to phishing sites.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
String unsafeLocation = ApexPage.getCurrentPage().getParameters.get('url_param');
|
||||
PageReference page() {
|
||||
return new PageReference(unsafeLocation);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexSharingViolations"
|
||||
since="5.5.3"
|
||||
message="Apex classes should declare a sharing model if DML or SOQL/SOSL is used"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexSharingViolationsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexsharingviolations">
|
||||
<description>
|
||||
Detect classes declared without explicit sharing mode if DML methods are used. This
|
||||
forces the developer to take access restrictions into account before modifying objects.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
// DML operation here
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexSOQLInjection"
|
||||
since="5.5.3"
|
||||
message="Avoid untrusted/unescaped variables in DML query"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexSOQLInjectionRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexsoqlinjection">
|
||||
<description>
|
||||
Detects the usage of untrusted / unescaped variables in DML queries.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void test1(String t1) {
|
||||
Database.query('SELECT Id FROM Account' + t1);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexSuggestUsingNamedCred"
|
||||
since="5.5.3"
|
||||
message="Suggest named credentials for authentication"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexSuggestUsingNamedCredRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexsuggestusingnamedcred">
|
||||
<description>
|
||||
Detects hardcoded credentials used in requests to an endpoint.
|
||||
|
||||
You should refrain from hardcoding credentials:
|
||||
* They are hard to mantain by being mixed in application code
|
||||
* Particularly hard to update them when used from different classes
|
||||
* Granting a developer access to the codebase means granting knowledge
|
||||
of credentials, keeping a two-level access is not possible.
|
||||
* Using different credentials for different environments is troublesome
|
||||
and error-prone.
|
||||
|
||||
Instead, you should use *Named Credentials* and a callout endpoint.
|
||||
|
||||
For more information, you can check [this](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_named_credentials.htm)
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void foo(String username, String password) {
|
||||
Blob headerValue = Blob.valueOf(username + ':' + password);
|
||||
String authorizationHeader = 'BASIC ' + EncodingUtil.base64Encode(headerValue);
|
||||
req.setHeader('Authorization', authorizationHeader);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexXSSFromEscapeFalse"
|
||||
since="5.5.3"
|
||||
message="Apex classes should escape Strings in error messages"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexXSSFromEscapeFalseRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexxssfromescapefalse">
|
||||
<description>
|
||||
Reports on calls to `addError` with disabled escaping. The message passed to `addError`
|
||||
will be displayed directly to the user in the UI, making it prime ground for XSS
|
||||
attacks if unescaped.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
Trigger.new[0].addError(vulnerableHTMLGoesHere, false);
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexXSSFromURLParam"
|
||||
since="5.5.3"
|
||||
message="Apex classes should escape/sanitize Strings obtained from URL parameters"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexXSSFromURLParamRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexxssfromurlparam">
|
||||
<description>
|
||||
Makes sure that all values obtained from URL parameters are properly escaped / sanitized
|
||||
to avoid XSS attacks.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
String unescapedstring = ApexPage.getCurrentPage().getParameters.get('url_param');
|
||||
String usedLater = unescapedstring;
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
@ -8,51 +8,7 @@
|
||||
These rules deal with different problems that can occur with Apex unit tests.
|
||||
</description>
|
||||
|
||||
<rule name="ApexUnitTestClassShouldHaveAsserts"
|
||||
since="5.5.1"
|
||||
message="Apex unit tests should System.assert() or assertEquals() or assertNotEquals()"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestClassShouldHaveAssertsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_apexunit.html#apexunittestclassshouldhaveasserts">
|
||||
<description>
|
||||
Apex unit tests should include at least one assertion. This makes the tests more robust, and using assert
|
||||
with messages provide the developer a clearer idea of what the test does.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
@isTest
|
||||
public class Foo {
|
||||
public static testMethod void testSomething() {
|
||||
Account a = null;
|
||||
// This is better than having a NullPointerException
|
||||
// System.assertNotEquals(a, null, 'account not found');
|
||||
a.toString();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule name="ApexUnitTestShouldNotUseSeeAllDataTrue"
|
||||
since="5.5.1"
|
||||
message="Apex unit tests should not use @isTest(seeAllData = true)"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestShouldNotUseSeeAllDataTrueRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_apexunit.html#apexunittestshouldnotuseseealldatatrue">
|
||||
<description>
|
||||
Apex unit tests should not use @isTest(seeAllData=true) because it opens up the existing database data for unexpected modification by tests.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
@isTest(seeAllData = true)
|
||||
public class Foo {
|
||||
public static testMethod void testSomething() {
|
||||
Account a = null;
|
||||
// This is better than having a NullPointerException
|
||||
// System.assertNotEquals(a, null, 'account not found');
|
||||
a.toString();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/bestpractices.xml/ApexUnitTestClassShouldHaveAsserts" deprecated="true" />
|
||||
<rule ref="category/apex/bestpractices.xml/ApexUnitTestShouldNotUseSeeAllDataTrue" deprecated="true" />
|
||||
|
||||
</ruleset>
|
||||
|
@ -8,142 +8,9 @@
|
||||
The Braces ruleset contains rules regarding the use and placement of braces.
|
||||
</description>
|
||||
|
||||
<rule name="IfStmtsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using if statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_braces.html#ifstmtsmustusebraces">
|
||||
<description>
|
||||
Avoid using if statements without using braces to surround the code block. If the code
|
||||
formatting or indentation is lost then it becomes difficult to separate the code being
|
||||
controlled from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//IfBlockStatement/BlockStatement[@CurlyBrace='false']
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
if (foo) // not recommended
|
||||
x++;
|
||||
|
||||
if (foo) { // preferred approach
|
||||
x++;
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="WhileLoopsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using 'while' statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_braces.html#whileloopsmustusebraces">
|
||||
<description>
|
||||
Avoid using 'while' statements without using braces to surround the code block. If the code
|
||||
formatting or indentation is lost then it becomes difficult to separate the code being
|
||||
controlled from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//WhileLoopStatement/BlockStatement[@CurlyBrace='false']
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
while (true) // not recommended
|
||||
x++;
|
||||
|
||||
while (true) { // preferred approach
|
||||
x++;
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="IfElseStmtsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using 'if...else' statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_braces.html#ifelsestmtsmustusebraces">
|
||||
<description>
|
||||
Avoid using if..else statements without using surrounding braces. If the code formatting
|
||||
or indentation is lost then it becomes difficult to separate the code being controlled
|
||||
from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//IfBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0]
|
||||
|
|
||||
//IfElseBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
// this is OK
|
||||
if (foo) x++;
|
||||
|
||||
// but this is not
|
||||
if (foo)
|
||||
x = x+1;
|
||||
else
|
||||
x = x-1;
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ForLoopsMustUseBraces"
|
||||
language="apex"
|
||||
since="5.6.0"
|
||||
message="Avoid using 'for' statements without curly braces"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_braces.html#forloopsmustusebraces">
|
||||
<description>
|
||||
Avoid using 'for' statements without using surrounding braces. If the code formatting or
|
||||
indentation is lost then it becomes difficult to separate the code being controlled
|
||||
from the rest.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//ForLoopStatement/BlockStatement[@CurlyBrace='false']
|
||||
|
|
||||
//ForEachStatement/BlockStatement[@CurlyBrace='false']
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
for (int i = 0; i < 42; i++) // not recommended
|
||||
foo();
|
||||
|
||||
for (int i = 0; i < 42; i++) { // preferred approach
|
||||
foo();
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/codestyle.xml/IfElseStmtsMustUseBraces" deprecated="true" />
|
||||
<rule ref="category/apex/codestyle.xml/IfStmtsMustUseBraces" deprecated="true" />
|
||||
<rule ref="category/apex/codestyle.xml/ForLoopsMustUseBraces" deprecated="true" />
|
||||
<rule ref="category/apex/codestyle.xml/WhileLoopsMustUseBraces" deprecated="true" />
|
||||
|
||||
</ruleset>
|
||||
|
@ -8,283 +8,14 @@
|
||||
The Complexity ruleset contains rules that find problems related to code size or complexity.
|
||||
</description>
|
||||
|
||||
<rule name="AvoidDeeplyNestedIfStmts"
|
||||
since="5.5.0"
|
||||
message="Deeply nested if..then statements are hard to read"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.AvoidDeeplyNestedIfStmtsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#avoiddeeplynestedifstmts">
|
||||
<description>
|
||||
Avoid creating deeply nested if-then statements since they are harder to read and error-prone to maintain.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void bar(Integer x, Integer y, Integer z) {
|
||||
if (x>y) {
|
||||
if (y>z) {
|
||||
if (z==x) {
|
||||
// !! too deep
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ExcessiveParameterList"
|
||||
since="5.5.0"
|
||||
message="Avoid long parameter lists."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.ExcessiveParameterListRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#excessiveparameterlist">
|
||||
<description>
|
||||
Methods with numerous parameters are a challenge to maintain, especially if most of them share the
|
||||
same datatype. These situations usually denote the need for new objects to wrap the numerous parameters.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
// too many arguments liable to be mixed up
|
||||
public void addPerson(int birthYear, int birthMonth, int birthDate, int height, int weight, int ssn) {
|
||||
// ...
|
||||
}
|
||||
// preferred approach
|
||||
public void addPerson(Date birthdate, BodyMeasurements measurements, int ssn) {
|
||||
// ...
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ExcessiveClassLength"
|
||||
since="5.5.0"
|
||||
message="Avoid really long classes."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.ExcessiveClassLengthRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#excessiveclasslength">
|
||||
<description>
|
||||
Excessive class file lengths are usually indications that the class may be burdened with excessive
|
||||
responsibilities that could be provided by external classes or functions. In breaking these methods
|
||||
apart the code becomes more managable and ripe for reuse.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void bar1() {
|
||||
// 1000 lines of code
|
||||
}
|
||||
public void bar2() {
|
||||
// 1000 lines of code
|
||||
}
|
||||
public void bar3() {
|
||||
// 1000 lines of code
|
||||
}
|
||||
public void barN() {
|
||||
// 1000 lines of code
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="NcssMethodCount"
|
||||
since="5.5.0"
|
||||
message="The method ''{0}()'' has an NCSS line count of {1}"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.NcssMethodCountRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#ncssmethodcount">
|
||||
<description>
|
||||
This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines
|
||||
of code for a given method. NCSS ignores comments, and counts actual statements. Using this algorithm,
|
||||
lines of code that are split are counted as one.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo extends Bar {
|
||||
//this method only has 1 NCSS lines
|
||||
public Integer methd() {
|
||||
super.methd();
|
||||
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="NcssTypeCount"
|
||||
since="5.5.0"
|
||||
message="The type has an NCSS line count of {0}"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.NcssTypeCountRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#ncsstypecount">
|
||||
<description>
|
||||
This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines
|
||||
of code for a given type. NCSS ignores comments, and counts actual statements. Using this algorithm,
|
||||
lines of code that are split are counted as one.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
//this class only has 6 NCSS lines
|
||||
public class Foo extends Bar {
|
||||
public Foo() {
|
||||
super();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
super.foo();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="NcssConstructorCount"
|
||||
since="5.5.0"
|
||||
message="The constructor has an NCSS line count of {0}"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.NcssConstructorCountRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#ncssconstructorcount">
|
||||
<description>
|
||||
This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines
|
||||
of code for a given constructor. NCSS ignores comments, and counts actual statements. Using this algorithm,
|
||||
lines of code that are split are counted as one.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo extends Bar {
|
||||
//this constructor only has 1 NCSS lines
|
||||
public Foo() {
|
||||
super();
|
||||
|
||||
|
||||
|
||||
|
||||
super.foo();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="StdCyclomaticComplexity"
|
||||
since="5.5.0"
|
||||
message="The {0} ''{1}'' has a Standard Cyclomatic Complexity of {2}."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.StdCyclomaticComplexityRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#stdcyclomaticcomplexity">
|
||||
<description>
|
||||
Complexity directly affects maintenance costs is determined by the number of decision points in a method
|
||||
plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls.
|
||||
Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote
|
||||
high complexity, and 11+ is very high complexity.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
// This has a Cyclomatic Complexity = 12
|
||||
public class Foo {
|
||||
1 public void example() {
|
||||
2 if (a == b || (c == d && e == f)) {
|
||||
3 if (a1 == b1) {
|
||||
fiddle();
|
||||
4 } else if a2 == b2) {
|
||||
fiddle();
|
||||
} else {
|
||||
fiddle();
|
||||
}
|
||||
5 } else if (c == d) {
|
||||
6 while (c == d) {
|
||||
fiddle();
|
||||
}
|
||||
7 } else if (e == f) {
|
||||
8 for (int n = 0; n < h; n++) {
|
||||
fiddle();
|
||||
}
|
||||
} else {
|
||||
switch (z) {
|
||||
9 case 1:
|
||||
fiddle();
|
||||
break;
|
||||
10 case 2:
|
||||
fiddle();
|
||||
break;
|
||||
11 case 3:
|
||||
fiddle();
|
||||
break;
|
||||
12 default:
|
||||
fiddle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="TooManyFields"
|
||||
since="5.5.0"
|
||||
message="Too many fields"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.TooManyFieldsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#toomanyfields">
|
||||
<description>
|
||||
Classes that have too many fields can become unwieldy and could be redesigned to have fewer fields,
|
||||
possibly through grouping related fields in new objects. For example, a class with individual
|
||||
city/state/zip fields could park them within a single Address field.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Person {
|
||||
// too many separate fields
|
||||
int birthYear;
|
||||
int birthMonth;
|
||||
int birthDate;
|
||||
float height;
|
||||
float weight;
|
||||
}
|
||||
|
||||
public class Person {
|
||||
// this is more manageable
|
||||
Date birthDate;
|
||||
BodyMeasurements measurements;
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ExcessivePublicCount"
|
||||
since="5.5.0"
|
||||
message="This class has a bunch of public methods and attributes"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.complexity.ExcessivePublicCountRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_complexity.html#excessivepubliccount">
|
||||
<description>
|
||||
Classes with large numbers of public methods and attributes require disproportionate testing efforts
|
||||
since combinational side effects grow rapidly and increase risk. Refactoring these classes into
|
||||
smaller ones not only increases testability and reliability but also allows new variations to be
|
||||
developed easily.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public String value;
|
||||
public Bar something;
|
||||
public Variable var;
|
||||
// [... more more public attributes ...]
|
||||
|
||||
public void doWork() {}
|
||||
public void doMoreWork() {}
|
||||
public void doWorkAgain() {}
|
||||
// [... more more public methods ...]
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/design.xml/AvoidDeeplyNestedIfStmts" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/ExcessiveClassLength" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/ExcessiveParameterList" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/ExcessivePublicCount" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/NcssConstructorCount" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/NcssMethodCount" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/NcssTypeCount" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/StdCyclomaticComplexity" deprecated="true" />
|
||||
<rule ref="category/apex/design.xml/TooManyFields" deprecated="true" />
|
||||
|
||||
</ruleset>
|
||||
|
@ -9,184 +9,10 @@ The Empty Code ruleset contains rules that find empty statements of any kind (em
|
||||
empty block statement, empty try or catch block,...).
|
||||
</description>
|
||||
|
||||
<rule name="EmptyCatchBlock"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty catch blocks"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_empty.html#emptycatchblock">
|
||||
<description>
|
||||
Empty Catch Block finds instances where an exception is caught, but nothing is done.
|
||||
In most circumstances, this swallows an exception which should either be acted on
|
||||
or reported.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//CatchBlockStatement[./BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public void doSomething() {
|
||||
...
|
||||
try {
|
||||
insert accounts;
|
||||
} catch (DmlException dmle) {
|
||||
// not good
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/errorprone.xml/EmptyCatchBlock" deprecated="true" />
|
||||
<rule ref="category/apex/errorprone.xml/EmptyIfStmt" deprecated="true" />
|
||||
<rule ref="category/apex/errorprone.xml/EmptyStatementBlock" deprecated="true" />
|
||||
<rule ref="category/apex/errorprone.xml/EmptyTryOrFinallyBlock" deprecated="true" />
|
||||
<rule ref="category/apex/errorprone.xml/EmptyWhileStmt" deprecated="true" />
|
||||
|
||||
<rule name="EmptyIfStmt"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty 'if' statements"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_empty.html#emptyifstmt">
|
||||
<description>
|
||||
Empty If Statement finds instances where a condition is checked but nothing is done about it.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//IfBlockStatement
|
||||
[BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void bar(Integer x) {
|
||||
if (x == 0) {
|
||||
// empty!
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
|
||||
<rule name="EmptyTryOrFinallyBlock"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty try or finally blocks"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_empty.html#emptytryorfinallyblock">
|
||||
<description>
|
||||
Avoid empty try or finally blocks - what's the point?
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//TryCatchFinallyBlockStatement[./BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void bar() {
|
||||
try {
|
||||
// empty !
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Foo {
|
||||
public void bar() {
|
||||
try {
|
||||
int x=2;
|
||||
} finally {
|
||||
// empty!
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="EmptyWhileStmt"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty 'while' statements"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_empty.html#emptywhilestmt">
|
||||
<description>
|
||||
Empty While Statement finds all instances where a while statement does nothing.
|
||||
If it is a timing loop, then you should use Thread.sleep() for it; if it is
|
||||
a while loop that does a lot in the exit expression, rewrite it to make it clearer.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//WhileLoopStatement[./BlockStatement[count(*) = 0]]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public void bar(Integer a, Integer b) {
|
||||
while (a == b) {
|
||||
// empty!
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
|
||||
<rule name="EmptyStatementBlock"
|
||||
language="apex"
|
||||
since="6.0.0"
|
||||
message="Avoid empty block statements."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_empty.html#emptystatementblock">
|
||||
<description>
|
||||
Empty block statements serve no purpose and should be removed.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//Method/ModifierNode[@Abstract!='true' and ../BlockStatement[count(*) = 0]]
|
||||
| //Method/BlockStatement//BlockStatement[count(*) = 0]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
|
||||
private int _bar;
|
||||
|
||||
public void setBar(int bar) {
|
||||
// empty
|
||||
}
|
||||
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
</ruleset>
|
||||
|
@ -9,57 +9,5 @@
|
||||
These are rules which use the Metrics Framework to calculate metrics.
|
||||
</description>
|
||||
|
||||
<rule name="CyclomaticComplexity"
|
||||
message="The {0} ''{1}'' has a{2} cyclomatic complexity of {3}."
|
||||
since="6.0.0"
|
||||
class="net.sourceforge.pmd.lang.apex.metrics.rule.CyclomaticComplexityRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_metrics.html#cyclomaticcomplexity">
|
||||
<description>
|
||||
<![CDATA[
|
||||
The complexity of methods directly affects maintenance costs and readability. Concentrating too much decisional logic
|
||||
in a single method makes its behaviour hard to read and change.
|
||||
|
||||
Cyclomatic complexity assesses the complexity of a method by counting the number of decision points in a method,
|
||||
plus one for the method entry. Decision points are places where the control flow jumps to another place in the
|
||||
program. As such, they include all control flow statements, such as 'if', 'while', 'for', and 'case'.
|
||||
|
||||
Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote
|
||||
high complexity, and 11+ is very high complexity. By default, this rule reports methods with a complexity >= 10.
|
||||
Additionnally, classes with many methods of moderate complexity get reported as well once the total of their
|
||||
methods' complexities reaches 40, even if none of the methods was directly reported.
|
||||
|
||||
Reported methods should be broken down into several smaller methods. Reported classes should probably be broken down
|
||||
into subcomponents.
|
||||
]]>
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Complicated {
|
||||
public void example() { // This method has a cyclomatic complexity of 12
|
||||
int x = 0, y = 1, z = 2, t = 2;
|
||||
boolean a = false, b = true, c = false, d = true;
|
||||
if (a && b || b && d) {
|
||||
if (y == z) {
|
||||
x = 2;
|
||||
} else if (y == t && !d) {
|
||||
x = 2;
|
||||
} else {
|
||||
x = 2;
|
||||
}
|
||||
} else if (c && d) {
|
||||
while (z < y) {
|
||||
x = 2;
|
||||
}
|
||||
} else {
|
||||
for (int n = 0; n < t; n++) {
|
||||
x = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/apex/design.xml/CyclomaticComplexity" deprecated="true" />
|
||||
</ruleset>
|
||||
|
@ -8,72 +8,8 @@
|
||||
The Performance ruleset contains a collection of good practices which should be followed.
|
||||
</description>
|
||||
|
||||
<rule name="AvoidSoqlInLoops"
|
||||
since="5.5.0"
|
||||
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.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Something {
|
||||
public static void main( String as[] ) {
|
||||
for (Integer i = 0; i < 10; i++) {
|
||||
List<Account> accounts = [SELECT Id FROM Account];
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</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"
|
||||
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.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Something {
|
||||
public void foo() {
|
||||
for (Integer i = 0; i < 151; i++) {
|
||||
Account account;
|
||||
// ...
|
||||
insert account;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/performance.xml/AvoidDmlStatementsInLoops" deprecated="true" />
|
||||
<rule ref="category/apex/performance.xml/AvoidSoqlInLoops" deprecated="true" />
|
||||
<rule ref="category/apex/performance.xml/AvoidSoslInLoops" deprecated="true" />
|
||||
|
||||
</ruleset>
|
||||
|
@ -3,10 +3,15 @@
|
||||
#
|
||||
|
||||
rulesets.filenames=\
|
||||
rulesets/apex/apexunit.xml,\
|
||||
rulesets/apex/braces.xml,\
|
||||
rulesets/apex/complexity.xml,\
|
||||
rulesets/apex/empty.xml,\
|
||||
rulesets/apex/performance.xml,\
|
||||
rulesets/apex/security.xml,\
|
||||
rulesets/apex/style.xml
|
||||
category/apex/bestpractices.xml,\
|
||||
category/apex/codestyle.xml,\
|
||||
category/apex/design.xml,\
|
||||
category/apex/errorprone.xml,\
|
||||
category/apex/performance.xml,\
|
||||
category/apex/security.xml
|
||||
|
||||
#
|
||||
# categories with no rules yet
|
||||
#
|
||||
#category/apex/documentation.xml
|
||||
#category/apex/multithreading.xml
|
||||
|
@ -8,268 +8,16 @@
|
||||
These rules deal with different security problems that can occur within Apex.
|
||||
</description>
|
||||
|
||||
<rule name="ApexSharingViolations"
|
||||
since="5.5.3"
|
||||
message="Apex classes should declare a sharing model if DML or SOQL/SOSL is used"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexSharingViolationsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexsharingviolations">
|
||||
<description>
|
||||
Detect classes declared without explicit sharing mode if DML methods are used. This
|
||||
forces the developer to take access restrictions into account before modifying objects.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
// DML operation here
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexOpenRedirect"
|
||||
since="5.5.3"
|
||||
message="Apex classes should safely redirect to a known location"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexOpenRedirectRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexopenredirect">
|
||||
<description>
|
||||
Checks against redirects to user-controlled locations. This prevents attackers from
|
||||
redirecting users to phishing sites.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
String unsafeLocation = ApexPage.getCurrentPage().getParameters.get('url_param');
|
||||
PageReference page() {
|
||||
return new PageReference(unsafeLocation);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexInsecureEndpoint"
|
||||
since="5.5.3"
|
||||
message="Apex callouts should use encrypted communication channels"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexInsecureEndpointRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexinsecureendpoint">
|
||||
<description>
|
||||
Checks against accessing endpoints under plain **http**. You should always use
|
||||
**https** for security.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
void foo() {
|
||||
HttpRequest req = new HttpRequest();
|
||||
req.setEndpoint('http://localhost:com');
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexXSSFromURLParam"
|
||||
since="5.5.3"
|
||||
message="Apex classes should escape/sanitize Strings obtained from URL parameters"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexXSSFromURLParamRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexxssfromurlparam">
|
||||
<description>
|
||||
Makes sure that all values obtained from URL parameters are properly escaped / sanitized
|
||||
to avoid XSS attacks.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
String unescapedstring = ApexPage.getCurrentPage().getParameters.get('url_param');
|
||||
String usedLater = unescapedstring;
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexXSSFromEscapeFalse"
|
||||
since="5.5.3"
|
||||
message="Apex classes should escape Strings in error messages"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexXSSFromEscapeFalseRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexxssfromescapefalse">
|
||||
<description>
|
||||
Reports on calls to `addError` with disabled escaping. The message passed to `addError`
|
||||
will be displayed directly to the user in the UI, making it prime ground for XSS
|
||||
attacks if unescaped.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
Trigger.new[0].addError(vulnerableHTMLGoesHere, false);
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexBadCrypto"
|
||||
since="5.5.3"
|
||||
message="Apex classes should use random IV/key"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexBadCryptoRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexbadcrypto">
|
||||
<description>
|
||||
The rule makes sure you are using randomly generated IVs and keys for `Crypto` calls.
|
||||
Hard-wiring these values greatly compromises the security of encrypted data.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
Blob hardCodedIV = Blob.valueOf('Hardcoded IV 123');
|
||||
Blob hardCodedKey = Blob.valueOf('0000000000000000');
|
||||
Blob data = Blob.valueOf('Data to be encrypted');
|
||||
Blob encrypted = Crypto.encrypt('AES128', hardCodedKey, hardCodedIV, data);
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexCSRF"
|
||||
since="5.5.3"
|
||||
message="Avoid making DML operations in Apex class constructor/init method"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexCSRFRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexcsrf">
|
||||
<description>
|
||||
Check to avoid making DML operations in Apex class constructor/init method. This prevents
|
||||
modification of the database just by accessing a page.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public init() {
|
||||
insert data;
|
||||
}
|
||||
|
||||
public Foo() {
|
||||
insert data;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexSOQLInjection"
|
||||
since="5.5.3"
|
||||
message="Avoid untrusted/unescaped variables in DML query"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexSOQLInjectionRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexsoqlinjection">
|
||||
<description>
|
||||
Detects the usage of untrusted / unescaped variables in DML queries.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void test1(String t1) {
|
||||
Database.query('SELECT Id FROM Account' + t1);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexCRUDViolation"
|
||||
since="5.5.3"
|
||||
message="Validate CRUD permission before SOQL/DML operation"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexCRUDViolationRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexcrudviolation">
|
||||
<description>
|
||||
The rule validates you are checking for access permissions before a SOQL/SOSL/DML operation.
|
||||
Since Apex runs in system mode not having proper permissions checks results in escalation of
|
||||
privilege and may produce runtime errors. This check forces you to handle such scenarios.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public Contact foo(String status, String ID) {
|
||||
Contact c = [SELECT Status__c FROM Contact WHERE Id=:ID];
|
||||
|
||||
// Make sure we can update the database before even trying
|
||||
if (!Schema.sObjectType.Contact.fields.Name.isUpdateable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
c.Status__c = status;
|
||||
update c;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexDangerousMethods"
|
||||
since="5.5.3"
|
||||
message="Calling potentially dangerous method"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexDangerousMethodsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexdangerousmethods">
|
||||
<description><![CDATA[
|
||||
Checks against calling dangerous methods.
|
||||
|
||||
For the time being, it reports:
|
||||
|
||||
* Against `FinancialForce`'s `Configuration.disableTriggerCRUDSecurity()`. Disabling CRUD security
|
||||
opens the door to several attacks and requires manual validation, which is unreliable.
|
||||
* Calling `System.debug` passing sensitive data as parameter, which could lead to exposure
|
||||
of private data.
|
||||
]]>
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public Foo() {
|
||||
Configuration.disableTriggerCRUDSecurity();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="ApexSuggestUsingNamedCred"
|
||||
since="5.5.3"
|
||||
message="Suggest named credentials for authentication"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.security.ApexSuggestUsingNamedCredRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_security.html#apexsuggestusingnamedcred">
|
||||
<description><
|
||||
]]>
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void foo(String username, String password) {
|
||||
Blob headerValue = Blob.valueOf(username + ':' + password);
|
||||
String authorizationHeader = 'BASIC ' + EncodingUtil.base64Encode(headerValue);
|
||||
req.setHeader('Authorization', authorizationHeader);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/security.xml/ApexBadCrypto" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexCRUDViolation" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexCSRF" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexDangerousMethods" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexInsecureEndpoint" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexOpenRedirect" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexSharingViolations" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexSOQLInjection" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexSuggestUsingNamedCred" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexXSSFromEscapeFalse" deprecated="true" />
|
||||
<rule ref="category/apex/security.xml/ApexXSSFromURLParam" deprecated="true" />
|
||||
|
||||
</ruleset>
|
||||
|
@ -8,193 +8,15 @@
|
||||
The Style Ruleset contains rules regarding preferred usage of names and identifiers.
|
||||
</description>
|
||||
|
||||
<rule name="VariableNamingConventions"
|
||||
since="5.5.0"
|
||||
message="{0} variable {1} should begin with {2}"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.VariableNamingConventionsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#variablenamingconventions">
|
||||
<description>
|
||||
A variable naming conventions rule - customize this to your liking. Currently, it
|
||||
checks for final variables that should be fully capitalized and non-final variables
|
||||
that should not include underscores.
|
||||
</description>
|
||||
<priority>1</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public static final Integer MY_NUM = 0;
|
||||
public String myTest = '';
|
||||
DataModule dmTest = new DataModule();
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/codestyle.xml/ClassNamingConventions" deprecated="true" />
|
||||
<rule ref="category/apex/codestyle.xml/MethodNamingConventions" deprecated="true" />
|
||||
<rule ref="category/apex/codestyle.xml/VariableNamingConventions" deprecated="true" />
|
||||
|
||||
<rule name="MethodNamingConventions"
|
||||
since="5.5.0"
|
||||
message="Method name does not begin with a lower case character."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.MethodNamingConventionsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#methodnamingconventions">
|
||||
<description>
|
||||
Method names should always begin with a lower case character, and should not contain underscores.
|
||||
</description>
|
||||
<priority>1</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {
|
||||
public void fooStuff() {
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/errorprone.xml/AvoidDirectAccessTriggerMap" deprecated="true" />
|
||||
<rule ref="category/apex/errorprone.xml/MethodWithSameNameAsEnclosingClass" deprecated="true" />
|
||||
|
||||
<rule name="ClassNamingConventions"
|
||||
since="5.5.0"
|
||||
message="Class names should begin with an uppercase character"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.ClassNamingConventionsRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#classnamingconventions">
|
||||
<description>
|
||||
Class names should always begin with an upper case character.
|
||||
</description>
|
||||
<priority>1</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Foo {}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="MethodWithSameNameAsEnclosingClass"
|
||||
since="5.5.0"
|
||||
message="Classes should not have non-constructor methods with the same name as the class"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.MethodWithSameNameAsEnclosingClassRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#methodwithsamenameasenclosingclass">
|
||||
<description>
|
||||
Non-constructor methods should not have the same name as the enclosing class.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class MyClass {
|
||||
// this is OK because it is a constructor
|
||||
public MyClass() {}
|
||||
// this is bad because it is a method
|
||||
public void MyClass() {}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidLogicInTrigger"
|
||||
since="5.5.0"
|
||||
message="Avoid logic in triggers"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.AvoidLogicInTriggerRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#avoidlogicintrigger">
|
||||
<description>
|
||||
As triggers do not allow methods like regular classes they are less flexible and suited to apply good encapsulation style.
|
||||
Therefore delegate the triggers work to a regular class (often called Trigger handler class).
|
||||
|
||||
See more here: https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
trigger Accounts on Account (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
|
||||
for(Account acc : Trigger.new) {
|
||||
if(Trigger.isInsert) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
if(Trigger.isDelete) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidGlobalModifier"
|
||||
since="5.5.0"
|
||||
message="Avoid using global modifier"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.AvoidGlobalModifierRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#avoidglobalmodifier">
|
||||
<description>
|
||||
Global classes should be avoided (especially in managed packages) as they can never be deleted or changed in signature. Always check twice if something needs to be global.
|
||||
Many interfaces (e.g. Batch) required global modifiers in the past but don't require this anymore. Don't lock yourself in.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
global class Unchangeable {
|
||||
global UndeletableType unchangable(UndeletableType param) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidDirectAccessTriggerMap"
|
||||
since="6.0.0"
|
||||
message="Avoid directly accessing Trigger.old and Trigger.new"
|
||||
class="net.sourceforge.pmd.lang.apex.rule.ApexXPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#avoiddirectaccesstriggermap">
|
||||
<description>
|
||||
Avoid directly accessing Trigger.old and Trigger.new as it can lead to a bug. Triggers should be bulkified and iterate through the map to handle the actions for each item separately.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//ArrayLoadExpression/TriggerVariableExpression | //ArrayLoadExpression/LiteralExpression
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
|
||||
<example>
|
||||
<![CDATA[
|
||||
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 ){
|
||||
//Good: Iterate through the trigger.new array instead.
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="AvoidHardcodingId"
|
||||
since="6.0.0"
|
||||
message="Hardcoding Id's is bound to break when changing environments."
|
||||
class="net.sourceforge.pmd.lang.apex.rule.style.AvoidHardcodingIdRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_style.html#avoidhardcodingid">
|
||||
<description>
|
||||
When deploying Apex code between sandbox and production environments, or installing Force.com AppExchange packages,
|
||||
it is essential to avoid hardcoding IDs in the Apex code. By doing so, if the record IDs change between environments,
|
||||
the logic can dynamically identify the proper data to operate against and not fail.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public without sharing class Foo {
|
||||
void foo() {
|
||||
//Error - hardcoded the record type id
|
||||
if(a.RecordTypeId == '012500000009WAr'){
|
||||
//do some logic here.....
|
||||
} else if(a.RecordTypeId == '0123000000095Km'){
|
||||
//do some logic here for a different record type...
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule ref="category/apex/bestpractices.xml/AvoidGlobalModifier" deprecated="true" />
|
||||
<rule ref="category/apex/bestpractices.xml/AvoidLogicInTrigger" deprecated="true" />
|
||||
<rule ref="category/apex/bestpractices.xml/AvoidHardcodingId" deprecated="true" />
|
||||
|
||||
</ruleset>
|
||||
|
Reference in New Issue
Block a user