Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 25 additions & 23 deletions packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { RuleDescription, SeverityLevel, COMMON_TAGS } from '@salesforce/code-analyzer-engine-api';

export const DEV_PREVIEW_TAG: string = 'DevPreview';

/**
* Known ApexGuru rules with descriptions and metadata.
*
Expand All @@ -16,15 +18,15 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'SoqlInALoop',
severityLevel: SeverityLevel.High,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'SOQL query inside a loop causes performance issues and can hit governor limits',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop.htm&type=5']
},

{
name: 'DmlInALoop',
severityLevel: SeverityLevel.High,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'DML statement inside a loop causes performance issues and can hit governor limits',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_dml_in_loop.htm&type=5']
},
Expand All @@ -36,15 +38,15 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'SoqlInALoopOneHop',
severityLevel: SeverityLevel.High,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'SOQL query reached one method-hop away inside a loop causes performance issues and can hit governor limits',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop_one_hop.htm&type=5']
},

{
name: 'ExpensiveMethods',
severityLevel: SeverityLevel.High,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Method accounts for a large share of observed Apex CPU time and is a hotspot for performance work',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_methods.htm&type=5']
},
Expand All @@ -56,23 +58,23 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'SoqlWithoutAWhereClauseOrLimitStatement',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'SOQL query without WHERE clause or LIMIT statement can cause performance issues and heap size exceptions',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_where_clause_or_limit_statement.htm&type=5']
},

{
name: 'SoqlWithWildcardFilter',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'SOQL query using LIKE with leading wildcard is inefficient and cannot use indexes',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_wildcard_filter.htm&type=5']
},

{
name: 'SchemaGetGlobalDescribeNotEfficient',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Using Schema.getGlobalDescribe() causes unnecessary overhead and decreases performance',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_schema_getglobaldescribe_not_efficient.htm&type=5']
},
Expand All @@ -84,55 +86,55 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'Soql Aggregation',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Manual aggregation in Apex instead of using SOQL aggregate functions causes performance issues',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_aggregating_in_apex.htm&type=5']
},

{
name: 'SoqlWithApexFilter',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Filtering records in Apex instead of using SOQL WHERE clause causes performance issues',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_apex_filter.htm&type=5']
},

{
name: 'CopyingListOrSetElementsUsingAForLoop',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Copying list or set elements using a for loop is inefficient - use addAll() instead',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_copying_elements_with_for_loop.htm&type=5']
},

{
name: 'Redundant Soql',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Multiple identical SOQL queries cause unnecessary database round trips',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_redundant_soql.htm&type=5']
},

{
name: 'SoqlWithNegativeExpressions',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'SOQL queries using negative expressions (NOT IN, !=) don\'t use indexes and cause full table scans',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_negative_expressions.htm&type=5']
},

{
name: 'SObjectMapInAForLoop',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Building Map<Id, SObject> using .put() in a for loop is inefficient - use map constructor or putAll()',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sobject_map_in_for_loop.htm&type=5']
},

{
name: 'SoqlWithoutPlatformCache',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Frequently executed SOQL query whose results could be served from Platform Cache to reduce database load',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_platform_cache.htm&type=5']
},
Expand All @@ -144,23 +146,23 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'LimitsGetHeapsizeMethods',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Frequent Limits.getHeapSize() calls add runtime overhead',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_limits_getheapsize_methods.htm&type=5']
},

{
name: 'ExpensiveStringComparison',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Inefficient string comparison wastes CPU time',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_string_comparison.htm&type=5']
},

{
name: 'ExpensiveDebugStatements',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
description: 'Expensive System.debug() statements add runtime overhead',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_debug_statements.htm&type=5']
},
Expand All @@ -172,7 +174,7 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'UsingTheTestMethodKeyword',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
description: 'The testMethod keyword is deprecated - use @isTest annotation instead',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_using_testmethod.htm&type=5']
},
Expand All @@ -184,31 +186,31 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'SortingInApex',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
description: 'Sorting records in Apex wastes CPU time and can exceed governor limits - use ORDER BY in SOQL',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sorting_in_apex.htm&type=5']
},

{
name: 'BusyLoopDelay',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
description: 'Using empty loops to delay execution wastes CPU time - use System.enqueueJob with delay parameter',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_busy_loop_delay.htm&type=5']
},

{
name: 'SoqlWithUnusedFields',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
description: 'SOQL query selecting unused fields increases resource consumption unnecessarily',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_unused_fields.htm&type=5']
},

{
name: 'WritingFillerStatements',
severityLevel: SeverityLevel.Low,
tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
description: 'Filler statements written to inflate code coverage instead of testing real behavior',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_filler_statements.htm&type=5']
},
Expand All @@ -220,7 +222,7 @@ export const APEXGURU_RULES: RuleDescription[] = [
{
name: 'apexguru-other',
severityLevel: SeverityLevel.Moderate,
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
tags: [DEV_PREVIEW_TAG, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
description: 'Other ApexGuru rules - covers new rules added by Salesforce that are not yet explicitly declared',
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru.htm']
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApexGuruEngine } from '../src/engine';
import { ApexGuruService } from '../src/services/ApexGuruService';
import { LogLevel, RunOptions, Workspace } from '@salesforce/code-analyzer-engine-api';
import { LogLevel, RunOptions, Workspace, COMMON_TAGS } from '@salesforce/code-analyzer-engine-api';
import * as fs from 'node:fs/promises';
import * as fsSync from 'node:fs';
import * as path from 'node:path';
Expand Down Expand Up @@ -602,4 +602,50 @@ describe('ApexGuruEngine', () => {
expect(results.violations[0].codeLocations[0].file).toBe(realFilePath);
});
});

describe('DevPreview behavior', () => {
it('rules list should show DevPreview tag on all rules and no Recommended tag', async () => {
const rules = await engine.describeRules({
logFolder: '/tmp/logs',
workingFolder: '/tmp/working'
});

expect(rules.length).toBeGreaterThan(0);
for (const rule of rules) {
expect(rule.tags).toContain('DevPreview');
expect(rule.tags).not.toContain('Recommended');
}
});

it('should describe rules that can be selected by engine name apexguru', async () => {
const rules = await engine.describeRules({
logFolder: '/tmp/logs',
workingFolder: '/tmp/working'
});

expect(rules.length).toBeGreaterThan(0);
expect(engine.getName()).toBe('apexguru');
});

it('should describe rules selectable by DevPreview tag', async () => {
const rules = await engine.describeRules({
logFolder: '/tmp/logs',
workingFolder: '/tmp/working'
});

const devPreviewRules = rules.filter(r => r.tags.includes('DevPreview'));
expect(devPreviewRules).toHaveLength(rules.length);
});

it('should describe individual rules by name for explicit selection', async () => {
const rules = await engine.describeRules({
logFolder: '/tmp/logs',
workingFolder: '/tmp/working'
});

expect(rules.find(r => r.name === 'SoqlInALoop')).toBeDefined();
expect(rules.find(r => r.name === 'DmlInALoop')).toBeDefined();
expect(rules.find(r => r.name === 'ExpensiveMethods')).toBeDefined();
});
});
});
Loading
Loading