Skip to content

Commit 665234e

Browse files
committed
add source location to errors in natspec parsing
1 parent 8143f60 commit 665234e

File tree

5 files changed

+35
-19
lines changed

5 files changed

+35
-19
lines changed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
},
2626
"dependencies": {
2727
"handlebars": "^4.7.7",
28-
"solidity-ast": "^0.4.31"
28+
"solidity-ast": "^0.4.38"
2929
},
3030
"peerDependencies": {
3131
"hardhat": "^2.8.0"

src/site.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path from 'path';
22
import { ContractDefinition, SourceUnit } from 'solidity-ast';
33
import { SolcOutput, SolcInput } from 'solidity-ast/solc';
4-
import { astDereferencer, ASTDereferencer, findAll, isNodeType } from 'solidity-ast/utils';
4+
import { astDereferencer, ASTDereferencer, findAll, isNodeType, srcDecoder, SrcDecoder } from 'solidity-ast/utils';
55
import { FullConfig } from './config';
66
import { DocItem, docItemTypes, isDocItem } from './doc-item';
77
import { Properties } from './templates';
@@ -17,6 +17,7 @@ export interface Build {
1717

1818
export interface BuildContext extends Build {
1919
deref: ASTDereferencer;
20+
decodeSrc: SrcDecoder;
2021
}
2122

2223
export type SiteConfig = Pick<FullConfig, 'pages' | 'exclude' | 'sourcesDir' | 'pageExtension'>;
@@ -63,7 +64,8 @@ export function buildSite(builds: Build[], siteConfig: SiteConfig, properties: P
6364
output = { ...output, sources: clone(output.sources) };
6465

6566
const deref = astDereferencer(output);
66-
const build = { input, output, deref };
67+
const decodeSrc = srcDecoder(input, output);
68+
const build = { input, output, deref, decodeSrc };
6769

6870
for (const { ast: file } of Object.values(output.sources)) {
6971
const isNewFile = !seen.has(file.absolutePath);

src/utils/ItemError.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { DocItemWithContext, DOC_ITEM_CONTEXT } from '../site';
2+
3+
export class ItemError extends Error {
4+
constructor(msg: string, item: DocItemWithContext) {
5+
const ctx = item[DOC_ITEM_CONTEXT];
6+
const src = ctx && ctx.build.decodeSrc(item);
7+
if (src) {
8+
super(msg + ` (${src})`);
9+
} else {
10+
super(msg);
11+
}
12+
}
13+
}

src/utils/natspec.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { FunctionDefinition } from 'solidity-ast';
22
import { findAll } from 'solidity-ast/utils';
3-
import { DocItemWithContext } from '../site';
3+
import { DocItemWithContext, DOC_ITEM_CONTEXT } from '../site';
44
import { arraysEqual } from './arrays-equal';
55
import { execAll } from './execall';
66
import { itemType } from './item-type';
7+
import { ItemError } from './ItemError';
78
import { readItemDocs } from './read-item-docs';
89
import { getContractsInScope } from './scope';
910

@@ -25,7 +26,7 @@ export interface NatSpec {
2526
}
2627

2728
export function parseNatspec(item: DocItemWithContext): NatSpec {
28-
if (!item.__item_context) throw new Error(`Not an item or item is missing context`);
29+
if (!item[DOC_ITEM_CONTEXT]) throw new Error(`Not an item or item is missing context`);
2930

3031
let res: NatSpec = {};
3132

@@ -46,7 +47,7 @@ export function parseNatspec(item: DocItemWithContext): NatSpec {
4647
let inheritFrom: FunctionDefinition | undefined;
4748

4849
for (const [, tag = 'notice', content] of tagMatches) {
49-
if (content === undefined) throw new Error('Unexpected error');
50+
if (content === undefined) throw new ItemError('Unexpected error', item);
5051

5152
if (tag === 'dev' || tag === 'notice') {
5253
res[tag] ??= '';
@@ -68,20 +69,20 @@ export function parseNatspec(item: DocItemWithContext): NatSpec {
6869

6970
if (tag === 'return') {
7071
if (!('returnParameters' in item)) {
71-
throw new Error(`Item does not contain return parameters`);
72+
throw new ItemError(`Item does not contain return parameters`, item);
7273
}
7374
res.returns ??= [];
7475
const i = res.returns.length;
7576
const p = item.returnParameters.parameters[i];
7677
if (p === undefined) {
77-
throw new Error(`Got more @return tags than expected for '${item.name}'`);
78+
throw new ItemError('Got more @return tags than expected', item);
7879
}
7980
if (!p.name) {
8081
res.returns.push({ description: content.trim() });
8182
} else {
8283
const paramMatches = content.match(/(\w+)( ([^]*))?/);
8384
if (!paramMatches || paramMatches[1] !== p.name) {
84-
throw new Error(`Expected @return tag to start with name '${p.name}'`);
85+
throw new ItemError(`Expected @return tag to start with name '${p.name}'`, item);
8586
}
8687
const [, name, description] = paramMatches as [string, string, string?];
8788
res.returns.push({ name, description: description?.trim() ?? '' });
@@ -97,20 +98,20 @@ export function parseNatspec(item: DocItemWithContext): NatSpec {
9798

9899
if (tag === 'inheritdoc') {
99100
if (!(item.nodeType === 'FunctionDefinition' || item.nodeType === 'VariableDeclaration')) {
100-
throw new Error(`Expected function or variable but saw ${itemType(item)}`);
101+
throw new ItemError(`Expected function or variable but saw ${itemType(item)}`, item);
101102
}
102103
const parentContractName = content.trim();
103104
const parentContract = getContractsInScope(item)[parentContractName];
104105
if (!parentContract) {
105-
throw new Error(`Parent contract '${parentContractName}' not found`);
106+
throw new ItemError(`Parent contract '${parentContractName}' not found`, item);
106107
}
107108
inheritFrom = [...findAll('FunctionDefinition', parentContract)].find(f => item.baseFunctions?.includes(f.id));
108109
}
109110
}
110111

111112
if (docString.length === 0) {
112113
if ('baseFunctions' in item && item.baseFunctions?.length === 1) {
113-
const baseFn = item.__item_context.build.deref('FunctionDefinition', item.baseFunctions[0]!);
114+
const baseFn = item[DOC_ITEM_CONTEXT].build.deref('FunctionDefinition', item.baseFunctions[0]!);
114115
const shouldInherit = item.nodeType === 'VariableDeclaration' || arraysEqual(item.parameters.parameters, baseFn.parameters.parameters, p => p.name);
115116
if (shouldInherit) {
116117
inheritFrom = baseFn;

0 commit comments

Comments
 (0)