From 1808d9851ab44083438c21d4787971aab89d6b94 Mon Sep 17 00:00:00 2001 From: Saad Jutt Date: Wed, 7 Apr 2021 00:58:38 +0500 Subject: [PATCH] test: for hasMacroParentheses & noNestedMacros --- src/rules/hasMacroNameInMend.ts | 4 +- src/rules/hasMacroParentheses.spec.ts | 128 ++++++++++++++++++++++++++ src/rules/hasMacroParentheses.ts | 13 ++- src/rules/noNestedMacros.spec.ts | 78 ++++++++++++++++ src/rules/noNestedMacros.ts | 4 +- 5 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 src/rules/hasMacroParentheses.spec.ts create mode 100644 src/rules/noNestedMacros.spec.ts diff --git a/src/rules/hasMacroNameInMend.ts b/src/rules/hasMacroNameInMend.ts index 2efcbd2..2081f77 100644 --- a/src/rules/hasMacroNameInMend.ts +++ b/src/rules/hasMacroNameInMend.ts @@ -25,8 +25,8 @@ const test = (value: string) => { if (trimmedStatement.startsWith('%macro ')) { const macroName = trimmedStatement - .split(' ') - .filter((s: string) => !!s)[1] + .slice(7, trimmedStatement.length) + .trim() .split('(')[0] stack.push(macroName) } else if (trimmedStatement.startsWith('%mend')) { diff --git a/src/rules/hasMacroParentheses.spec.ts b/src/rules/hasMacroParentheses.spec.ts new file mode 100644 index 0000000..bb77ae7 --- /dev/null +++ b/src/rules/hasMacroParentheses.spec.ts @@ -0,0 +1,128 @@ +import { Severity } from '../types/Severity' +import { hasMacroParentheses } from './hasMacroParentheses' + +describe('hasMacroParentheses', () => { + it('should return an empty array when macro defined correctly', () => { + const content = ` + %macro somemacro(); + %put &sysmacroname; + %mend somemacro;` + + expect(hasMacroParentheses.test(content)).toEqual([]) + }) + + it('should return an array with a single diagnostics when macro defined without parentheses', () => { + const content = ` + %macro somemacro; + %put &sysmacroname; + %mend somemacro;` + + expect(hasMacroParentheses.test(content)).toEqual([ + { + message: 'Macro definition missing parentheses', + lineNumber: 2, + startColumnNumber: 10, + endColumnNumber: 18, + severity: Severity.Warning + } + ]) + }) + + it('should return an array with a single diagnostics when macro defined without name', () => { + const content = ` + %macro (); + %put &sysmacroname; + %mend;` + + expect(hasMacroParentheses.test(content)).toEqual([ + { + message: 'Macro definition missing name', + lineNumber: 2, + startColumnNumber: 3, + endColumnNumber: 12, + severity: Severity.Warning + } + ]) + }) + + it('should return an array with a single diagnostics when macro defined without name and parentheses', () => { + const content = ` + %macro ; + %put &sysmacroname; + %mend;` + + expect(hasMacroParentheses.test(content)).toEqual([ + { + message: 'Macro definition missing name', + lineNumber: 2, + startColumnNumber: 3, + endColumnNumber: 10, + severity: Severity.Warning + } + ]) + }) + + it('should return an empty array when the file is undefined', () => { + const content = undefined + + expect(hasMacroParentheses.test((content as unknown) as string)).toEqual([]) + }) + + describe('with extra spaces and comments', () => { + it('should return an empty array when %mend has correct macro name', () => { + const content = ` + /* 1st comment */ + %macro somemacro(); + + %put &sysmacroname; + + /* 2nd + comment */ + /* 3rd comment */ %mend somemacro ;` + + expect(hasMacroParentheses.test(content)).toEqual([]) + }) + + it('should return an array with a single diagnostic when macro defined without parentheses having code in comments', () => { + const content = `/** + @file examplemacro.sas + @brief an example of a macro to be used in a service + @details This macro is great. Yadda yadda yadda. Usage: + + * code formatting applies when indented by 4 spaces; code formatting applies when indented by 4 spaces; code formatting applies when indented by 4 spaces; code formatting applies when indented by 4 spaces; code formatting applies when indented by 4 spaces; + + some code + %macro examplemacro123(); + + %examplemacro() + +

SAS Macros

+ @li doesnothing.sas + + @author Allan Bowe + **/ + + %macro examplemacro; + + proc sql; + create table areas + as select area + + from sashelp.springs; + + %doesnothing(); + + %mend;` + + expect(hasMacroParentheses.test(content)).toEqual([ + { + message: 'Macro definition missing parentheses', + lineNumber: 19, + startColumnNumber: 12, + endColumnNumber: 23, + severity: Severity.Warning + } + ]) + }) + }) +}) diff --git a/src/rules/hasMacroParentheses.ts b/src/rules/hasMacroParentheses.ts index 3799cec..fb894fa 100644 --- a/src/rules/hasMacroParentheses.ts +++ b/src/rules/hasMacroParentheses.ts @@ -22,14 +22,23 @@ const test = (value: string) => { commentStarted )) - if (trimmedStatement.startsWith('%macro ')) { + if (trimmedStatement.startsWith('%macro')) { const macroNameDefinition = trimmedStatement .slice(7, trimmedStatement.length) .trim() const macroNameDefinitionParts = macroNameDefinition.split('(') const macroName = macroNameDefinitionParts[0] - if (macroNameDefinitionParts.length === 1) + + if (!macroName) + diagnostics.push({ + message: 'Macro definition missing name', + lineNumber: getLineNumber(statements, index + 1), + startColumnNumber: getColNumber(statement, '%macro'), + endColumnNumber: statement.length, + severity: Severity.Warning + }) + else if (macroNameDefinitionParts.length === 1) diagnostics.push({ message, lineNumber: getLineNumber(statements, index + 1), diff --git a/src/rules/noNestedMacros.spec.ts b/src/rules/noNestedMacros.spec.ts new file mode 100644 index 0000000..5d262b5 --- /dev/null +++ b/src/rules/noNestedMacros.spec.ts @@ -0,0 +1,78 @@ +import { Severity } from '../types/Severity' +import { noNestedMacros } from './noNestedMacros' + +describe('noNestedMacros', () => { + it('should return an empty array when no nested macro', () => { + const content = ` + %macro somemacro(); + %put &sysmacroname; + %mend somemacro;` + + expect(noNestedMacros.test(content)).toEqual([]) + }) + + it('should return an array with a single diagnostics when nested macro defined', () => { + const content = ` + %macro outer(); + /* any amount of arbitrary code */ + %macro inner(); + %put inner; + %mend; + %inner() + %put outer; + %mend; + + %outer()` + + expect(noNestedMacros.test(content)).toEqual([ + { + message: "Macro definition present inside another macro 'outer'", + lineNumber: 4, + startColumnNumber: 7, + endColumnNumber: 21, + severity: Severity.Warning + } + ]) + }) + + it('should return an array with a single diagnostics when nested macro defined 2 levels', () => { + const content = ` + %macro outer(); + /* any amount of arbitrary code */ + %macro inner(); + %put inner; + + %macro inner2(); + %put inner2; + %mend; + %mend; + %inner() + %put outer; + %mend; + + %outer()` + + expect(noNestedMacros.test(content)).toEqual([ + { + message: "Macro definition present inside another macro 'outer'", + lineNumber: 4, + startColumnNumber: 7, + endColumnNumber: 21, + severity: Severity.Warning + }, + { + message: "Macro definition present inside another macro 'inner'", + lineNumber: 7, + startColumnNumber: 17, + endColumnNumber: 32, + severity: Severity.Warning + } + ]) + }) + + it('should return an empty array when the file is undefined', () => { + const content = undefined + + expect(noNestedMacros.test((content as unknown) as string)).toEqual([]) + }) +}) diff --git a/src/rules/noNestedMacros.ts b/src/rules/noNestedMacros.ts index f7cb540..377ce81 100644 --- a/src/rules/noNestedMacros.ts +++ b/src/rules/noNestedMacros.ts @@ -25,8 +25,8 @@ const test = (value: string) => { if (trimmedStatement.startsWith('%macro ')) { const macroName = trimmedStatement - .split(' ') - .filter((s: string) => !!s)[1] + .slice(7, trimmedStatement.length) + .trim() .split('(')[0] if (stack.length) { const parentMacro = stack.slice(-1).pop()