[PATCH] D65526: [Clangd] Initial prototype version of ExtractFunction
Sam McCall via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 20 04:11:35 PDT 2019
sammccall added a comment.
Main themes are:
- there are a few places we can reduce scope for the first patch: e.g. template support
- internally, it'd be clearer to pass data around in structs and avoid complex classes unless the abstraction is important
- high-level documentation would aid in understanding: for each concept/function: what is the goal, how does it fit into higher-level goals, are there any non-obvious reasons it's done this way
- naming questions (this can solve some of the same problems as docs, but much more cheaply)
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:7
+//
+//===----------------------------------------------------------------------===//
+#include "ClangdUnit.h"
----------------
this file will need an overview describing
- the refactoring, and (briefly) major user-visible design decisions (e.g. params, return types, hoisting)
- structure/approach/major parts
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:37
+
+// Source is the part of code that is being extracted.
+// EnclosingFunction is the function/method inside which the source lies.
----------------
this name is *also* pretty confusing because of `SourceLocation`, `SourceRange` etc classes.
Best option might just be to make up something distinctive, like `Zone` :-/
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:39
+// EnclosingFunction is the function/method inside which the source lies.
+// PreSource is everything before source and part of EnclosingFunction.
+// PostSource is everything after source and part of EnclosingFunction.
----------------
nit: if you're going to document the values one-by-one, do it in a comment on each value instead
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:42
+// We split the file into 4 parts w.r.t. the Source.
+enum LocType { PRESOURCE, INSOURCE, POSTSOURCE, OUTSIDEFUNC };
+
----------------
nit: we don't use MACROCASE for enums in LLVM (I think). This of course makes namespacing more important, we should consider `enum class` here.
I'd probably suggest
```
enum class ZoneRelative { // or some name consistent with above
Before,
After,
Inside,
External
};
```
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:45
+// ExtractionSourceView forms a view of the code wrt to Source.
+class ExtractionSourceView {
+public:
----------------
This looks like basically a struct (mostly public data members) where all the logic is in the constructor.
This would be more obvious/more consistent with style elsewhere if it were literally a struct and the constructor was instead a function (which could return `Expected<ExtractionSourceView>` instead of setting flags on the result)
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:45
+// ExtractionSourceView forms a view of the code wrt to Source.
+class ExtractionSourceView {
+public:
----------------
sammccall wrote:
> This looks like basically a struct (mostly public data members) where all the logic is in the constructor.
> This would be more obvious/more consistent with style elsewhere if it were literally a struct and the constructor was instead a function (which could return `Expected<ExtractionSourceView>` instead of setting flags on the result)
nit: not sure `view` adds anything here. `ExtractionSource` or `ExtractionZone`?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:50
+ // Get the location type for a given location.
+ LocType getLocType(SourceLocation Loc) const;
+ // Parent of RootStatements being extracted.
----------------
classify?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:59
+ SourceRange EnclosingFunctionRng;
+ const Node *LastRootStmt = nullptr;
+ bool isEligibleForExtraction() const { return IsEligibleForExtraction; }
----------------
when is this not Parent->Children.back()?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:93
+ for (const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) {
+ if (CurNode->ASTNode.get<FunctionDecl>()) {
+ // FIXME: Support extraction from methods.
----------------
I think you probably want to avoid extracting from lambdas and templates for now, too.
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:104
+SourceRange
+ExtractionSourceView::computeEnclosingFunctionRng(const Node *EnclosingFunction,
+ const SourceManager &SM,
----------------
nit: Please spell 'range' rather than 'rng' in function names, as it's not a standard abbreviation in clang[d]
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:107
+ const LangOptions &LangOpts) {
+ const Node *N = EnclosingFunction->Parent->ASTNode.get<FunctionTemplateDecl>()
+ ? EnclosingFunction->Parent
----------------
can we avoid adding template support in the first patch? It's complicated, and I don't think the code below handles it robustly.
For instance, this case will be extracted (despite being dependent), because the reference to T is not seen by the ast visitor, and the type of the decls that are seen are not dependent:
```
template <typename T>
int zero() { return std::vector<T>().size(); }
```
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:113
+
+const Node *ExtractionSourceView::getParentOfRootStmts(const Node *CommonAnc) {
+ if (!CommonAnc)
----------------
It's not clear what this function does and why. Can you add a high-level comment?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:124
+ return nullptr;
+ [[clang::fallthrough]];
+ case SelectionTree::Selection::Complete:
----------------
LLVM_FALLTHROUGH
(we support other host compilers: msvc, gcc)
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:144
+ return;
+ // FIXME: check if EnclosingFunction has any attributes
+ EnclosingFunction = computeEnclosingFunction(Parent);
----------------
what needs to be fixed, and why is it important?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:175
+
+bool ExtractionSourceView::hasOnlyRootStmtChildren() {
+ for (const Node *Child : Parent->Children) {
----------------
what is this function doing, and why?
why is it separated from getParentOfRootStmts?
it seems like we're trying to determine that the selection consists of either a single completely-selected stmt, or a bunch of completely-selected statements with a common parent, and then we represent them as "all the children of a certain parent node".
Splitting the logic into two distant functions, and not documenting this intent, makes it hard to follow.
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:206
+// rendering it.
+class NewFunction {
+private:
----------------
as above, this seems like it would be clearer as a struct.
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:214
+ bool IsConst;
+ Parameter(std::string Name, std::string Type, bool IsConst)
+ : Name(Name), Type(Type), IsConst(IsConst) {}
----------------
I'd suggest capturing the type as a QualType instead of a string + const + ref flag
When types may need to be re-spelled, we'll need that extra information - the context needed to re-spell is available at render() time, not addParameter() time.
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:225
+ std::string ReturnType = "void";
+ std::set<Parameter> Parameters;
+ std::vector<const Decl *> DeclsToHoist;
----------------
We tend to avoid std::set except in the rare cases where we need both sorting and on-the-fly deduplication.
Here, it seems std::vector would be enough, and explicitly sorting an the end would make the intent clearer.
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:226
+ std::set<Parameter> Parameters;
+ std::vector<const Decl *> DeclsToHoist;
+ SourceRange BodyRange;
----------------
Hoisting isn't supported, drop this?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:235
+ SemicolonPolicy(SemicolonPolicy){};
+ std::string render(bool IsDefinition) const;
+ std::string getFuncBody() const;
----------------
as previously discussed, there's not enough (conceptual or implementation) commonality between the call and the declaration, can we have separate renderDefinition() and renderCall() functions?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:262
+std::string NewFunction::getFuncBody() const {
+ // TODO: Hoist Decls
+ // TODO: Add return statement.
----------------
these TODOs don't really describe the structural change that's needed here: we need a way to apply a bunch of edits to the function text (in terms of the original coordinates).
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:400
+// to be hoisted. Returns true if able to find the parameters successfully.
+bool createParametersAndGetDeclsToHoist(
+ NewFunction &ExtractedFunc, const CapturedSourceInfo &CapturedInfo) {
----------------
I'm confused about the decls to host part - this isn't actually supported right? Take it out of the name?
Or are they detected in order to cause the refactoring to fail?
================
Comment at: clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp:454
+
+// Get semicolon extraction policy (may change SrcRange in SrcView)
+tooling::ExtractionSemicolonPolicy
----------------
you've got a return type, a comment, and a function name, and they all say the same thing. Can you explain (at a high level) what this is for?
================
Comment at: clang-tools-extra/clangd/unittests/TweakTests.cpp:603
+TWEAK_TEST(ExtractFunction);
+TEST_F(ExtractFunctionTest, PrepareFunctionTest) {
+ Header = R"cpp(
----------------
the test helpers are really aimed at testing prepare/apply together rather than separately.
These EXPECT_AVAILABLE could be EXPECT_EQ(apply(), ...). If prepare() returns false it'll return "prepare failed" or some string like that.
================
Comment at: clang-tools-extra/clangd/unittests/TweakTests.cpp:618
+ // TODO: Add tests for macros after selectionTree works properly for macros.
+ // EXPECT_AVAILABLE(R"cpp( F (int x = 0; [[x = 1;]])cpp");
+ // FIXME: This should be unavailable since partially selected but
----------------
rather than checking in commented-out tests, I'd prefer to check in the tests verifying current incorrect behavior with a FIXME.
Commented out code tends to rot (or often not quite work in the first place), and it's not discoverable
(or just leave the FIXME by itself)
================
Comment at: clang-tools-extra/clangd/unittests/TweakTests.cpp:639
+ void f() {
+ [[int x;]]
+ }
----------------
maybe add a comment saying why not?
================
Comment at: clang-tools-extra/clangd/unittests/TweakTests.cpp:649
+ // Checks const qualifier and extraction parameters.
+ Header = R"cpp(
+ )cpp";
----------------
this line does nothing?
================
Comment at: clang-tools-extra/clangd/unittests/TweakTests.cpp:651
+ )cpp";
+ EXPECT_EQ(apply(
+ R"cpp(
----------------
IIRC you can't use raw strings inside a macro (it's legal, but one of the supported compilers chokes on it)
================
Comment at: clang-tools-extra/clangd/unittests/TweakTests.cpp:654
+struct FOO {
+ int x;
+};
----------------
please avoid large tests that test lots of things, test them one-at-a-time instead. The intent is clearer, and it's clearer what failures mean.
================
Comment at: clang-tools-extra/clangd/unittests/TweakTests.cpp:683
+})cpp");
+ auto ShouldSucceed = [&](llvm::StringRef Code) {
+ EXPECT_THAT(apply(Code), HasSubstr("extracted"));
----------------
please write these out explicitly instead.
Among other things, the goal in the helpers was that assertion failures should preserve line numbers of examples
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D65526/new/
https://reviews.llvm.org/D65526
More information about the cfe-commits
mailing list