Skip to content

Commit cf9fe52

Browse files
dristpunkgas1cent
andauthored
feat: unnamed returns (#22)
Co-authored-by: Gas <[email protected]>
1 parent d5c64f7 commit cf9fe52

File tree

6 files changed

+142
-12
lines changed

6 files changed

+142
-12
lines changed

sample-data/BasicSample.sol

+9
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ contract BasicSample is AbstractBasic {
7676
return (true, 111);
7777
}
7878

79+
/**
80+
* @notice External function that returns a bool
81+
* @dev A dev comment
82+
* @return Some return data
83+
*/
84+
function externalSimpleMultipleUnnamedReturn() external pure returns (bool, uint256) {
85+
return (true, 111);
86+
}
87+
7988
/**
8089
* @notice This function should have an inheritdoc tag
8190
*/

sample-data/ParserTest.sol

+7
Original file line numberDiff line numberDiff line change
@@ -179,5 +179,12 @@ contract ParserTestFunny is IParserTest {
179179
function _viewLinterFail() internal pure {
180180

181181
}
182+
183+
/// fun fact: there are extra spaces after the 1st return
184+
/// @return
185+
/// @return
186+
function functionUnnamedEmptyReturn() external view returns (uint256, bool){
187+
return (1, true);
188+
}
182189
}
183190
// forgefmt: disable-end

src/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export function parseNodeNatspec(node: NodeToProcess): Natspec {
9595
result.inheritdoc = { content: tagMatch[2] };
9696
}
9797
} else if (tagName === 'param' || tagName === 'return') {
98-
const tagMatch = line.match(/^\s*@(\w+) *(\w+) (.*)$/);
98+
const tagMatch = line.match(/^\s*@(\w+) *(\w*) *(.*)$/);
9999
if (tagMatch) {
100100
currentTag = { name: tagMatch[2], content: tagMatch[3].trim() };
101101
result[tagName === 'param' ? 'params' : 'returns'].push(currentTag);

src/validator.ts

+6-10
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,13 @@ export class Validator {
7575
let functionReturns = node.vReturnParameters.vParameters.map((p) => p.name);
7676

7777
// Make sure all defined returns have natspec
78-
for (let paramName of functionReturns) {
79-
if (!natspecReturns.includes(paramName)) {
80-
let message = paramName === '' ? '@return missing for unnamed return' : `@return ${paramName} is missing`;
78+
for (let [paramIndex, paramName] of functionReturns.entries()) {
79+
if (paramIndex > natspecReturns.length - 1) {
80+
let message = paramName === '' ? `@return missing for unnamed return №${paramIndex + 1}` : `@return ${paramName} is missing`;
81+
alerts.push(message);
82+
} else if (natspecReturns[paramIndex] !== paramName && paramName !== '') {
83+
let message = `@return ${paramName} is missing`;
8184
alerts.push(message);
82-
}
83-
}
84-
85-
// Make sure there is no natspec defined for non-existing returns
86-
for (let paramName of natspecReturns) {
87-
if (paramName && !functionReturns.includes(paramName)) {
88-
alerts.push(`Missing named return for: @return ${paramName}`);
8985
}
9086
}
9187

test/parser.test.ts

+22
Original file line numberDiff line numberDiff line change
@@ -420,5 +420,27 @@ describe('Parser', () => {
420420
})
421421
);
422422
});
423+
424+
it('should correctly parse empty return tag', async () => {
425+
const node = contract.vFunctions.find(({ name }) => name === 'functionUnnamedEmptyReturn')!;
426+
const result = parseNodeNatspec(node);
427+
428+
expect(result).toEqual(
429+
mockNatspec({
430+
tags: [],
431+
params: [],
432+
returns: [
433+
{
434+
name: '',
435+
content: '',
436+
},
437+
{
438+
name: '',
439+
content: '',
440+
},
441+
],
442+
})
443+
);
444+
});
423445
});
424446
});

test/validator.test.ts

+97-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,103 @@ describe('Validator', () => {
153153
};
154154

155155
const result = validator.validate(node, natspec);
156-
expect(result).toContainEqual(`@return missing for unnamed return`);
156+
expect(result).toContainEqual(`@return missing for unnamed return №2`);
157+
});
158+
159+
it('should warn of a missing unnamed return', () => {
160+
node = contract.vFunctions.find(({ name }) => name === 'externalSimpleMultipleUnnamedReturn')!;
161+
let natspec = {
162+
tags: [
163+
{
164+
name: 'notice',
165+
content: 'External function that returns a bool',
166+
},
167+
{
168+
name: 'dev',
169+
content: 'A dev comment',
170+
},
171+
],
172+
params: [],
173+
returns: [
174+
{
175+
name: 'Some',
176+
content: 'return data',
177+
},
178+
],
179+
};
180+
181+
const result = validator.validate(node, natspec);
182+
expect(result).toEqual([`@return missing for unnamed return №2`]); // only 1 warning
183+
});
184+
185+
it('should warn all returns if the first natspec tag is missing', () => {
186+
node = contract.vFunctions.find(({ name }) => name === 'externalSimpleMultipleReturn')!;
187+
let natspec = {
188+
tags: [
189+
{
190+
name: 'notice',
191+
content: 'External function that returns a bool',
192+
},
193+
{
194+
name: 'dev',
195+
content: 'A dev comment',
196+
},
197+
],
198+
params: [
199+
{
200+
name: '_magicNumber',
201+
content: 'A parameter description',
202+
},
203+
{
204+
name: '_name',
205+
content: 'Another parameter description',
206+
},
207+
],
208+
returns: [
209+
{
210+
name: 'Some',
211+
content: 'return data',
212+
},
213+
],
214+
};
215+
216+
const result = validator.validate(node, natspec);
217+
expect(result).toEqual(['@return _isMagic is missing', '@return missing for unnamed return №2']); // 2 warnings
218+
});
219+
220+
it('should warn if the last natspec tag is missing', () => {
221+
node = contract.vFunctions.find(({ name }) => name === 'externalSimpleMultipleReturn')!;
222+
let natspec = {
223+
tags: [
224+
{
225+
name: 'notice',
226+
content: 'External function that returns a bool',
227+
},
228+
{
229+
name: 'dev',
230+
content: 'A dev comment',
231+
},
232+
],
233+
params: [
234+
{
235+
name: '_magicNumber',
236+
content: 'A parameter description',
237+
},
238+
{
239+
name: '_name',
240+
content: 'Another parameter description',
241+
},
242+
],
243+
returns: [
244+
{
245+
name: '_isMagic',
246+
content: 'Some return data',
247+
},
248+
],
249+
};
250+
251+
const result = validator.validate(node, natspec);
252+
expect(result).toEqual(['@return missing for unnamed return №2']); // 1 warnings
157253
});
158254

159255
// TODO: Check overridden functions, virtual, etc?

0 commit comments

Comments
 (0)