mirror of
https://github.com/sasjs/lint.git
synced 2026-01-06 20:20:06 +00:00
fix(strictMacroDefinition): moved from lineRules to fileRules
This commit is contained in:
@@ -3,3 +3,4 @@ export { hasMacroNameInMend } from './hasMacroNameInMend'
|
|||||||
export { hasMacroParentheses } from './hasMacroParentheses'
|
export { hasMacroParentheses } from './hasMacroParentheses'
|
||||||
export { lineEndings } from './lineEndings'
|
export { lineEndings } from './lineEndings'
|
||||||
export { noNestedMacros } from './noNestedMacros'
|
export { noNestedMacros } from './noNestedMacros'
|
||||||
|
export { strictMacroDefinition } from './strictMacroDefinition'
|
||||||
|
|||||||
204
src/rules/file/strictMacroDefinition.spec.ts
Normal file
204
src/rules/file/strictMacroDefinition.spec.ts
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import { LintConfig, Severity } from '../../types'
|
||||||
|
import { strictMacroDefinition } from './strictMacroDefinition'
|
||||||
|
|
||||||
|
describe('strictMacroDefinition', () => {
|
||||||
|
it('should return an empty array when the content has correct macro definition syntax', () => {
|
||||||
|
const content = '%macro somemacro;'
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([])
|
||||||
|
|
||||||
|
const content2 = '%macro somemacro();'
|
||||||
|
expect(strictMacroDefinition.test(content2)).toEqual([])
|
||||||
|
|
||||||
|
const content3 = '%macro somemacro(var1);'
|
||||||
|
expect(strictMacroDefinition.test(content3)).toEqual([])
|
||||||
|
|
||||||
|
const content4 = '%macro somemacro/minoperator;'
|
||||||
|
expect(strictMacroDefinition.test(content4)).toEqual([])
|
||||||
|
|
||||||
|
const content5 = '%macro somemacro /minoperator;'
|
||||||
|
expect(strictMacroDefinition.test(content5)).toEqual([])
|
||||||
|
|
||||||
|
const content6 = '%macro somemacro(var1, var2)/minoperator;'
|
||||||
|
expect(strictMacroDefinition.test(content6)).toEqual([])
|
||||||
|
|
||||||
|
const content7 =
|
||||||
|
' /* Some Comment */ %macro somemacro(var1, var2) /minoperator ; /* Some Comment */'
|
||||||
|
expect(strictMacroDefinition.test(content7)).toEqual([])
|
||||||
|
|
||||||
|
const content8 =
|
||||||
|
'%macro macroName( arr, arr/* / store source */3 ) /* / store source */;/* / store source */'
|
||||||
|
expect(strictMacroDefinition.test(content8)).toEqual([])
|
||||||
|
|
||||||
|
const content9 = '%macro macroName(var1, var2=with space, var3=);'
|
||||||
|
expect(strictMacroDefinition.test(content9)).toEqual([])
|
||||||
|
|
||||||
|
const content10 = '%macro macroName()/ /* some comment */ store source;'
|
||||||
|
expect(strictMacroDefinition.test(content10)).toEqual([])
|
||||||
|
|
||||||
|
const content11 = '`%macro macroName() /* / store source */;'
|
||||||
|
expect(strictMacroDefinition.test(content11)).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a single diagnostic when Macro definition has space in param', () => {
|
||||||
|
const content = '%macro somemacro(va r1);'
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Param 'va r1' cannot have space`,
|
||||||
|
lineNumber: 1,
|
||||||
|
startColumnNumber: 18,
|
||||||
|
endColumnNumber: 22,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a two diagnostics when Macro definition has space in params', () => {
|
||||||
|
const content = '%macro somemacro(var1, var 2, v ar3, var4);'
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Param 'var 2' cannot have space`,
|
||||||
|
lineNumber: 1,
|
||||||
|
startColumnNumber: 24,
|
||||||
|
endColumnNumber: 28,
|
||||||
|
severity: Severity.Warning
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: `Param 'v ar3' cannot have space`,
|
||||||
|
lineNumber: 1,
|
||||||
|
startColumnNumber: 31,
|
||||||
|
endColumnNumber: 35,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a two diagnostics when Macro definition has space in params - special case', () => {
|
||||||
|
const content =
|
||||||
|
'%macro macroName( arr, ar r/* / store source */ 3 ) /* / store source */;/* / store source */'
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Param 'ar r 3' cannot have space`,
|
||||||
|
lineNumber: 1,
|
||||||
|
startColumnNumber: 24,
|
||||||
|
endColumnNumber: 49,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a single diagnostic when Macro definition has invalid option', () => {
|
||||||
|
const content = '%macro somemacro(var1, var2)/minXoperator;'
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Option 'minXoperator' is not valid`,
|
||||||
|
lineNumber: 1,
|
||||||
|
startColumnNumber: 30,
|
||||||
|
endColumnNumber: 41,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a two diagnostics when Macro definition has invalid options', () => {
|
||||||
|
const content =
|
||||||
|
'%macro somemacro(var1, var2)/ store invalidoption secure ;'
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Option 'invalidoption' is not valid`,
|
||||||
|
lineNumber: 1,
|
||||||
|
startColumnNumber: 39,
|
||||||
|
endColumnNumber: 51,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('multi-content macro declarations', () => {
|
||||||
|
it('should return an array with a single diagnostic when Macro definition has space in param', () => {
|
||||||
|
const content = `%macro
|
||||||
|
somemacro(va r1);`
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Param 'va r1' cannot have space`,
|
||||||
|
lineNumber: 2,
|
||||||
|
startColumnNumber: 18,
|
||||||
|
endColumnNumber: 22,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a two diagnostics when Macro definition has space in params', () => {
|
||||||
|
const content = `%macro somemacro(
|
||||||
|
var1,
|
||||||
|
var 2,
|
||||||
|
v ar3,
|
||||||
|
var4);`
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Param 'var 2' cannot have space`,
|
||||||
|
lineNumber: 3,
|
||||||
|
startColumnNumber: 7,
|
||||||
|
endColumnNumber: 11,
|
||||||
|
severity: Severity.Warning
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: `Param 'v ar3' cannot have space`,
|
||||||
|
lineNumber: 4,
|
||||||
|
startColumnNumber: 7,
|
||||||
|
endColumnNumber: 11,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a two diagnostics when Macro definition has space in params - special case', () => {
|
||||||
|
const content = `%macro macroName(
|
||||||
|
arr,
|
||||||
|
ar r/* / store source */ 3
|
||||||
|
) /* / store source */;/* / store source */`
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Param 'ar r 3' cannot have space`,
|
||||||
|
lineNumber: 3,
|
||||||
|
startColumnNumber: 7,
|
||||||
|
endColumnNumber: 32,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a single diagnostic when Macro definition has invalid option', () => {
|
||||||
|
const content = `%macro somemacro(var1, var2)
|
||||||
|
/minXoperator;`
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Option 'minXoperator' is not valid`,
|
||||||
|
lineNumber: 2,
|
||||||
|
startColumnNumber: 8,
|
||||||
|
endColumnNumber: 19,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an array with a two diagnostics when Macro definition has invalid options', () => {
|
||||||
|
const content = `%macro
|
||||||
|
somemacro(
|
||||||
|
var1, var2
|
||||||
|
)
|
||||||
|
/ store
|
||||||
|
invalidoption
|
||||||
|
secure ;`
|
||||||
|
expect(strictMacroDefinition.test(content)).toEqual([
|
||||||
|
{
|
||||||
|
message: `Option 'invalidoption' is not valid`,
|
||||||
|
lineNumber: 6,
|
||||||
|
startColumnNumber: 16,
|
||||||
|
endColumnNumber: 28,
|
||||||
|
severity: Severity.Warning
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
140
src/rules/file/strictMacroDefinition.ts
Normal file
140
src/rules/file/strictMacroDefinition.ts
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import { Diagnostic } from '../../types/Diagnostic'
|
||||||
|
import { LintConfig } from '../../types'
|
||||||
|
import { FileLintRule } from '../../types/LintRule'
|
||||||
|
import { LintRuleType } from '../../types/LintRuleType'
|
||||||
|
import { Severity } from '../../types/Severity'
|
||||||
|
import { parseMacros } from '../../utils/parseMacros'
|
||||||
|
|
||||||
|
const name = 'strictMacroDefinition'
|
||||||
|
const description = 'Enforce strictly rules of macro definition syntax.'
|
||||||
|
const message = 'Incorrent Macro Definition Syntax'
|
||||||
|
|
||||||
|
const validOptions = [
|
||||||
|
'CMD',
|
||||||
|
'DES',
|
||||||
|
'MINDELIMITER',
|
||||||
|
'MINOPERATOR',
|
||||||
|
'NOMINOPERATOR',
|
||||||
|
'PARMBUFF',
|
||||||
|
'SECURE',
|
||||||
|
'NOSECURE',
|
||||||
|
'STMT',
|
||||||
|
'SOURCE',
|
||||||
|
'SRC',
|
||||||
|
'STORE'
|
||||||
|
]
|
||||||
|
|
||||||
|
const test = (value: string, config?: LintConfig) => {
|
||||||
|
const diagnostics: Diagnostic[] = []
|
||||||
|
|
||||||
|
const macros = parseMacros(value, config)
|
||||||
|
|
||||||
|
macros.forEach((macro) => {
|
||||||
|
const declaration = macro.declaration
|
||||||
|
|
||||||
|
const regExpParams = new RegExp(/\((.*?)\)/)
|
||||||
|
const regExpParamsResult = regExpParams.exec(declaration)
|
||||||
|
|
||||||
|
let _declaration = declaration
|
||||||
|
if (regExpParamsResult) {
|
||||||
|
const paramsPresent = regExpParamsResult[1]
|
||||||
|
|
||||||
|
const paramsTrimmed = paramsPresent.trim()
|
||||||
|
const params = paramsTrimmed.split(',')
|
||||||
|
params.forEach((param) => {
|
||||||
|
const trimedParam = param.split('=')[0].trim()
|
||||||
|
|
||||||
|
let paramLineNumber: number = 1,
|
||||||
|
paramStartIndex: number = 1,
|
||||||
|
paramEndIndex: number = value.length
|
||||||
|
|
||||||
|
if (
|
||||||
|
macro.declarationLines.findIndex(
|
||||||
|
(dl) => dl.indexOf(trimedParam) !== -1
|
||||||
|
) === -1
|
||||||
|
) {
|
||||||
|
const comment = '/\\*(.*?)\\*/'
|
||||||
|
for (let i = 1; i < trimedParam.length; i++) {
|
||||||
|
const paramWithComment =
|
||||||
|
trimedParam.slice(0, i) + comment + trimedParam.slice(i)
|
||||||
|
const regEx = new RegExp(paramWithComment)
|
||||||
|
|
||||||
|
const declarationLineIndex = macro.declarationLines.findIndex(
|
||||||
|
(dl) => !!regEx.exec(dl)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (declarationLineIndex !== -1) {
|
||||||
|
const declarationLine =
|
||||||
|
macro.declarationLines[declarationLineIndex]
|
||||||
|
const partFound = regEx.exec(declarationLine)![0]
|
||||||
|
|
||||||
|
paramLineNumber = macro.startLineNumbers[declarationLineIndex]
|
||||||
|
paramStartIndex = declarationLine.indexOf(partFound)
|
||||||
|
paramEndIndex =
|
||||||
|
declarationLine.indexOf(partFound) + partFound.length
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const declarationLineIndex = macro.declarationLines.findIndex(
|
||||||
|
(dl) => dl.indexOf(trimedParam) !== -1
|
||||||
|
)
|
||||||
|
const declarationLine = macro.declarationLines[declarationLineIndex]
|
||||||
|
paramLineNumber = macro.startLineNumbers[declarationLineIndex]
|
||||||
|
|
||||||
|
paramStartIndex = declarationLine.indexOf(trimedParam)
|
||||||
|
paramEndIndex =
|
||||||
|
declarationLine.indexOf(trimedParam) + trimedParam.length
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimedParam.includes(' ')) {
|
||||||
|
diagnostics.push({
|
||||||
|
message: `Param '${trimedParam}' cannot have space`,
|
||||||
|
lineNumber: paramLineNumber,
|
||||||
|
startColumnNumber: paramStartIndex + 1,
|
||||||
|
endColumnNumber: paramEndIndex,
|
||||||
|
severity: Severity.Warning
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
_declaration = declaration.split(`(${paramsPresent})`)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionsPresent = _declaration.split('/')?.[1]?.trim().split(' ')
|
||||||
|
|
||||||
|
optionsPresent
|
||||||
|
?.filter((o) => !!o)
|
||||||
|
.forEach((option) => {
|
||||||
|
const trimmedOption = option.trim()
|
||||||
|
if (!validOptions.includes(trimmedOption.toUpperCase())) {
|
||||||
|
const declarationLineIndex = macro.declarationLines.findIndex(
|
||||||
|
(dl) => dl.indexOf(trimmedOption) !== -1
|
||||||
|
)
|
||||||
|
const declarationLine = macro.declarationLines[declarationLineIndex]
|
||||||
|
|
||||||
|
diagnostics.push({
|
||||||
|
message: `Option '${trimmedOption}' is not valid`,
|
||||||
|
lineNumber: macro.startLineNumbers[declarationLineIndex],
|
||||||
|
startColumnNumber: declarationLine.indexOf(trimmedOption) + 1,
|
||||||
|
endColumnNumber:
|
||||||
|
declarationLine.indexOf(trimmedOption) + trimmedOption.length,
|
||||||
|
severity: Severity.Warning
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lint rule that checks if a line has followed syntax for macro definition
|
||||||
|
*/
|
||||||
|
export const strictMacroDefinition: FileLintRule = {
|
||||||
|
type: LintRuleType.File,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
message,
|
||||||
|
test
|
||||||
|
}
|
||||||
@@ -3,4 +3,3 @@ export { maxLineLength } from './maxLineLength'
|
|||||||
export { noEncodedPasswords } from './noEncodedPasswords'
|
export { noEncodedPasswords } from './noEncodedPasswords'
|
||||||
export { noTabIndentation } from './noTabIndentation'
|
export { noTabIndentation } from './noTabIndentation'
|
||||||
export { noTrailingSpaces } from './noTrailingSpaces'
|
export { noTrailingSpaces } from './noTrailingSpaces'
|
||||||
export { strictMacroDefinition } from './strictMacroDefinition'
|
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
import { LintConfig, Severity } from '../../types'
|
|
||||||
import { strictMacroDefinition } from './strictMacroDefinition'
|
|
||||||
|
|
||||||
describe('strictMacroDefinition', () => {
|
|
||||||
it('should return an empty array when the line has correct macro definition syntax', () => {
|
|
||||||
const line = '%macro somemacro;'
|
|
||||||
expect(strictMacroDefinition.test(line, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line2 = '%macro somemacro();'
|
|
||||||
expect(strictMacroDefinition.test(line2, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line3 = '%macro somemacro(var1);'
|
|
||||||
expect(strictMacroDefinition.test(line3, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line4 = '%macro somemacro/minoperator;'
|
|
||||||
expect(strictMacroDefinition.test(line4, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line5 = '%macro somemacro /minoperator;'
|
|
||||||
expect(strictMacroDefinition.test(line5, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line6 = '%macro somemacro(var1, var2)/minoperator;'
|
|
||||||
expect(strictMacroDefinition.test(line6, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line7 =
|
|
||||||
' /* Some Comment */ %macro somemacro(var1, var2) /minoperator ; /* Some Comment */'
|
|
||||||
expect(strictMacroDefinition.test(line7, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line8 =
|
|
||||||
'%macro macroName( arr, arr/* / store source */3 ) /* / store source */;/* / store source */'
|
|
||||||
expect(strictMacroDefinition.test(line8, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line9 = '%macro macroName(var1, var2=with space, var3=);'
|
|
||||||
expect(strictMacroDefinition.test(line9, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line10 = '%macro macroName()/ /* some comment */ store source;'
|
|
||||||
expect(strictMacroDefinition.test(line10, 1)).toEqual([])
|
|
||||||
|
|
||||||
const line11 = '`%macro macroName() /* / store source */;'
|
|
||||||
expect(strictMacroDefinition.test(line11, 1)).toEqual([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return an array with a single diagnostic when Macro definition has space in param', () => {
|
|
||||||
const line = '%macro somemacro(va r1);'
|
|
||||||
expect(strictMacroDefinition.test(line, 1)).toEqual([
|
|
||||||
{
|
|
||||||
message: `Param 'va r1' cannot have space`,
|
|
||||||
lineNumber: 1,
|
|
||||||
startColumnNumber: 18,
|
|
||||||
endColumnNumber: 22,
|
|
||||||
severity: Severity.Warning
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return an array with a two diagnostics when Macro definition has space in params', () => {
|
|
||||||
const line = '%macro somemacro(var1, var 2, v ar3, var4);'
|
|
||||||
expect(strictMacroDefinition.test(line, 1)).toEqual([
|
|
||||||
{
|
|
||||||
message: `Param 'var 2' cannot have space`,
|
|
||||||
lineNumber: 1,
|
|
||||||
startColumnNumber: 24,
|
|
||||||
endColumnNumber: 28,
|
|
||||||
severity: Severity.Warning
|
|
||||||
},
|
|
||||||
{
|
|
||||||
message: `Param 'v ar3' cannot have space`,
|
|
||||||
lineNumber: 1,
|
|
||||||
startColumnNumber: 31,
|
|
||||||
endColumnNumber: 35,
|
|
||||||
severity: Severity.Warning
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return an array with a two diagnostics when Macro definition has space in params - special case', () => {
|
|
||||||
const line =
|
|
||||||
'%macro macroName( arr, ar r/* / store source */ 3 ) /* / store source */;/* / store source */'
|
|
||||||
expect(strictMacroDefinition.test(line, 1)).toEqual([
|
|
||||||
{
|
|
||||||
message: `Param 'ar r 3' cannot have space`,
|
|
||||||
lineNumber: 1,
|
|
||||||
startColumnNumber: 24,
|
|
||||||
endColumnNumber: 49,
|
|
||||||
severity: Severity.Warning
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return an array with a single diagnostic when Macro definition has invalid option', () => {
|
|
||||||
const line = '%macro somemacro(var1, var2)/minXoperator;'
|
|
||||||
expect(strictMacroDefinition.test(line, 1)).toEqual([
|
|
||||||
{
|
|
||||||
message: `Option 'minXoperator' is not valid`,
|
|
||||||
lineNumber: 1,
|
|
||||||
startColumnNumber: 30,
|
|
||||||
endColumnNumber: 41,
|
|
||||||
severity: Severity.Warning
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return an array with a two diagnostics when Macro definition has invalid options', () => {
|
|
||||||
const line =
|
|
||||||
'%macro somemacro(var1, var2)/ store invalidoption secure ;'
|
|
||||||
expect(strictMacroDefinition.test(line, 1)).toEqual([
|
|
||||||
{
|
|
||||||
message: `Option 'invalidoption' is not valid`,
|
|
||||||
lineNumber: 1,
|
|
||||||
startColumnNumber: 39,
|
|
||||||
endColumnNumber: 51,
|
|
||||||
severity: Severity.Warning
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import { Diagnostic } from '../../types/Diagnostic'
|
|
||||||
import { LintConfig } from '../../types'
|
|
||||||
import { LineLintRule } from '../../types/LintRule'
|
|
||||||
import { LintRuleType } from '../../types/LintRuleType'
|
|
||||||
import { Severity } from '../../types/Severity'
|
|
||||||
import { parseMacros } from '../../utils/parseMacros'
|
|
||||||
|
|
||||||
const name = 'strictMacroDefinition'
|
|
||||||
const description = 'Enforce strictly rules of macro definition syntax.'
|
|
||||||
const message = 'Incorrent Macro Definition Syntax'
|
|
||||||
|
|
||||||
const validOptions = [
|
|
||||||
'CMD',
|
|
||||||
'DES',
|
|
||||||
'MINDELIMITER',
|
|
||||||
'MINOPERATOR',
|
|
||||||
'NOMINOPERATOR',
|
|
||||||
'PARMBUFF',
|
|
||||||
'SECURE',
|
|
||||||
'NOSECURE',
|
|
||||||
'STMT',
|
|
||||||
'SOURCE',
|
|
||||||
'SRC',
|
|
||||||
'STORE'
|
|
||||||
]
|
|
||||||
|
|
||||||
const test = (value: string, lineNumber: number) => {
|
|
||||||
const diagnostics: Diagnostic[] = []
|
|
||||||
|
|
||||||
const macros = parseMacros(value)
|
|
||||||
const declaration = macros[0]?.declaration
|
|
||||||
if (!declaration) return []
|
|
||||||
|
|
||||||
const regExpParams = new RegExp(/\((.*?)\)/)
|
|
||||||
const regExpParamsResult = regExpParams.exec(declaration)
|
|
||||||
|
|
||||||
let _declaration = declaration
|
|
||||||
if (regExpParamsResult) {
|
|
||||||
const paramsPresent = regExpParamsResult[1]
|
|
||||||
|
|
||||||
const paramsTrimmed = paramsPresent.trim()
|
|
||||||
const params = paramsTrimmed.split(',')
|
|
||||||
params.forEach((param) => {
|
|
||||||
const trimedParam = param.split('=')[0].trim()
|
|
||||||
|
|
||||||
let paramStartIndex: number = 1,
|
|
||||||
paramEndIndex: number = value.length
|
|
||||||
|
|
||||||
if (value.indexOf(trimedParam) === -1) {
|
|
||||||
const comment = '/\\*(.*?)\\*/'
|
|
||||||
for (let i = 1; i < trimedParam.length; i++) {
|
|
||||||
const paramWithComment =
|
|
||||||
trimedParam.slice(0, i) + comment + trimedParam.slice(i)
|
|
||||||
const regEx = new RegExp(paramWithComment)
|
|
||||||
const result = regEx.exec(value)
|
|
||||||
if (result) {
|
|
||||||
paramStartIndex = value.indexOf(result[0])
|
|
||||||
paramEndIndex = value.indexOf(result[0]) + result[0].length
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
paramStartIndex = value.indexOf(trimedParam)
|
|
||||||
paramEndIndex = value.indexOf(trimedParam) + trimedParam.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trimedParam.includes(' ')) {
|
|
||||||
diagnostics.push({
|
|
||||||
message: `Param '${trimedParam}' cannot have space`,
|
|
||||||
lineNumber,
|
|
||||||
startColumnNumber: paramStartIndex + 1,
|
|
||||||
endColumnNumber: paramEndIndex,
|
|
||||||
severity: Severity.Warning
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
_declaration = declaration.split(`(${paramsPresent})`)[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
const optionsPresent = _declaration.split('/')?.[1]?.trim().split(' ')
|
|
||||||
|
|
||||||
optionsPresent
|
|
||||||
?.filter((o) => !!o)
|
|
||||||
.forEach((option) => {
|
|
||||||
const trimmedOption = option.trim()
|
|
||||||
if (!validOptions.includes(trimmedOption.toUpperCase())) {
|
|
||||||
diagnostics.push({
|
|
||||||
message: `Option '${trimmedOption}' is not valid`,
|
|
||||||
lineNumber,
|
|
||||||
startColumnNumber: value.indexOf(trimmedOption) + 1,
|
|
||||||
endColumnNumber: value.indexOf(trimmedOption) + trimmedOption.length,
|
|
||||||
severity: Severity.Warning
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return diagnostics
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lint rule that checks if a line has followed syntax for macro definition
|
|
||||||
*/
|
|
||||||
export const strictMacroDefinition: LineLintRule = {
|
|
||||||
type: LintRuleType.Line,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
message,
|
|
||||||
test
|
|
||||||
}
|
|
||||||
@@ -3,15 +3,15 @@ import {
|
|||||||
hasMacroNameInMend,
|
hasMacroNameInMend,
|
||||||
noNestedMacros,
|
noNestedMacros,
|
||||||
hasMacroParentheses,
|
hasMacroParentheses,
|
||||||
lineEndings
|
lineEndings,
|
||||||
|
strictMacroDefinition
|
||||||
} from '../rules/file'
|
} from '../rules/file'
|
||||||
import {
|
import {
|
||||||
indentationMultiple,
|
indentationMultiple,
|
||||||
maxLineLength,
|
maxLineLength,
|
||||||
noEncodedPasswords,
|
noEncodedPasswords,
|
||||||
noTabIndentation,
|
noTabIndentation,
|
||||||
noTrailingSpaces,
|
noTrailingSpaces
|
||||||
strictMacroDefinition
|
|
||||||
} from '../rules/line'
|
} from '../rules/line'
|
||||||
import { lowerCaseFileNames, noSpacesInFileNames } from '../rules/path'
|
import { lowerCaseFileNames, noSpacesInFileNames } from '../rules/path'
|
||||||
import { LineEndings } from './LineEndings'
|
import { LineEndings } from './LineEndings'
|
||||||
@@ -93,7 +93,7 @@ export class LintConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (json?.strictMacroDefinition) {
|
if (json?.strictMacroDefinition) {
|
||||||
this.lineLintRules.push(strictMacroDefinition)
|
this.fileLintRules.push(strictMacroDefinition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import * as fileModule from '@sasjs/utils/file'
|
|||||||
import { LintConfig } from '../types/LintConfig'
|
import { LintConfig } from '../types/LintConfig'
|
||||||
import { getLintConfig } from './getLintConfig'
|
import { getLintConfig } from './getLintConfig'
|
||||||
|
|
||||||
const expectedFileLintRulesCount = 4
|
const expectedFileLintRulesCount = 5
|
||||||
const expectedLineLintRulesCount = 6
|
const expectedLineLintRulesCount = 5
|
||||||
const expectedPathLintRulesCount = 2
|
const expectedPathLintRulesCount = 2
|
||||||
|
|
||||||
describe('getLintConfig', () => {
|
describe('getLintConfig', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user