Background
NextSyncBlock() is used in BaseIndex::Sync() to get the next block index to be synced, so that BaseIndex::Sync() can build the index for that block. https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/index/base.cpp#L150-L151
It normally calls chain.Next(pindex_prev) to get the next block index on the active chain. If that fails, pindex_prev is on a fork. In that case, chain.FindFork(pindex_prev) is called to return the fork point, and then chain.Next(fork point) is returned for the caller to handle. The latter logic is referred to as the fork code below.
Issue:
The current master implementation of NextSyncBlock() handles a specific case implicitly in the fork code.
The special case is pindex_prev == chain tip (simplified as the special case below). This means the input block is already the tip of the active chain, and there is no next block to be indexed anymore. Thus, NextSyncBlock() should return nullptr. This expectation is also implied in BaseIndex::Sync(): https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/index/base.cpp#L220-L221
Although NextSyncBlock() does return nullptr in this case, it is handled in a confusing code flow: https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/index/base.cpp#L150-L166
calls line 158: chain.Next(pindex_prev) returns nullptr because the below code flow: https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/chain.h#L417-L423
calls line 419: chain.Contains(): https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/chain.h#L411-L414
calls line 405: https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/chain.h#L403-L408
Thus in NextSyncBlock line 159, because pindex is nullptr, it finally falls into the fork code.
it is handled only coincidentally by the fork code:
https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/index/base.cpp#L163-L165
in this code, FindFork() is called,
https://github.com/bitcoin/bitcoin/blob/1d3243806da6db8bd13e6b20906628ed434362b6/src/chain.cpp#L50-L59
and because pindex is the active chain tip, the while loop on line 56 is not iterated. It finally returns pindex itself.
So at last chain.Next(pindex_prev) is called again to return nullptr.
However, the fork code is designed to handle reorgs, not this special case. This seems to be an oversight rather than something by design.
How to test it
Because NextSyncBlock() returns nullptr in the special case anyway, some debug code like assert() or std::cout or log sematics has to be added into this method to see the code flow in this method. The caller can be this unit test:
Line 32 calls Sync() and it calls NextSyncBlock()
Solution:
Rather than modifying the comments on lines 163–164 to make them consistent with the fork code behavior, it would be better to handle this special case explicitly and early in NextSyncBlock().