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
2 changes: 1 addition & 1 deletion packages/code-analyzer-regex-engine/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@salesforce/code-analyzer-regex-engine",
"description": "Plugin package that adds 'regex' as an engine into Salesforce Code Analyzer",
"version": "0.40.0",
"version": "0.41.0-SNAPSHOT",
"author": "The Salesforce Code Analyzer Team",
"license": "BSD-3-Clause",
"homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview",
Expand Down
2 changes: 1 addition & 1 deletion packages/code-analyzer-regex-engine/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const MESSAGE_CATALOG : { [key: string]: string } = {
`Found trailing whitespace at the end of a line of code.`,

MixedIndentationRuleDescription:
`Identifies lines with both spaces and tabs in the leading indentation. Mixed indentation causes unexpected behavior in multiline strings because the indentation algorithm processes whitespace differently.`,
`Identifies lines with both spaces and tabs in the leading indentation. Mixed indentation causes unexpected behavior in multiline strings because the indentation algorithm processes whitespace differently. Lines that start with tabs followed by a single space and an asterisk, such as the continuation lines of a tab-indented block comment, are not reported.`,

MixedIndentationRuleMessage:
`Leading indentation contains mixed spaces and tabs. To fix this, use either spaces or tabs.`,
Expand Down
5 changes: 4 additions & 1 deletion packages/code-analyzer-regex-engine/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ export function createBaseRegexRules(now: Date): RegexRules {
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.CODE_STYLE, COMMON_TAGS.LANGUAGES.APEX]
},
NoMixedIndentation: {
regex: (/^(?<target>[ \t]*(\t[ ]+|[ ]+\t)[ \t]*)/gm).toString(),
// The negative lookahead skips lines that start with tabs, a single space, and an asterisk. These are the
// continuation lines of tab-indented block comments (like ApexDoc), where the single space aligns the
// asterisk under the asterisk of the opening "/*" or "/**", so they are not mixed indentation.
regex: (/^(?!\t+ \*)(?<target>[ \t]*(\t[ ]+|[ ]+\t)[ \t]*)/gm).toString(),
file_extensions: ['.cls', '.trigger'],
description: getMessage('MixedIndentationRuleDescription'),
violation_message: getMessage('MixedIndentationRuleMessage'),
Expand Down
46 changes: 46 additions & 0 deletions packages/code-analyzer-regex-engine/test/engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1029,8 +1029,54 @@ describe('Tests for runRules', () => {
expect(violation.message).toBe(getMessage('MixedIndentationRuleMessage'));
}
});

it("NoMixedIndentation rule should NOT flag block comment continuation lines that are indented with tabs then a single space", async () => {
const runOptions: RunOptions = createRunOptions(
new Workspace('id', [path.resolve(__dirname, "test-data", "apexClassMixedIndentation", "validIndentation_TabsWithBlockComments.cls")]));
const runResults: EngineRunResults = await engine.runRules(["NoMixedIndentation"], runOptions);

// ApexDoc and other block comments indented with tabs have continuation lines of tabs, one space, and an
// asterisk. The single space only aligns the asterisk under the opening "/*", so it is not mixed indentation.
expect(runResults.violations).toHaveLength(0);
});

it("NoMixedIndentation rule should still flag lines that resemble block comment continuation lines but are mixed indentation", async () => {
const file: string = path.resolve(__dirname, "test-data", "apexClassMixedIndentation", "mixedIndentation_BlockCommentLookalikes.cls");
const runOptions: RunOptions = createRunOptions(new Workspace('id', [file]));
const runResults: EngineRunResults = await engine.runRules(["NoMixedIndentation"], runOptions);

const expectedViolations: Violation[] = [
// 1. Tabs, then two spaces, then an asterisk
createExpectedMixedIndentationViolation(file, 5, 4),
// 2. A space, then a tab, then a space, then an asterisk
createExpectedMixedIndentationViolation(file, 9, 4),
// 3. Tabs, then a single space, then a character that is not an asterisk
createExpectedMixedIndentationViolation(file, 13, 3),
// 4. Tabs, then two spaces, then text
createExpectedMixedIndentationViolation(file, 17, 5)
];
expect(runResults.violations).toHaveLength(expectedViolations.length);
expect(runResults.violations).toEqual(expectedViolations);
});
});

function createExpectedMixedIndentationViolation(file: string, line: number, endColumn: number): Violation {
return {
ruleName: "NoMixedIndentation",
message: getMessage('MixedIndentationRuleMessage'),
primaryLocationIndex: 0,
codeLocations: [
{
file: file,
startLine: line,
startColumn: 1,
endLine: line,
endColumn: endColumn
}
]
};
}

describe('Tests for regex_ignore', () => {
it('regex_ignore should exclude matches that match the negative pattern', async () => {
const customRulesWithNegativePattern: RegexRules = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
public class BlockCommentLookalikes {
// This file SHOULD trigger violations. Each case is close to, but is not, a tab-indented block comment line.

/**
* 1. Tabs, then two spaces, then an asterisk
*/

/**
* 2. A space, then a tab, then a space, then an asterisk
*/

/**
3. Tabs, then a single space, then a character that is not an asterisk
*/

/*
4. Tabs, then two spaces, then text
*/

public void method() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* The ApexDoc comment on the class is not indented, so this file starts with the usual space-asterisk lines.
*/
public class TabsWithBlockComments {
/**
* @description Every continuation line of this ApexDoc comment is a tab, a single space, and an asterisk.
* @param accountId The Id of the Account to query
* @return The Account with the given Id
*/
public static Account getAccount(Id accountId) {
/*
* A plain block comment formatted the same way, two tabs deep.
*/
return [SELECT Id FROM Account WHERE Id = :accountId];
}

/**
* An ApexDoc comment that contains a block quote and a code block.
*
* > Each line of this block quote starts with a tab, a single space, and an asterisk.
* > So it is a block comment continuation line, not mixed indentation.
*
* ```
* Account a = getAccount(someId);
* ```
*/
public static void example() {
System.debug('ok');
}
}