Fix and improve array
and mdspan
static analysis warnings
#4856
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
🗺️ Overview
Fixes DevCom-10342063 VSO-1804139 "False positive C28020 iterating over single element
std::array
". Thanks to Hwi-sung Im from the static analysis team for explaining how to fix this with_Ret_range_(==, _Size)
.The issue is that we had SAL annotations on
array::operator[]
explaining its precondition, but static analysis didn't know thatarray<T, N>::size()
always returnsN
. (By design, it doesn't do interprocedural analysis.) Adding a return annotation allows/analyze
to understand loops that are guarded byarr.size()
.Updating
array
then revealed that<mdspan>
(which usesarray
extensively) should be updated accordingly.📜 Commits
array::size()
with_Ret_range_(==, _Size)
, which is what fixes the reported bug._In_range_(0, _Size - 1)
with_In_range_(<, _Size)
. I wasn't previously aware of this form, but it appears elsewhere in the MSVC codebase, and is much more natural as it aligns with our_STL_VERIFY
checks.array
and<mdspan>
below, we're always working withsize_t
, so we don't need to worry about negative numbers.<mdspan>
, update a comment from citing DevCom-923103 to saying "guaranteed by how_Rank_dynamic
is calculated". We have an ironclad guarantee here, but the proof is way more subtle than static analysis can reasonably be expected to understand, so this isn't a compiler bug workaround.rank()
with_Ret_range_
andstatic_extent()
,extent()
, andstride()
with_In_range_
.layout_stride::mapping::stride()
.mdspan::extent()
isn't missing a check, it's just performed bythis->_Map.extents().extent(_Idx)
and the message will be sufficiently relevant._In_range_(<, _Rank)
.FAIL
, but one needs to be added (because we have more precondition annotations).test_mdspan_support.hpp
, addassert(i < rank)
so static analysis is happy with the precondition. (Attempting to propagate the annotation to the lambda led to more warnings, since it's used withinvoke()
andiota_view
.)Ext::rank()
is now understood.P0009R18_mdspan_layout_stride
to useExt::rank()
instead of the equivalentstrs.size()
so/analyze
can see the guarantee.P0218R1_filesystem
, theEXPECT
macro calls a function that returns the givenbool
, but/analyze
doesn't see that, so it doesn't realize we're checking the validity ofindex
. Restructure this with a direct test andEXPECT(false)
(which will still result in decent logging since the line number is captured)./analyze
warning when intentionally making out-of-bounds calls. I mention/analyze
instead of the internal "PREfast" terminology here.suppress
withpush
/disable
/pop
, as we've already completely done in the product code, sincesuppress
is annoyingly fragile and we should avoid it, despite the additional lines needed to push/disable/pop.