1
0
mirror of https://github.com/sasjs/lint.git synced 2026-01-08 04:50:07 +00:00

feat: new rule hasMacroNameInMend

This commit is contained in:
Saad Jutt
2021-04-05 21:30:09 +05:00
parent 82bef9f26b
commit a0e2c2d843
6 changed files with 309 additions and 3 deletions

View File

@@ -0,0 +1,219 @@
import { Severity } from '../types/Severity'
import { hasMacroNameInMend } from './hasMacroNameInMend'
describe('hasMacroNameInMend', () => {
it('should return an empty array when %mend has correct macro name', () => {
const content = `
%macro somemacro();
%put &sysmacroname;
%mend somemacro;`
expect(hasMacroNameInMend.test(content)).toEqual([])
})
it('should return an empty array when %mend has correct macro name without parentheses', () => {
const content = `
%macro somemacro;
%put &sysmacroname;
%mend somemacro;`
expect(hasMacroNameInMend.test(content)).toEqual([])
})
it('should return an array with a single diagnostic when %mend has no macro name', () => {
const content = `
%macro somemacro;
%put &sysmacroname;
%mend;`
expect(hasMacroNameInMend.test(content)).toEqual([
{
message: '%mend missing macro name',
lineNumber: 4,
startColumnNumber: 3,
endColumnNumber: 9,
severity: Severity.Warning
}
])
})
it('should return an array with a single diagnostic when %mend has incorrect macro name', () => {
const content = `
%macro somemacro;
%put &sysmacroname;
%mend someanothermacro;`
expect(hasMacroNameInMend.test(content)).toEqual([
{
message: 'mismatch macro name in %mend statement',
lineNumber: 4,
startColumnNumber: 9,
endColumnNumber: 25,
severity: Severity.Warning
}
])
})
it('should return an empty array when the file is undefined', () => {
const content = undefined
expect(hasMacroNameInMend.test((content as unknown) as string)).toEqual([])
})
describe('nestedMacros', () => {
it('should return an empty array when %mend has correct macro name', () => {
const content = `
%macro outer();
%macro inner();
%put inner;
%mend inner;
%inner()
%put outer;
%mend outer;`
expect(hasMacroNameInMend.test(content)).toEqual([])
})
it('should return an array with a single diagnostic when %mend has no macro name(inner)', () => {
const content = `
%macro outer();
%macro inner();
%put inner;
%mend;
%inner()
%put outer;
%mend outer;`
expect(hasMacroNameInMend.test(content)).toEqual([
{
message: '%mend missing macro name',
lineNumber: 6,
startColumnNumber: 5,
endColumnNumber: 11,
severity: Severity.Warning
}
])
})
it('should return an array with a single diagnostic when %mend has no macro name(outer)', () => {
const content = `
%macro outer();
%macro inner();
%put inner;
%mend inner;
%inner()
%put outer;
%mend;`
expect(hasMacroNameInMend.test(content)).toEqual([
{
message: '%mend missing macro name',
lineNumber: 9,
startColumnNumber: 3,
endColumnNumber: 9,
severity: Severity.Warning
}
])
})
it('should return an array with two diagnostics when %mend has no macro name(none)', () => {
const content = `
%macro outer();
%macro inner();
%put inner;
%mend;
%inner()
%put outer;
%mend;`
expect(hasMacroNameInMend.test(content)).toEqual([
{
message: '%mend missing macro name',
lineNumber: 6,
startColumnNumber: 5,
endColumnNumber: 11,
severity: Severity.Warning
},
{
message: '%mend missing macro name',
lineNumber: 9,
startColumnNumber: 3,
endColumnNumber: 9,
severity: Severity.Warning
}
])
})
})
describe('with extra spaces ', () => {
it('should return an empty array when %mend has correct macro name', () => {
const content = `
%macro somemacro ;
%put &sysmacroname;
%mend somemacro ;`
expect(hasMacroNameInMend.test(content)).toEqual([])
})
it('should return an array with a single diagnostic when %mend has incorrect macro name', () => {
const content = `
%macro somemacro;
%put &sysmacroname;
%mend someanothermacro ;`
expect(hasMacroNameInMend.test(content)).toEqual([
{
message: 'mismatch macro name in %mend statement',
lineNumber: 6,
startColumnNumber: 14,
endColumnNumber: 30,
severity: Severity.Warning
}
])
})
it('should return an array with a single diagnostic when %mend has no macro name', () => {
const content = `
%macro somemacro ;
%put &sysmacroname;
%mend ;`
expect(hasMacroNameInMend.test(content)).toEqual([
{
message: '%mend missing macro name',
lineNumber: 4,
startColumnNumber: 5,
endColumnNumber: 11,
severity: Severity.Warning
}
])
})
describe('nestedMacros', () => {
it('should return an empty array when %mend has correct macro name', () => {
const content = `
%macro outer( ) ;
%macro inner();
%put inner;
%mend inner;
%inner()
%put outer;
%mend outer;`
expect(hasMacroNameInMend.test(content)).toEqual([])
})
})
})
})

View File

@@ -0,0 +1,80 @@
import { Diagnostic } from '../types/Diagnostic'
import { FileLintRule } from '../types/LintRule'
import { LintRuleType } from '../types/LintRuleType'
import { Severity } from '../types/Severity'
const name = 'hasMacroNameInMend'
const description = 'The %mend statement should contain the macro name'
const message = '$mend statement missing or incorrect'
const test = (value: string) => {
const diagnostics: Diagnostic[] = []
const statements: string[] = value ? value.split(';') : []
const stack: string[] = []
statements.forEach((statement, index) => {
const trimmedStatement = statement.trim()
if (trimmedStatement.startsWith('%macro ')) {
const macroName = trimmedStatement
.split(' ')
.filter((s: string) => !!s)[1]
.split('(')[0]
stack.push(macroName)
} else if (trimmedStatement.startsWith('%mend')) {
const macroStarted = stack.pop()
const macroName = trimmedStatement
.split(' ')
.filter((s: string) => !!s)[1]
if (!macroName) {
diagnostics.push({
message: '%mend missing macro name',
lineNumber: getLineNumber(statements, index + 1),
startColumnNumber: getColNumber(statement, '%mend'),
endColumnNumber: getColNumber(statement, '%mend') + 6,
severity: Severity.Warning
})
} else if (macroName !== macroStarted) {
diagnostics.push({
message: 'mismatch macro name in %mend statement',
lineNumber: getLineNumber(statements, index + 1),
startColumnNumber: getColNumber(statement, macroName),
endColumnNumber:
getColNumber(statement, macroName) + macroName.length,
severity: Severity.Warning
})
}
}
})
if (stack.length) {
diagnostics.push({
message: 'missing %mend statement for macro(s)',
lineNumber: statements.length + 1,
startColumnNumber: 1,
endColumnNumber: 1,
severity: Severity.Warning
})
}
return diagnostics
}
const getLineNumber = (statements: string[], index: number): number => {
const combinedCode = statements.slice(0, index).join(';')
const lines = (combinedCode.match(/\n/g) || []).length + 1
return lines
}
const getColNumber = (statement: string, text: string): number => {
return statement.replace(/[\r\n]+/, '').indexOf(text) + 1
}
/**
* Lint rule that checks for the presence of macro name in %mend statement.
*/
export const hasMacroNameInMend: FileLintRule = {
type: LintRuleType.File,
name,
description,
message,
test
}