This is an archive of the discontinued LLVM Phabricator instance.

[analyzer][NFC] Refactor CallEvent::isCalled()
ClosedPublic

Authored by steakhal on Oct 11 2021, 3:28 AM.

Details

Summary

Refactor the code to make it more readable.

It will set up further changes, and improvements to this code in subsequent patches.
This is a non-functional change.

Diff Detail

Event Timeline

steakhal created this revision.Oct 11 2021, 3:28 AM
steakhal requested review of this revision.Oct 11 2021, 3:28 AM
steakhal added inline comments.Oct 12 2021, 1:07 AM
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
370–373

I'd like to enable this, but we need to reach consensus about this.

What is the test coverage of the newly added lines?

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1287–1289

qualifier is an overloaded term. One might think about type qualifiers like const and volatile. I'd rather call this something like begin_qualified_name_parts or begin_qualified_name.

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
325–329

can we make it pure?

345–363
383

Should we check names before arg/param count? Just to preserve the non-functional change attribute of this patch.

386–387

Why? I don't see this logic in the original code. Even if it was there, it is not obvious why we do this and what justifies it.

What is the test coverage of the newly added lines?

I'm pretty sure they all are covered.
Let me check this, after we decided if we want to enable L370.

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1287–1289

I agree! I'll update accordingly.

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
325–329

We could. My intention was to express the most important parameter it will depend on.
I'm okay with either approach.

However, if we pursue this goal, the ExactMatchArgAndParamCounts should be also changed to follow this pattern.
But that would result in less readable code IMO.
So, I would rather stick to the current style. WDYT?

370–373

Really, just check return-value-guaranteed.cpp for more insight.

383

The order in which we check the conditions should not matter. Regarless of the fact we return false, we return false.
My point was to match param counts first, since that is O(1), while the name matching could be more complex in the case of non-identifiers which involves stringification and string comparisons.
So, I would keep the order as-is.
I'll add a comment about this to make this decision clear.

386–387

tldr; this if condition only makes sense if L370 is enabled.


CD.QualifiedName.size() == 1 means that we don't have any qualifier parts, but only a single function name which was previously matched at L381.

Consider this example: CD: { "getenv" } and Call: std::getenv()
The name part and param count would match, thus we need to match the qualifier parts.

Now comes L370 into play. It will reject any match, where by the end of the match we still have unmatched qualifier parts of Call.
For instance, CD: { "my_namespace", "std", "invoke" } and Call: test_good::my_namespace::std::invoke.
Previously, we would have accept the Call by the CallDescription CD. Now, I'm proposing to tighten the behavior, by rejecting these, and only accept if by the end of the matching the Call does not have any qualifier parts left.

Now imagine that the if (CD.QualifiedName.size() == 1) at L384 is missing.
We would reject the match of qualifier parts using the example Call and CD, when we don't even specified any qualifier parts - which seems odd. We should have match for any qualifier sequence if none is specified in the CallDescription.

This property was implicitly encoded by the invariant of the loop. The loop entered only once, but it immediately broke out due to L340, since NumUnmatched is 0. After the loop, at L355 the branch is not taken, thus it will fallthrough and accept the qualifier segments.

steakhal added inline comments.Oct 13 2021, 4:56 AM
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
370–373

Actually, it might be cleaner to enable this in a follow-up patch.
It will need a lot of fixup in the return-value-guaranteed.cpp file.

The way I see now, we should break this into 2 patches. 1) the strictly NFC changes 2) the return-value-guaranteed.cpp related FIXME and the reordering of the matching logic.

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
325–329

For me, a function with a name match suggests that there should at least two things we'd like to match or compare, that is why I consider having the compared things passed as parameters more readable.

And ExactMatchArgAndParamCounts takes the CallDescription as a parameter while other matcher lambdas don't, this is not consistent. IMHO, CD should be a parameter for all matcher lambda because that is the thingy that we want to compare/match with (and the other parameter should be the other thing we compare to).

370–373

Umm, could you please elaborate? There are 3 structs in that test file. What is the exact problem? Or rather just do that in a follow-up patch and just remove this FIXME together with the uncommented block?

383

Ok. But then it's no longer an NFC patch, because we do change the original behavior, this could perhaps result that a CallEvent is not processed by a checker, isn't it? I still don't see why the changed order in which we check the conditions should not matter.

Since you already started to work out a follow-up patch for the return-value-guaranteed.cpp cases, perhaps it'd make sense to do this reordering in a follow-up patch? That way, we could more confidently state that this patch is indeed NFC.

386–387

Okay, makes sense, could you please add a comment then to this if block that it means that we don't have any qualifier parts, but only a single function name which was previously matched ?

xazax.hun added inline comments.Oct 13 2021, 7:31 AM
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1240

If we are about to refactor this, maybe IsLookupDone and II can be merged into a single optional.

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
331–334

You probably mean C++ overloaded operator. We can get a name for most overloaded function from getDeclName.

370–373

What do you mean by this comment? It is probably perfectly valid to have unmatched NamespaceDecl when the namespace is inline. Do I miss something?

380

I wonder if it would be nicer to have the definitions of these closer to their use.

steakhal updated this revision to Diff 379670.Oct 14 2021, 5:05 AM
steakhal marked 11 inline comments as done.
steakhal added reviewers: balazske, ASDenysPetrov.

Addressed review comments.

steakhal added inline comments.Oct 14 2021, 5:15 AM
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1240

I'm gonna fix this, thanks.

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
331–334

Actually, I'm doing exactly that in the child revision.
This patch is an NFC change, preparing that one you pointed at.

370–373

Oh well, I completely forgot about inline namespaces.
You are right, if we have only inline namespaces beyond this point, we should still match.
I'm gonna address this in a follow-up patch, enforcing this property while ignoring inline and anonymous namespaces.

380

TBH, they should be private member functions of CallDescription IMO.

383

What I wanted to highlight is that the order of the properties in which we check them does not matter.
The logical and operator is commutative: PropertyA && PropertyB && PropertyC is the same as PropertyB && PropertyA && PropertyC.
In our case, all properties should match to accept the Call. That being said, I'm pretty sure it's still NFC.

386–387

You are right that we should not directly access QualifiedName, and we should use a member function call here.
Check the updated version of this patch.

martong accepted this revision.Oct 14 2021, 6:22 AM

LGTM (with nits)! Thanks!

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1240

Lovely.

1275

Good!

1293

Maybe a comment would be useful here with an example. Something like e.g. 'std::sort' has a qualified part ('std') but the global 'memset' does not have

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
325–329

This is the only lambda where we still have [&CD], with all other lambda's you pass CD as a param.

380

That's a good idea!

383

Okay, makes sense!

This revision is now accepted and ready to land.Oct 14 2021, 6:22 AM
xazax.hun accepted this revision.Oct 14 2021, 10:14 AM
steakhal updated this revision to Diff 379778.Oct 14 2021, 11:04 AM
steakhal marked 8 inline comments as done.
  • purified lambda
  • added doc comments for hasQualifiedNameParts()
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1293

Yeah, sure.

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
325–329

Oh, I must have missed that, sorry.

380

I'm not planning to do that.

steakhal added inline comments.Oct 15 2021, 12:59 AM
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
349

TBH I don't understand why doesn't isa<NamespaceDecl, RecordDecl>() work. I could have used this variadic form so many times.
WDYT, should I propose turning isa and isa_and_nonnull˙into variadic functions?

martong accepted this revision.Oct 15 2021, 7:58 AM
martong added inline comments.
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
349

TBH I don't understand why doesn't isa<NamespaceDecl, RecordDecl>() work. I could have used this variadic form so many times.
WDYT, should I propose turning isa and isa_and_nonnull˙into variadic functions?

Yes, that's a good idea!

This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptOct 18 2021, 5:58 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Thank you for adding me. I'll make a deeper review later.

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1289

What rules did you use to name this functions? It seems that it goes against LLVM naming rules.

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
336–352

Can we move these lambdas in separate functions? IMO it could make the code even more readable.

steakhal marked 3 inline comments as done.Oct 18 2021, 9:25 AM
steakhal added inline comments.
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1289

Not exactly. There is an exception for begin() and end().
That being said, begin should have been a suffix instead of being a prefix.
But I still like this more :D

clang/lib/StaticAnalyzer/Core/CallEvent.cpp
336–352

We could, but I'm not planning to move them.
That would be more appropriate to move all CallDescription implementation to its own translation unit.

349

Actually, it's already variadic :D
I created a follow-up patch for addressing similar issues in D111982.

ASDenysPetrov added inline comments.Oct 19 2021, 9:11 AM
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
1289

Oh, now I see the exception. I also prefer begin as a prefix because it makes better search through the popup hints of IDEs.