This is an archive of the discontinued LLVM Phabricator instance.

[ASTMatchers] Add matcher for functions that are effectively inline
Needs ReviewPublic

Authored by Trass3r on Oct 11 2022, 9:34 AM.

Details

Summary

The existing isInline matcher only catches the ones explicitly marked inline.

Diff Detail

Event Timeline

Trass3r created this revision.Oct 11 2022, 9:34 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 11 2022, 9:34 AM
Trass3r updated this revision to Diff 466882.Oct 11 2022, 12:12 PM

fix formatting

Trass3r published this revision for review.Oct 11 2022, 2:30 PM
Herald added a project: Restricted Project. · View Herald TranscriptOct 11 2022, 2:30 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
ziqingluo-90 added inline comments.
clang/include/clang/ASTMatchers/ASTMatchersMacros.h
96

I was wondering if this change is necessary. This definition is so general that it could affect a massive matchers. So any change to it should be very careful and unnecessary changes may be avoided.

Other than that, this patch looks good to me.

Trass3r added inline comments.Oct 11 2022, 4:30 PM
clang/include/clang/ASTMatchers/ASTMatchers.h
7791

Is there any value in adding variables?
I guess that would mainly target constexpr vars, for which there is isConstexpr.

clang/include/clang/ASTMatchers/ASTMatchersMacros.h
96

Just came across it. Since it's a macro-internal class I couldn't see any potential breakage. Nobody should inherit from that. But I can take it out.

Thanks for the new matcher, can you also add a release note for the addition? One question I have is: what's the need for adding this matcher? Do you plan to use it for some in-tree needs? We usually only add new matchers where there's an immediate need for them because of how expensive AST matchers are to compile (and each matcher adds a fair number of template instantiations to the final binary as well, so there's a bit of runtime cost too).

clang/include/clang/ASTMatchers/ASTMatchers.h
7787–7788

I think it'd be interesting to show some other cases here -- like when the inline keyword is specified on a declaration without a visible definition or when the inline keyword is used on a declaration with a non-inline definition. (And update the comment below accordingly with what is matched.)

7791

I don't see a need for covering variables yet, but I wouldn't be opposed if there was a use case.

clang/include/clang/ASTMatchers/ASTMatchersMacros.h
96

I'd remove it as it's unrelated to the changes in the patch, but it might be a reasonable NFC change (especially if there's some positive impact to the change in terms of codegen).

clang/lib/ASTMatchers/Dynamic/Registry.cpp
435

This should be kept in alphabetical order.

clang/unittests/ASTMatchers/ASTMatchersInternalTest.cpp
310–311

I think you should also test that g() does not match and the additional tests I suggested in the documentation.

what's the need for adding this matcher? Do you plan to use it for some in-tree needs? We usually only add new matchers where there's an immediate need for them because of how expensive AST matchers are to compile (and each matcher adds a fair number of template instantiations to the final binary as well, so there's a bit of runtime cost too).

Didn't realize it has a big cost. Looking inside the AST_MATCHER and REGISTER_MATCHER macros I can't see any unique instantiations, should be memoized?
I created it a while ago for use in a clang-tidy check. Oddly I can't find that code right now.
It might have been for finding inline ctors/dtors: https://github.com/llvm/llvm-project/issues/51577.
https://chromium.googlesource.com/chromium/src/tools/clang/+/refs/heads/main/plugins/FindBadConstructsConsumer.cpp#495

what's the need for adding this matcher? Do you plan to use it for some in-tree needs? We usually only add new matchers where there's an immediate need for them because of how expensive AST matchers are to compile (and each matcher adds a fair number of template instantiations to the final binary as well, so there's a bit of runtime cost too).

Didn't realize it has a big cost. Looking inside the AST_MATCHER and REGISTER_MATCHER macros I can't see any unique instantiations, should be memoized?

IIRC, the cost may depend on the compiler. I know we had to enable /bigobj when building with MSVC because each template instantiation here was being added to its own section in the object file and we'd wind up with tens of thousands of sections.

I created it a while ago for use in a clang-tidy check. Oddly I can't find that code right now.
It might have been for finding inline ctors/dtors: https://github.com/llvm/llvm-project/issues/51577.
https://chromium.googlesource.com/chromium/src/tools/clang/+/refs/heads/main/plugins/FindBadConstructsConsumer.cpp#495

I think it might make more sense to use a local matcher if you need it only for one clang-tidy check. If we find we need it in more checks or there's a wider need for it, we can hoist it up to ASTMatchers.h so it's exposed more generally at that time. WDYT?

Didn't realize it has a big cost. Looking inside the AST_MATCHER and REGISTER_MATCHER macros I can't see any unique instantiations, should be memoized?

IIRC, the cost may depend on the compiler. I know we had to enable /bigobj when building with MSVC because each template instantiation here was being added to its own section in the object file and we'd wind up with tens of thousands of sections.

Interesting, was that before/without -Zc:inline?

I think it might make more sense to use a local matcher if you need it only for one clang-tidy check. If we find we need it in more checks or there's a wider need for it, we can hoist it up to ASTMatchers.h so it's exposed more generally at that time. WDYT?

Yeah a quick search revealed 2 places where it might be useful:
https://github.com/llvm/llvm-project/blob/b6c6933e9548f17d567a20c83eede16b3fcb0c2b/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp#L100-L101
https://github.com/llvm/llvm-project/blob/22e4203df813a8051b40adb3e2872e30fdbe1bbe/clang-tools-extra/clang-move/Move.cpp#L243-L245

Of course as shown there you can always do additional checks after matching when writing a check in C++.
But for tools like clang-query the matcher might be useful. Maybe there are also similar code search tools out there based on matchers?

Didn't realize it has a big cost. Looking inside the AST_MATCHER and REGISTER_MATCHER macros I can't see any unique instantiations, should be memoized?

IIRC, the cost may depend on the compiler. I know we had to enable /bigobj when building with MSVC because each template instantiation here was being added to its own section in the object file and we'd wind up with tens of thousands of sections.

Interesting, was that before/without -Zc:inline?

Pretty sure that was after /Zc:inline (IIRC that existed in MSVC 2015 which was roughly when I recall we were hitting this).

I think it might make more sense to use a local matcher if you need it only for one clang-tidy check. If we find we need it in more checks or there's a wider need for it, we can hoist it up to ASTMatchers.h so it's exposed more generally at that time. WDYT?

Yeah a quick search revealed 2 places where it might be useful:
https://github.com/llvm/llvm-project/blob/b6c6933e9548f17d567a20c83eede16b3fcb0c2b/clang-tools-extra/clang-tidy/misc/DefinitionsInHeadersCheck.cpp#L100-L101
https://github.com/llvm/llvm-project/blob/22e4203df813a8051b40adb3e2872e30fdbe1bbe/clang-tools-extra/clang-move/Move.cpp#L243-L245

Of course as shown there you can always do additional checks after matching when writing a check in C++.
But for tools like clang-query the matcher might be useful. Maybe there are also similar code search tools out there based on matchers?

The trouble with clang-query is that it can be used as a reason to expose *any* matcher, because it's specifically about exploring how to match the AST. I do know there are folks who use search tools based on matchers that aren't clang-tidy, but we don't usually support those as part of the matcher interface without a compelling reason.

The two examples you found do look like plausible reasons to expose this matcher though. Would you mind proving that out by switching those over to use the new matcher (as a second patch building on top of this one)? If it looks like we can easily use the new matcher in those checks, then I think it's a good enough reason to add the matcher (and update those checks).

Trass3r added a comment.EditedOct 17 2022, 8:30 AM

Pretty sure that was after /Zc:inline (IIRC that existed in MSVC 2015 which was roughly when I recall we were hitting this).

I see, yeah /Zc:inline got added slightly earlier:
https://github.com/llvm/llvm-project/commit/f846557eb89a913c69b54653b9bece04b0473233
than /bigobj:
https://github.com/llvm/llvm-project/commit/969b1a50a04e579146f1a78c0c10547a8175577c
Though interestingly it seems to build just fine without the bigobj flag.

fwiw on Linux it adds the following to Registry.cpp.o:

0000000000000000 0000000000000042 W clang::ast_matchers::isEffectivelyInline()
0000000000000000 000000000000002d W clang::ast_matchers::internal::matcher_isEffectivelyInlineMatcher::~matcher_isEffectivelyInlineMatcher()
0000000000000000 000000000000000a W clang::ast_matchers::internal::matcher_isEffectivelyInlineMatcher::matches(clang::FunctionDecl const&, clang::ast_matchers::internal::ASTMatchFinder*, clang::ast_matchers::internal::BoundNodesTreeBuilder*) const
0000000000000000 0000000000000038 V vtable for clang::ast_matchers::internal::matcher_isEffectivelyInlineMatcher

The two examples you found do look like plausible reasons to expose this matcher though. Would you mind proving that out by switching those over to use the new matcher (as a second patch building on top of this one)? If it looks like we can easily use the new matcher in those checks, then I think it's a good enough reason to add the matcher (and update those checks).

Ok 👍

Trass3r marked 5 inline comments as done.Oct 18 2022, 9:43 AM
Trass3r added inline comments.
clang/include/clang/ASTMatchers/ASTMatchers.h
7787–7788

inline keyword is specified on a declaration without a visible definition

According to https://learn.microsoft.com/en-us/cpp/build/reference/zc-inline-remove-unreferenced-comdat that's not standard-compliant?
But anyways those sound more like use-cases of the existing isInline matcher to me.

This one is more focused on those implicit inline methods which you can't find easily with a simple text search. I've also considered an alternative isImplicitlyInline matcher which would exclude explicit inline usage but I'm not sure which design is better.
It seems I ended up with this inclusive design because I wanted to match the opposite: cxxMethodDecl(unless(isEffectivelyInline()).