mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Syntactic call graph
This commit is contained in:
parent
bdd2f02c83
commit
9656cf105b
@ -188,6 +188,8 @@ set(sources
|
||||
experimental/analysis/Analysis.h
|
||||
experimental/analysis/DebugWarner.cpp
|
||||
experimental/analysis/DebugWarner.h
|
||||
experimental/analysis/FunctionCallGraph.cpp
|
||||
experimental/analysis/FunctionCallGraph.h
|
||||
experimental/analysis/TypeClassRegistration.cpp
|
||||
experimental/analysis/TypeClassRegistration.h
|
||||
experimental/analysis/TypeInference.cpp
|
||||
@ -196,6 +198,8 @@ set(sources
|
||||
experimental/analysis/TypeRegistration.h
|
||||
experimental/analysis/SyntaxRestrictor.cpp
|
||||
experimental/analysis/SyntaxRestrictor.h
|
||||
experimental/ast/CallGraph.cpp
|
||||
experimental/ast/CallGraph.h
|
||||
experimental/ast/Type.cpp
|
||||
experimental/ast/Type.h
|
||||
experimental/ast/TypeSystem.cpp
|
||||
|
@ -17,6 +17,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#include <libsolidity/experimental/analysis/Analysis.h>
|
||||
#include <libsolidity/experimental/analysis/DebugWarner.h>
|
||||
#include <libsolidity/experimental/analysis/FunctionCallGraph.h>
|
||||
#include <libsolidity/experimental/analysis/SyntaxRestrictor.h>
|
||||
#include <libsolidity/experimental/analysis/TypeClassRegistration.h>
|
||||
#include <libsolidity/experimental/analysis/TypeInference.h>
|
||||
@ -35,9 +36,11 @@ struct Analysis::AnnotationContainer
|
||||
|
||||
struct Analysis::GlobalAnnotationContainer
|
||||
{
|
||||
FunctionCallGraph::GlobalAnnotation functionCallGraphAnnotation;
|
||||
TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation;
|
||||
TypeRegistration::GlobalAnnotation typeRegistrationAnnotation;
|
||||
TypeInference::GlobalAnnotation typeInferenceAnnotation;
|
||||
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -52,7 +55,6 @@ TypeClassRegistration::GlobalAnnotation const& solidity::frontend::experimental:
|
||||
return analysis.annotationContainer().typeClassRegistrationAnnotation;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
TypeClassRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeClassRegistration>::get()
|
||||
{
|
||||
@ -65,6 +67,18 @@ TypeClassRegistration::Annotation const& solidity::frontend::experimental::detai
|
||||
return analysis.annotationContainer(_node).typeClassRegistrationAnnotation;
|
||||
}
|
||||
|
||||
template<>
|
||||
FunctionCallGraph::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<FunctionCallGraph>::get() const
|
||||
{
|
||||
return analysis.annotationContainer().functionCallGraphAnnotation;
|
||||
}
|
||||
|
||||
template<>
|
||||
FunctionCallGraph::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<FunctionCallGraph>::get()
|
||||
{
|
||||
return analysis.annotationContainer().functionCallGraphAnnotation;
|
||||
}
|
||||
|
||||
template<>
|
||||
TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get(ASTNode const& _node)
|
||||
{
|
||||
@ -77,7 +91,6 @@ TypeRegistration::GlobalAnnotation const& solidity::frontend::experimental::deta
|
||||
return analysis.annotationContainer().typeRegistrationAnnotation;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
TypeRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get()
|
||||
{
|
||||
@ -108,7 +121,6 @@ TypeInference::GlobalAnnotation const& solidity::frontend::experimental::detail:
|
||||
return analysis.annotationContainer().typeInferenceAnnotation;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
TypeInference::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeInference>::get()
|
||||
{
|
||||
@ -151,6 +163,7 @@ bool Analysis::check(std::vector<std::shared_ptr<SourceUnit const>> const& _sour
|
||||
{
|
||||
using AnalysisSteps = std::tuple<
|
||||
SyntaxRestrictor,
|
||||
FunctionCallGraph,
|
||||
TypeClassRegistration,
|
||||
TypeRegistration,
|
||||
TypeInference,
|
||||
|
87
libsolidity/experimental/analysis/FunctionCallGraph.cpp
Normal file
87
libsolidity/experimental/analysis/FunctionCallGraph.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libsolidity/experimental/analysis/Analysis.h>
|
||||
#include <libsolidity/experimental/analysis/FunctionCallGraph.h>
|
||||
|
||||
using namespace solidity::frontend::experimental;
|
||||
using namespace solidity::util;
|
||||
|
||||
FunctionCallGraph::FunctionCallGraph(solidity::frontend::experimental::Analysis& _analysis):
|
||||
m_analysis(_analysis),
|
||||
m_errorReporter(_analysis.errorReporter()),
|
||||
m_currentNode(nullptr),
|
||||
m_inFunctionDefinition(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool FunctionCallGraph::analyze(SourceUnit const& _sourceUnit)
|
||||
{
|
||||
_sourceUnit.accept(*this);
|
||||
// TODO remove debug output before merge
|
||||
//std::cout << annotation().functionCallGraph << std::endl;
|
||||
return !m_errorReporter.hasErrors();
|
||||
}
|
||||
|
||||
bool FunctionCallGraph::visit(FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
m_inFunctionDefinition = true;
|
||||
m_currentNode = &_functionDefinition;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FunctionCallGraph::endVisit(FunctionDefinition const&)
|
||||
{
|
||||
// If we're done visiting a function declaration without said function referencing/calling
|
||||
// another function in its body - insert it into the graph without child nodes.
|
||||
if (!annotation().functionCallGraph.edges.count(m_currentNode))
|
||||
{
|
||||
annotation().functionCallGraph.edges.insert({m_currentNode, {}});
|
||||
annotation().functionCallGraph.reverseEdges[nullptr].insert(m_currentNode);
|
||||
}
|
||||
m_inFunctionDefinition = false;
|
||||
}
|
||||
|
||||
bool FunctionCallGraph::visit(Identifier const& _identifier)
|
||||
{
|
||||
auto callee = dynamic_cast<FunctionDefinition const*>(_identifier.annotation().referencedDeclaration);
|
||||
// Check that the identifier is within a function body and is a function, and add it to the graph
|
||||
// as an ``m_currentNode`` -> ``callee`` edge.
|
||||
if (m_inFunctionDefinition && _identifier.annotation().referencedDeclaration && callee)
|
||||
{
|
||||
solAssert(m_currentNode, "Child node must have a parent");
|
||||
add(m_currentNode, callee);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FunctionCallGraph::add(FunctionDefinition const* _caller, FunctionDefinition const* _callee)
|
||||
{
|
||||
// Add caller and callee as and edge, as well as the reverse edge, i.e. callee -> caller.
|
||||
// If the caller is already in the reverse edges as a childless node, remove it, since it now
|
||||
// has a child.
|
||||
annotation().functionCallGraph.edges[_caller].insert(_callee);
|
||||
annotation().functionCallGraph.reverseEdges[_callee].insert(_caller);
|
||||
if (annotation().functionCallGraph.reverseEdges[nullptr].count(_caller) > 0)
|
||||
annotation().functionCallGraph.reverseEdges[nullptr].erase(_caller);
|
||||
}
|
||||
|
||||
FunctionCallGraph::GlobalAnnotation& FunctionCallGraph::annotation()
|
||||
{
|
||||
return m_analysis.annotation<FunctionCallGraph>();
|
||||
}
|
58
libsolidity/experimental/analysis/FunctionCallGraph.h
Normal file
58
libsolidity/experimental/analysis/FunctionCallGraph.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/experimental/ast/CallGraph.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
class Analysis;
|
||||
|
||||
class FunctionCallGraph: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
FunctionCallGraph(Analysis& _analysis);
|
||||
bool analyze(SourceUnit const& _sourceUnit);
|
||||
|
||||
struct Annotation {};
|
||||
struct GlobalAnnotation
|
||||
{
|
||||
CallGraph functionCallGraph;
|
||||
};
|
||||
|
||||
private:
|
||||
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||
void endVisit(FunctionDefinition const&) override;
|
||||
bool visit(Identifier const& _identifier) override;
|
||||
void add(FunctionDefinition const* _caller, FunctionDefinition const* _callee);
|
||||
GlobalAnnotation& annotation();
|
||||
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
FunctionDefinition const* m_currentNode;
|
||||
bool m_inFunctionDefinition;
|
||||
};
|
||||
|
||||
}
|
54
libsolidity/experimental/ast/CallGraph.cpp
Normal file
54
libsolidity/experimental/ast/CallGraph.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libsolidity/experimental/ast/CallGraph.h>
|
||||
|
||||
using namespace solidity::frontend::experimental;
|
||||
|
||||
// TODO remove before merge (delete entire file)
|
||||
std::ostream& solidity::frontend::experimental::operator<<(std::ostream& _out, CallGraph const& _callGraph)
|
||||
{
|
||||
_out << "EDGES" << "\n";
|
||||
_out << "===================" << std::endl;
|
||||
for (auto [top, subs]: _callGraph.edges)
|
||||
{
|
||||
std::string topName = top->name().empty() ? "fallback" : top->name();
|
||||
_out << "(" << topName <<") --> {";
|
||||
for (auto sub: subs)
|
||||
{
|
||||
_out << sub->name() << ",";
|
||||
}
|
||||
_out << "}" << std::endl;
|
||||
}
|
||||
|
||||
_out << "\nREVERSE EDGES" << "\n";
|
||||
_out << "===================" << std::endl;
|
||||
for (auto [top, subs]: _callGraph.reverseEdges)
|
||||
{
|
||||
std::string topName = top ? top->name() : "nullptr";
|
||||
_out << "(" << topName <<") --> {";
|
||||
for (auto sub: subs)
|
||||
{
|
||||
std::string subName = sub->name().empty() ? "fallback" : sub->name();
|
||||
_out << subName << ",";
|
||||
}
|
||||
_out << "}" << std::endl;
|
||||
}
|
||||
|
||||
return _out;
|
||||
}
|
43
libsolidity/experimental/ast/CallGraph.h
Normal file
43
libsolidity/experimental/ast/CallGraph.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
/// Data structure representing a function call graph.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <ostream>
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
struct CallGraph
|
||||
{
|
||||
/// Graph edges. Edges are directed and lead from the caller to the callee.
|
||||
/// The map contains a key for every possible caller, even if does not actually perform
|
||||
/// any calls.
|
||||
std::map<FunctionDefinition const*, std::set<FunctionDefinition const*>> edges;
|
||||
std::map<FunctionDefinition const*, std::set<FunctionDefinition const*>> reverseEdges;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& _out, CallGraph const& _callGraph);
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
// fallback---------->f----------+
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// v v
|
||||
// g h
|
||||
//
|
||||
//
|
||||
// add
|
||||
//
|
||||
// unreferenced
|
||||
|
||||
pragma experimental solidity;
|
||||
|
||||
type uint256 = __builtin("word");
|
||||
|
||||
instantiation uint256: + {
|
||||
function add(x, y) -> uint256 {
|
||||
let a = uint256.rep(x);
|
||||
let b = uint256.rep(y);
|
||||
assembly {
|
||||
a := add(a,b)
|
||||
}
|
||||
return uint256.abs(a);
|
||||
}
|
||||
}
|
||||
|
||||
function unreferenced(x:uint256) -> uint256
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
function f(x:uint256) -> uint256
|
||||
{
|
||||
return g(h(x));
|
||||
}
|
||||
|
||||
function g(x:uint256) -> uint256
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
function h(x:uint256) -> uint256
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
contract C {
|
||||
fallback() external {
|
||||
let a: uint256->uint256 = f;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=constantinople
|
||||
// ----
|
||||
// Warning 2264: (278-307): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// Info 4164: (309-342): Inferred type: word:(type, +)
|
||||
// Info 4164: (344-564): Inferred type: void
|
||||
// Info 4164: (375-562): Inferred type: (word, word) -> word
|
||||
// Info 4164: (387-393): Inferred type: (word, word)
|
||||
// Info 4164: (388-389): Inferred type: word
|
||||
// Info 4164: (391-392): Inferred type: word
|
||||
// Info 4164: (397-404): Inferred type: word
|
||||
// Info 4164: (419-420): Inferred type: word
|
||||
// Info 4164: (423-437): Inferred type: word
|
||||
// Info 4164: (423-434): Inferred type: word -> word
|
||||
// Info 4164: (423-430): Inferred type: word
|
||||
// Info 4164: (435-436): Inferred type: word
|
||||
// Info 4164: (451-452): Inferred type: word
|
||||
// Info 4164: (455-469): Inferred type: word
|
||||
// Info 4164: (455-466): Inferred type: word -> word
|
||||
// Info 4164: (455-462): Inferred type: word
|
||||
// Info 4164: (467-468): Inferred type: word
|
||||
// Info 4164: (541-555): Inferred type: word
|
||||
// Info 4164: (541-552): Inferred type: word -> word
|
||||
// Info 4164: (541-548): Inferred type: word
|
||||
// Info 4164: (553-554): Inferred type: word
|
||||
// Info 4164: (566-627): Inferred type: word -> word
|
||||
// Info 4164: (587-598): Inferred type: word
|
||||
// Info 4164: (588-597): Inferred type: word
|
||||
// Info 4164: (590-597): Inferred type: word
|
||||
// Info 4164: (602-609): Inferred type: word
|
||||
// Info 4164: (623-624): Inferred type: word
|
||||
// Info 4164: (629-685): Inferred type: word -> word
|
||||
// Info 4164: (639-650): Inferred type: word
|
||||
// Info 4164: (640-649): Inferred type: word
|
||||
// Info 4164: (642-649): Inferred type: word
|
||||
// Info 4164: (654-661): Inferred type: word
|
||||
// Info 4164: (675-682): Inferred type: word
|
||||
// Info 4164: (675-676): Inferred type: word -> word
|
||||
// Info 4164: (677-681): Inferred type: word
|
||||
// Info 4164: (677-678): Inferred type: word -> word
|
||||
// Info 4164: (679-680): Inferred type: word
|
||||
// Info 4164: (687-737): Inferred type: word -> word
|
||||
// Info 4164: (697-708): Inferred type: word
|
||||
// Info 4164: (698-707): Inferred type: word
|
||||
// Info 4164: (700-707): Inferred type: word
|
||||
// Info 4164: (712-719): Inferred type: word
|
||||
// Info 4164: (733-734): Inferred type: word
|
||||
// Info 4164: (739-789): Inferred type: word -> word
|
||||
// Info 4164: (749-760): Inferred type: word
|
||||
// Info 4164: (750-759): Inferred type: word
|
||||
// Info 4164: (752-759): Inferred type: word
|
||||
// Info 4164: (764-771): Inferred type: word
|
||||
// Info 4164: (785-786): Inferred type: word
|
||||
// Info 4164: (808-872): Inferred type: () -> ()
|
||||
// Info 4164: (816-818): Inferred type: ()
|
||||
// Info 4164: (842-861): Inferred type: word -> word
|
||||
// Info 4164: (845-861): Inferred type: word -> word
|
||||
// Info 4164: (845-852): Inferred type: word
|
||||
// Info 4164: (854-861): Inferred type: word
|
||||
// Info 4164: (864-865): Inferred type: word -> word
|
@ -0,0 +1,92 @@
|
||||
// a<------b
|
||||
// | ^
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// +------>c------->d-------->e------->f
|
||||
// ^ |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | v
|
||||
// h<-------g
|
||||
|
||||
pragma experimental solidity;
|
||||
|
||||
function a()
|
||||
{
|
||||
c();
|
||||
}
|
||||
|
||||
function b()
|
||||
{
|
||||
a();
|
||||
}
|
||||
|
||||
function c()
|
||||
{
|
||||
b();
|
||||
d();
|
||||
}
|
||||
|
||||
function d()
|
||||
{
|
||||
e();
|
||||
}
|
||||
|
||||
function e()
|
||||
{
|
||||
f();
|
||||
}
|
||||
|
||||
function f()
|
||||
{
|
||||
g();
|
||||
}
|
||||
|
||||
function g()
|
||||
{
|
||||
h();
|
||||
}
|
||||
|
||||
function h()
|
||||
{
|
||||
e();
|
||||
}
|
||||
// ----
|
||||
// Warning 2264: (366-395): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// Info 4164: (397-422): Inferred type: () -> ()
|
||||
// Info 4164: (407-409): Inferred type: ()
|
||||
// Info 4164: (416-419): Inferred type: ()
|
||||
// Info 4164: (416-417): Inferred type: () -> ()
|
||||
// Info 4164: (424-449): Inferred type: () -> ()
|
||||
// Info 4164: (434-436): Inferred type: ()
|
||||
// Info 4164: (443-446): Inferred type: ()
|
||||
// Info 4164: (443-444): Inferred type: () -> ()
|
||||
// Info 4164: (451-485): Inferred type: () -> ()
|
||||
// Info 4164: (461-463): Inferred type: ()
|
||||
// Info 4164: (470-473): Inferred type: ()
|
||||
// Info 4164: (470-471): Inferred type: () -> ()
|
||||
// Info 4164: (479-482): Inferred type: ()
|
||||
// Info 4164: (479-480): Inferred type: () -> ()
|
||||
// Info 4164: (487-512): Inferred type: () -> ()
|
||||
// Info 4164: (497-499): Inferred type: ()
|
||||
// Info 4164: (506-509): Inferred type: ()
|
||||
// Info 4164: (506-507): Inferred type: () -> ()
|
||||
// Info 4164: (514-539): Inferred type: () -> ()
|
||||
// Info 4164: (524-526): Inferred type: ()
|
||||
// Info 4164: (533-536): Inferred type: ()
|
||||
// Info 4164: (533-534): Inferred type: () -> ()
|
||||
// Info 4164: (541-566): Inferred type: () -> ()
|
||||
// Info 4164: (551-553): Inferred type: ()
|
||||
// Info 4164: (560-563): Inferred type: ()
|
||||
// Info 4164: (560-561): Inferred type: () -> ()
|
||||
// Info 4164: (568-593): Inferred type: () -> ()
|
||||
// Info 4164: (578-580): Inferred type: ()
|
||||
// Info 4164: (587-590): Inferred type: ()
|
||||
// Info 4164: (587-588): Inferred type: () -> ()
|
||||
// Info 4164: (595-620): Inferred type: () -> ()
|
||||
// Info 4164: (605-607): Inferred type: ()
|
||||
// Info 4164: (614-617): Inferred type: ()
|
||||
// Info 4164: (614-615): Inferred type: () -> ()
|
Loading…
Reference in New Issue
Block a user