Skip to content

Commit cf8a7c3

Browse files
authored
Merge pull request #12544 from ethereum/natspec-ice-12528
Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
2 parents 79e9d61 + 7c0a121 commit cf8a7c3

File tree

4 files changed

+73
-20
lines changed

4 files changed

+73
-20
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Bugfixes:
1313
* Antlr Grammar: Allow builtin names in ``yulPath`` to support ``.address`` in function pointers.
1414
* Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis.
1515
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the parent contract contains immutable variables.
16+
* Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
1617
* TypeChecker: Fix ICE when a constant variable declaration forward references a struct.
1718

1819

libsolidity/analysis/DocStringAnalyser.cpp

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <libsolidity/analysis/DocStringAnalyser.h>
2626

2727
#include <libsolidity/ast/AST.h>
28+
#include <libsolidity/ast/TypeProvider.h>
2829
#include <liblangutil/ErrorReporter.h>
2930

3031
#include <boost/algorithm/string.hpp>
@@ -37,20 +38,14 @@ using namespace solidity::frontend;
3738
namespace
3839
{
3940

40-
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, CallableDeclaration const* _declaration = nullptr)
41+
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, FunctionType const* _functionType = nullptr)
4142
{
4243
// Only copy if there is exactly one direct base function.
4344
if (_baseFunctions.size() != 1)
4445
return;
4546

4647
CallableDeclaration const& baseFunction = **_baseFunctions.begin();
4748

48-
auto hasReturnParameter = [](CallableDeclaration const& declaration, size_t _n)
49-
{
50-
return declaration.returnParameterList() &&
51-
declaration.returnParameters().size() > _n;
52-
};
53-
5449
auto& sourceDoc = dynamic_cast<StructurallyDocumentedAnnotation const&>(baseFunction.annotation());
5550

5651
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
@@ -70,21 +65,22 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
7065
DocTag content = it->second;
7166

7267
// Update the parameter name for @return tags
73-
if (_declaration && tag == "return")
68+
if (_functionType && tag == "return")
7469
{
7570
size_t docParaNameEndPos = content.content.find_first_of(" \t");
7671
string const docParameterName = content.content.substr(0, docParaNameEndPos);
7772

7873
if (
79-
hasReturnParameter(*_declaration, n) &&
80-
docParameterName != _declaration->returnParameters().at(n)->name()
74+
_functionType->returnParameterNames().size() > n &&
75+
docParameterName != _functionType->returnParameterNames().at(n)
8176
)
8277
{
8378
bool baseHasNoName =
84-
hasReturnParameter(baseFunction, n) &&
79+
baseFunction.returnParameterList() &&
80+
baseFunction.returnParameters().size() > n &&
8581
baseFunction.returnParameters().at(n)->name().empty();
8682

87-
string paramName = _declaration->returnParameters().at(n)->name();
83+
string paramName = _functionType->returnParameterNames().at(n);
8884
content.content =
8985
(paramName.empty() ? "" : std::move(paramName) + " ") + (
9086
string::npos == docParaNameEndPos || baseHasNoName ?
@@ -127,7 +123,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
127123
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
128124
{
129125
if (!_function.isConstructor())
130-
handleCallable(_function, _function, _function.annotation());
126+
handleCallable(_function, _function, _function.annotation(), TypeProvider::function(_function));
131127
return true;
132128
}
133129

@@ -136,10 +132,12 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
136132
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
137133
return false;
138134

135+
auto const* getterType = TypeProvider::function(_variable);
136+
139137
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
140-
copyMissingTags({baseFunction}, _variable.annotation());
138+
copyMissingTags({baseFunction}, _variable.annotation(), getterType);
141139
else if (_variable.annotation().docTags.empty())
142-
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation());
140+
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation(), getterType);
143141

144142
return false;
145143
}
@@ -168,17 +166,18 @@ bool DocStringAnalyser::visit(ErrorDefinition const& _error)
168166
void DocStringAnalyser::handleCallable(
169167
CallableDeclaration const& _callable,
170168
StructurallyDocumented const& _node,
171-
StructurallyDocumentedAnnotation& _annotation
169+
StructurallyDocumentedAnnotation& _annotation,
170+
FunctionType const* _functionType
172171
)
173172
{
174173
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
175-
copyMissingTags({baseFunction}, _annotation, &_callable);
174+
copyMissingTags({baseFunction}, _annotation, _functionType);
176175
else if (
177176
_annotation.docTags.empty() &&
178177
_callable.annotation().baseFunctions.size() == 1 &&
179178
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
180179
)
181-
copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable);
180+
copyMissingTags(_callable.annotation().baseFunctions, _annotation, _functionType);
182181
}
183182

184183
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(

libsolidity/analysis/DocStringAnalyser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ class DocStringAnalyser: private ASTConstVisitor
5454
void handleCallable(
5555
CallableDeclaration const& _callable,
5656
StructurallyDocumented const& _node,
57-
StructurallyDocumentedAnnotation& _annotation
57+
StructurallyDocumentedAnnotation& _annotation,
58+
FunctionType const* _functionType = nullptr
5859
);
5960

6061
langutil::ErrorReporter& m_errorReporter;

test/libsolidity/SolidityNatspecJSON.cpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2482,7 +2482,7 @@ BOOST_AUTO_TEST_CASE(custom_inheritance)
24822482
checkNatspec(sourceCode, "B", natspecB, false);
24832483
}
24842484

2485-
BOOST_AUTO_TEST_CASE(dev_different_amount_return_parameters)
2485+
BOOST_AUTO_TEST_CASE(dev_struct_getter_override)
24862486
{
24872487
char const *sourceCode = R"(
24882488
interface IThing {
@@ -2534,6 +2534,58 @@ BOOST_AUTO_TEST_CASE(dev_different_amount_return_parameters)
25342534
checkNatspec(sourceCode, "Thing", natspec2, false);
25352535
}
25362536

2537+
BOOST_AUTO_TEST_CASE(dev_struct_getter_override_different_return_parameter_names)
2538+
{
2539+
char const *sourceCode = R"(
2540+
interface IThing {
2541+
/// @return x a number
2542+
/// @return y another number
2543+
function value() external view returns (uint128 x, uint128 y);
2544+
}
2545+
2546+
contract Thing is IThing {
2547+
struct Value {
2548+
uint128 a;
2549+
uint128 b;
2550+
}
2551+
2552+
Value public override value;
2553+
}
2554+
)";
2555+
2556+
char const *natspec = R"ABCDEF({
2557+
"methods":
2558+
{
2559+
"value()":
2560+
{
2561+
"returns":
2562+
{
2563+
"x": "a number",
2564+
"y": "another number"
2565+
}
2566+
}
2567+
}
2568+
})ABCDEF";
2569+
2570+
char const *natspec2 = R"ABCDEF({
2571+
"methods": {},
2572+
"stateVariables":
2573+
{
2574+
"value":
2575+
{
2576+
"returns":
2577+
{
2578+
"a": "a number",
2579+
"b": "another number"
2580+
}
2581+
}
2582+
}
2583+
})ABCDEF";
2584+
2585+
checkNatspec(sourceCode, "IThing", natspec, false);
2586+
checkNatspec(sourceCode, "Thing", natspec2, false);
2587+
}
2588+
25372589
}
25382590

25392591
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)