1
0
mirror of https://github.com/sasjs/lint.git synced 2025-12-10 17:34:36 +00:00

Merge branch 'main' into dependabot/npm_and_yarn/ts-jest-26.5.6

This commit is contained in:
Krishna Acondy
2021-06-09 08:15:17 +01:00
committed by GitHub
10 changed files with 273 additions and 6 deletions

View File

@@ -16,7 +16,8 @@
"noSpacesInFileNames": true, "noSpacesInFileNames": true,
"noTabIndentation": true, "noTabIndentation": true,
"noTrailingSpaces": true, "noTrailingSpaces": true,
"lineEndings": "lf" "lineEndings": "lf",
"strictMacroDefinition": true
}, },
"examples": [ "examples": [
{ {
@@ -31,7 +32,8 @@
"hasMacroNameInMend": true, "hasMacroNameInMend": true,
"noNestedMacros": true, "noNestedMacros": true,
"hasMacroParentheses": true, "hasMacroParentheses": true,
"lineEndings": "crlf" "lineEndings": "crlf",
"strictMacroDefinition": true
} }
], ],
"properties": { "properties": {
@@ -130,6 +132,14 @@
"description": "Enforces the configured terminating character for each line. Shows a warning when incorrect line endings are present.", "description": "Enforces the configured terminating character for each line. Shows a warning when incorrect line endings are present.",
"default": "lf", "default": "lf",
"examples": ["lf", "crlf"] "examples": ["lf", "crlf"]
},
"strictMacroDefinition": {
"$id": "#/properties/strictMacroDefinition",
"type": "boolean",
"title": "strictMacroDefinition",
"description": "Enforces Macro Definition syntax. Shows a warning when incorrect syntax is used.",
"default": true,
"examples": [true, false]
} }
} }
} }

View File

@@ -3,3 +3,4 @@ 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'

View File

@@ -0,0 +1,88 @@
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([])
})
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 param', () => {
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 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
}
])
})
})

View File

@@ -0,0 +1,88 @@
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()
if (trimedParam.includes(' ')) {
diagnostics.push({
message: `Param '${trimedParam}' cannot have space`,
lineNumber,
startColumnNumber: value.indexOf(trimedParam) + 1,
endColumnNumber: value.indexOf(trimedParam) + trimedParam.length,
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
}

View File

@@ -10,7 +10,8 @@ import {
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'
@@ -90,5 +91,9 @@ export class LintConfig {
if (json?.hasMacroParentheses) { if (json?.hasMacroParentheses) {
this.fileLintRules.push(hasMacroParentheses) this.fileLintRules.push(hasMacroParentheses)
} }
if (json?.strictMacroDefinition) {
this.lineLintRules.push(strictMacroDefinition)
}
} }
} }

View File

@@ -3,7 +3,7 @@ import { LintConfig } from '../types/LintConfig'
import { getLintConfig } from './getLintConfig' import { getLintConfig } from './getLintConfig'
const expectedFileLintRulesCount = 4 const expectedFileLintRulesCount = 4
const expectedLineLintRulesCount = 5 const expectedLineLintRulesCount = 6
const expectedPathLintRulesCount = 2 const expectedPathLintRulesCount = 2
describe('getLintConfig', () => { describe('getLintConfig', () => {

View File

@@ -17,7 +17,8 @@ export const DefaultLintConfiguration = {
indentationMultiple: 2, indentationMultiple: 2,
hasMacroNameInMend: true, hasMacroNameInMend: true,
noNestedMacros: true, noNestedMacros: true,
hasMacroParentheses: true hasMacroParentheses: true,
strictMacroDefinition: true
} }
/** /**

View File

@@ -25,6 +25,76 @@ describe('parseMacros', () => {
}) })
}) })
it('should return an array with a single macro having parameters', () => {
const text = `%macro test(var,sum);
%put 'hello';
%mend`
const macros = parseMacros(text, new LintConfig())
expect(macros.length).toEqual(1)
expect(macros).toContainEqual({
name: 'test',
declarationLine: '%macro test(var,sum);',
terminationLine: '%mend',
declaration: '%macro test(var,sum)',
termination: '%mend',
startLineNumber: 1,
endLineNumber: 3,
parentMacro: '',
hasMacroNameInMend: false,
hasParentheses: false,
mismatchedMendMacroName: ''
})
})
it('should return an array with a single macro having PARMBUFF option', () => {
const text = `%macro test/parmbuff;
%put 'hello';
%mend`
const macros = parseMacros(text, new LintConfig())
expect(macros.length).toEqual(1)
expect(macros).toContainEqual({
name: 'test',
declarationLine: '%macro test/parmbuff;',
terminationLine: '%mend',
declaration: '%macro test/parmbuff',
termination: '%mend',
startLineNumber: 1,
endLineNumber: 3,
parentMacro: '',
hasMacroNameInMend: false,
hasParentheses: false,
mismatchedMendMacroName: ''
})
})
it('should return an array with a single macro having paramerter & SOURCE option', () => {
const text = `/* commentary */ %macro foobar(arg) /store source
des="This macro does not do much";
%put 'hello';
%mend`
const macros = parseMacros(text, new LintConfig())
expect(macros.length).toEqual(1)
expect(macros).toContainEqual({
name: 'foobar',
declarationLine: '/* commentary */ %macro foobar(arg) /store source',
terminationLine: '%mend',
declaration: '%macro foobar(arg) /store source',
termination: '%mend',
startLineNumber: 1,
endLineNumber: 4,
parentMacro: '',
hasMacroNameInMend: false,
hasParentheses: false,
mismatchedMendMacroName: ''
})
})
it('should return an array with multiple macros', () => { it('should return an array with multiple macros', () => {
const text = `%macro foo; const text = `%macro foo;
%put 'foo'; %put 'foo';

View File

@@ -43,6 +43,7 @@ export const parseMacros = (text: string, config?: LintConfig): Macro[] => {
const name = trimmedStatement const name = trimmedStatement
.slice(7, trimmedStatement.length) .slice(7, trimmedStatement.length)
.trim() .trim()
.split('/')[0]
.split('(')[0] .split('(')[0]
macroStack.push({ macroStack.push({
name, name,

View File

@@ -6,11 +6,14 @@ export const trimComments = (
if (commentStarted || trimmed.startsWith('/*')) { if (commentStarted || trimmed.startsWith('/*')) {
const parts = trimmed.split('*/') const parts = trimmed.split('*/')
if (parts.length > 1) { if (parts.length === 2) {
return { return {
statement: (parts.pop() as string).trim(), statement: (parts.pop() as string).trim(),
commentStarted: false commentStarted: false
} }
} else if (parts.length > 2) {
parts.shift()
return trimComments(parts.join('*/'), false)
} else { } else {
return { statement: '', commentStarted: true } return { statement: '', commentStarted: true }
} }