Skip to content

Eliminate level-4 warnings - #1116

Closed
Kitsune44 wants to merge 4 commits into
ReactiveDrop:reactivedrop_betafrom
Kitsune44:cpp20-build_warnings
Closed

Kitsune44 wants to merge 4 commits into
ReactiveDrop:reactivedrop_betafrom
Kitsune44:cpp20-build_warnings

Conversation

@Kitsune44

@Kitsune44 Kitsune44 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Eliminate level-4 warnings

Reduces the warning count of src/reactivedrop_vs13.sln from 3,249 lines in Debug|Win32 (17 codes, 824 distinct file:line sites) and 3,161 in Release|Win32 to 5 in both configurations, all of them the C5205 uses documented at the end. The projects keep /W4, do not use /WX, and no warning was disabled, no pragma added and no conformance switch relaxed. Every edit is behaviour preserving.

Warnings were fixed at their root cause where one existed, so that a single declaration or macro covers hundreds of sites, and with an explicit conversion or a scope-local rename otherwise. Edits preserved the existing file encodings (parts of the tree are Windows-1252) and BOMs. A rebuild followed every batch, so that a regression could not hide behind an unrelated diagnostic, and both configurations were finally rebuilt from scratch.

The groups, in descending order of size
  1. C5054 (1,868 occurrences / 642 sites), comparisons between two different enumeration types, which C++20 deprecates. Three causes:

    • Classify() returns Class_T while the entity classes of this mod (CLASS_ASW_, CLASS_FUNC_) are a separate anonymous enum that continues the Class_T numbering from LAST_SHARED_ENTITY_CLASS. The enum is now named ASW_Class_T and the intended comparison with Class_T is stated once, as four inline ==/!= operators, instead of converting roughly 585 call sites.

    • ASW_COLLISION_GROUP_* continues Collision_Group_t and SHOULD_COLLIDE()/ASSERT_INVARIANT() compare the two numberings with <=. Those assertions now compare (int) values.

    • Six further sites (the COMPILE_TIME_ASSERT of bits_BUILD_* against bits_CAP_MOVE_, MAX( ASW_NUM_EQUIP_ ), two shadow flag bitmasks and MB_FIELD_TEXCOORD_LAST) take an explicit conversion.

  2. C4471 (1,150 / 12), forward declarations of unscoped enumerations without an underlying type: they now name : int, and the two enums that could be defined before their declaration (ASW_Skill, RD_Crafting_Material_t) received the same fixed type in their definition, so declaration order no longer matters. The unused DXGI_FORMAT forward declaration in bitmap/imageformat.h was removed: nothing in the tree refers to it and the DXGI headers define it without an explicit underlying type, so any fixed type would conflict with them.

  3. C4456/C4457/C4458/C4459 (112), shadowing of class members, globals, parameters and enclosing locals. The shadowing declaration was renamed inside its own scope only (index -> iIndex, view -> viewSetup, effects -> nEffects, flags -> nFlags, model -> pModel, pRenderContext -> pRenderCtx, ...), leaving access to members and statements preceding the declaration untouched. The five reports in CShadowDepthView::Draw come from VPROF_BUDGET() declaring a fixed local name, so nesting a budget inside the braces of an enclosing budget shadowed the outer scope object; the macro now uses the existing UNIQUE_ID.

    • One of the 112 was not renamed but removed: in CNPC_AttackHelicopter::Event_Killed the inner declaration re-fetched CSoundEnvelopeController::GetController(), a side-effect-free accessor that returns a reference to a file-static singleton (soundenvelope.cpp). The same object was therefore already in scope and the second declaration was redundant by construction. Every other case either declares a fresh object (a buffer, an RAII guard, a re-initialised local) or re-reads a value that is only incidentally identical, and keeps the rename.
  4. C4864 (56 / 2), a dependent template name used without the template keyword in CSOAAttributeReference::operator=.

  5. C5055 (21), enumeration constants used in floating point arithmetic, now converted to float explicitly.

  6. The single groups: C5033 (register), C4189 (locals that are dead, or referenced by Assert() only in a release build), C4211 (command callbacks that a friend declaration introduces with external linkage now use CON_COMMAND_EXTERN_F), C4005 (INVALID_HANDLE_VALUE is left to the platform headers on Windows and the duplicate ALIGN_VALUE in video_material.cpp is dropped), C4018, C4706, C4554 (parentheses only, the existing evaluation order is kept), C4463 (a 1-bit signed bit-field is made unsigned) and the release-only C4756, where HUGE_VAL becomes std::numeric_limits::infinity(): the same +inf value, but it does not overflow while the LTCG code generator folds the constant.

Not fixed: the five C5205 reports in asw_util_shared.cpp, video_material.cpp and optionssubmultiplayer.cpp. IVguiMatInfo, IVguiMatInfoVar and IDirectSoundBuffer are abstract interfaces implemented outside this solution (the VGUI surface of the prebuilt material system and the DirectSound SDK), and IVguiMatInfo.h explicitly asks the caller to delete the object. Adding a virtual destructor would change the vtable of an interface whose implementation is not rebuilt here, and replacing the delete with a release call would change ownership semantics, so both are worse than the diagnostic.

  • Verified with:
  • MSBuild src\reactivedrop_vs13.sln /t:Rebuild for Debug|Win32 and Release|Win32 - 0 errors in both, 5 warnings in both, all of them C5205; client.dll, server.dll and missionchooser.dll produced for each configuration.
  • Both the Release and the Debug DLLs were then swapped into the game installation and a smoke test was played on one solo map.

Enables /std:c++20 (LanguageStandard=stdcpp20) for the Client (Swarm),
Server (Swarm) and missionchooser projects in every build
configuration, and fixes every error the switch surfaces. No compiler
conformance switch was relaxed: neither /Zc:twoPhase- nor
/Zc:strictStrings- was added. Where the legacy code relied on a macro
workaround (clamp in mathlib.h), the workaround was removed and the
code fixed instead.

Process: complete build logs were collected per project in parallel so
that a failure in one project did not hide diagnostics in the others;
errors were grouped by root cause rather than by file; and each batch
was followed by a rebuild of every affected configuration. Every edit
was dry-run verified with occurrence-count checks, preserving existing
source encodings (including the Windows-1252 files in parts of the
tree). 167 files changed: 164 sources and the 3 project files.

1. Two-phase lookup and dependent base classes: C3861/C2065 for Base,
   Count, m_Size, GetTypeName, log10, CopyArray and others, plus C2027
   for incomplete types. Fixed with this->, explicit declarations,
   missing math/KeyValues includes and out-of-line definitions, in
   about 20 files, mostly headers. The Debug configuration additionally
   required this-> where Assert and #ifdef _DEBUG compile the call out
   in Release (utllinkedlist.h, utlobjectreference.h, ai_speech.h).

2. concept is a C++20 keyword: used as an identifier in the AI speech
   code, it produced C2059/C2143 and a cascade of about 31,000
   diagnostics in a single project build. Renamed 216 identifiers from
   concept to conceptName across 20 files, skipping comments and
   string literals.

3. clamp macro vs std::clamp: the legacy macro in mathlib.h corrupted
   <algorithm> (C2059/C2988) with large translation-unit cascades.
   Removed the macro and generalized the basetypes.h clamp template to
   three parameter types with explicit result conversion.

4. Strict string literals: about 3,000 C2440/C2664 diagnostics. Made
   the datatable API const-correct (dt_recv, dt_send, dt_utlvector_recv,
   dt_utlvector_send, ClientClass, ServerClass, recvproxy, sendproxy),
   which fixes the RECVINFO/SENDINFO macro call sites without touching
   the tables, and fixed about 60 further sites. Several of those are
   parameter changes, not call sites: UTIL_ImpactTrace and
   CBaseEntity::ImpactTrace including its overrides, SetSuitUpdate,
   UTIL_LogPrintf, GetTotalAmmoCount, CDescription::InitFromFile,
   UTIL_va, SetStatsFilename, AddGlobalFlexController, LoadHudTextures,
   and IMatSystemSurface DrawColoredText, DrawColoredTextRect,
   DrawTextHeight and DrawTextLen in IMatSystemSurface.h and
   IMatSystemSurfaceV5.h. The Debug-only name parameters of
   CBaseEntity::TouchSet, UseSet and BlockedSet were made const char* as
   well.

5. false is not a null pointer constant: conformance mode now enforces
   the C++11 rule, so bool to pointer conversions are rejected. Fixed
   chunkfile.cpp return(false) -> return(NULL) and a misplaced
   EmitSound argument. For that call the missing argument is supplied as
   an explicit NULL instead of casting the attenuation to soundlevel_t,
   which would silently select the other overload and change the
   attenuation.

6. C++20 rewritten comparison candidates: fixed C2666/C2593/C2445 for
   CUtlSymbol, DHANDLE, CUtlReference and CHandle with explicit
   IsValid()/Get() and casts for ScriptVariant_t.

7. Temporaries cannot bind to non-const references: fixed C2665/C2664
   in VectorNormalize, Tracer_Draw, MeleeAttack, SetMoveTarget,
   FindSnowVolumes, FX_MicroExplosion, ReadParticleConfigFile and
   GetParticleSystemsInBuffer by introducing local Vector, QAngle and
   CUtlBuffer variables.

8. Other conformance errors: C2216 friend static, C4596 qualified
   member declarations inside a class body, C2362 goto crossing an
   initialization, C2614 dependent-base injected-class-name in a
   mem-initializer, and a use before declaration in UtlCachedFileData.h
   (CSortedCacheFile was used near line 715 and defined near line 965).

9. Explicit specializations of class template members are not
   implicitly inline and legacy MSVC folded the duplicates: fixed
   LNK2005/LNK1169 by marking the four
   ITilegenClassFactory<T>::ReadLiteralValue specializations and
   C_SpatialEntityTemplate<Vector>::ResetAccumulation inline.

MaterialSystemUtil.h was intentionally left at char *pStrOptionalName:
MaterialSystemUtil.cpp belongs to none of the three projects and its
implementation is provided by the prebuilt engine, so const char *
produced LNK2001. The affected call sites use explicit casts instead.

Review notes: the changes are intended to be behaviour preserving,
conformance and const-correctness only, with explicit casts in a few
read-only legacy interfaces. Everything that only affects Debug is
gated by Assert or #ifdef _DEBUG, so the Release binaries are
unaffected. The build still emits warnings (3,161 in Release, 3,249 in
Debug) and the projects do not use /WX. Runtime validation covered one
solo map in each build only; no multiplayer or campaign-wide
regression run was performed.

Verified with: MSBuild src\reactivedrop_vs13.sln /t:Rebuild for
Release|Win32 and Debug|Win32 - 0 errors in both; client.dll,
server.dll and missionchooser.dll produced for each. Both the Release
and the Debug DLLs were then swapped into the game installation and a
smoke test was played on one solo map.
@Kitsune44 Kitsune44 changed the title Eliminate level-4 warnings from the Debug and Release builds Eliminate level-4 warnings Sep 28, 2026
@Kitsune44
Kitsune44 requested a review from BenLubar September 28, 2026 00:52
@anf3is

anf3is commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Most changes (all except register keyword?) do not depend on #1115. So probably should omit first commit and move relevant changes to that PR.
Besides, could you please group similar changes together as separate commits? It's pretty hard to review entire blob at once.

@Kitsune44
Kitsune44 marked this pull request as draft September 28, 2026 02:09
@Kitsune44
Kitsune44 force-pushed the cpp20-build_warnings branch 2 times, most recently from 34ec40c to 6c12199 Compare September 28, 2026 03:48
@Kitsune44
Kitsune44 removed the request for review from BenLubar September 28, 2026 04:03
The migration added (char *) and (wchar_t *) casts on string literals.
This change removes twelve of them, either by making a local const or by
const-correcting a whole in-repo call chain, and it adds no new cast:

1. asw_hud_3dmarinenames.cpp: IMatSystemSurface::DrawColoredText and
   DrawColoredTextRect already take a const char *fmt, so the two casts
   on those literals were redundant.
2. vhybridbutton.cpp and objectivetitlepanel.cpp: declare the
   wide-string locals const wchar_t * instead of casting L"" back to
   wchar_t *. ILocalize::Find returns wchar_t *, so adding const to the
   local is free, and ConvertUnicodeToANSI and ConstructString already
   accept const wchar_t *.
3. CNPC_BaseScanner::GetEngineSound and GetScannerSoundPrefix
   (npc_basescanner.h, npc_scanner.h/.cpp): the only call sites are
   CSoundEnvelopeController::SoundCreate, which takes const char *, and
   a sprintf. Both classes live in the server binary, so the return type
   is changed for the whole chain.
4. CBaseCombatWeapon::GetDeathNoticeName
   (basecombatweapon_shared.h, basecombatweapon_shared.cpp): its only
   caller already keeps the result in a const char *.
5. C_BaseEntity tesla effect data (fx.h, fx.cpp): m_pszSpriteName is
   only ever read, its consumer FX_BuildTesla already takes
   const char *, and its other producer is a char[256] member.

Deliberately not touched, because each would move a cast rather than
remove it, or would const-correct only half of a contract whose
boundary stays mutable:

- keyframe.cpp keeps TimeModifier_t::szName and
  RotationInterpolator_t::szName as char *. IPositionInterpolator::
  GetDetails and Motion_Get*Details hand out a mutable char * through
  char ** outName, so making the fields const would only push the cast
  from the table initialisers to the output assignment.
- studiohdr_t::pszNodeName and the Motion_Get*Details helpers keep
  char *: they have no caller in this tree, so they are exported entry
  points and a signature change would change the mangled name, the same
  class of breakage as MaterialSystemUtil.cpp, which is not built here
  either.
- IHudTextMessage::LookupString keeps char *: it hands out a writable
  pointer and asw_hud_chat.cpp strips newlines in place.
- IVTex::VTex(int, char **) is argv-style, so its arguments may be
  written to; the fix there is a writable buffer, not a cast.
- ReadAndAllocStringValue returns caller-owned memory, so its char *
  return type is deliberate; its failure path returning a literal is a
  separate ownership issue.
- CSplitString stores char * elements by design and is implemented
  outside this tree.
- CTextureReference::InitRenderTarget and friends keep char *: their
  implementation is in the prebuilt engine, so const char * there
  produced LNK2001.

No behaviour change: every signature changed here is only read from, and
every removed cast re-created exactly the expression that replaced it.

Verified with: MSBuild for the client and server projects in
Release|Win32, Debug|Win32 and Profile|Win32 - 0 errors in all six.
@Kitsune44
Kitsune44 marked this pull request as ready for review September 28, 2026 04:36
@Kitsune44
Kitsune44 marked this pull request as draft September 28, 2026 04:36
The C++20 migration extracted a named local for every temporary that had
to bind to a non-const reference. All of those parameters are ours and
are only read, so the fix belongs in the signatures instead, and callers
can pass vector and angle arithmetic directly again.

- Tracer_Draw (c_tracer.h, c_tracer.cpp): both overloads take
  const Vector& start and const Vector& delta. The function forwards
  them to Tracer_ComputeVerts, which already takes them by const
  reference, so the non-const parameters contradicted the line below.
- CSnowFallManager::FindSnowVolumes (c_effects.cpp): vecEyePos is only
  read, so it is const Vector& now.
- CASW_Simple_Alien::SetMoveTarget (asw_simple_alien.h,
  asw_simple_alien.cpp): it only stores the value, so it is
  const Vector& now. TryMove and ApplyGravity keep Vector& because they
  modify their argument.
- The melee helpers CASW_Drone_Advanced::MeleeAttack,
  CASW_Shieldbug::MeleeAttack and CASW_Simple_Alien::MeleeAttack (three
  independent class hierarchies) take const QAngle& viewPunch and
  const Vector& shove now. Their bodies only read both, through
  ViewPunch() and the shove components. Six call sites lose their
  QAngle/Vector locals.
- FX_MicroExplosion (fx.h, fx_sparks.cpp): const Vector& for both
  parameters. It only reads them, and the address it passes on goes to a
  Setup() that already takes const Vector*.
- asw_broadcast_camera.cpp no longer extracts a vector just to feed the
  normalising VectorNormalize(Vector&): the call uses VectorLength(),
  which takes a const reference. VectorNormalize returned v.Length(),
  and Vector::Length() is literally VectorLength( *this ), while the
  normalised vector was never read again, so the value is identical.

The extracted locals are gone from all fourteen call sites, and the
calls pass the arithmetic directly again.

Left alone, because a signature change is not the fix there:
IParticleSystemMgr::ReadParticleConfigFile and
GetParticleSystemsInBuffer take CUtlBuffer& because parsing consumes the
buffer, and that interface is implemented by the engine (declared in
public/particles/particles.h, with no implementation in this tree), so
the two locals in entity_client_tools.cpp are correct as they are.

No behaviour change: every parameter that became const is only read, and
the restored call expressions are exactly the ones the migration
replaced.

Verified with: MSBuild for the client and server projects in
Release|Win32 - 0 errors in both.
Reduces the warning count of src/reactivedrop_vs13.sln from 3,249
lines in Debug|Win32 (17 codes, 824 distinct file:line sites) and
3,161 in Release|Win32 to 5 in both configurations, all of them the
C5205 uses documented at the end. The projects keep /W4, do not use
/WX, and no warning was disabled, no pragma added and no conformance
switch relaxed. Every edit is behaviour preserving.

Warnings were fixed at their root cause where one existed, so that a
single declaration or macro covers hundreds of sites, and with an
explicit conversion or a scope-local rename otherwise. Edits preserved
the existing file encodings (parts of the tree are Windows-1252) and
BOMs. A rebuild followed every batch, so that a regression could not
hide behind an unrelated diagnostic, and both configurations were
finally rebuilt from scratch.

The groups, in descending order of size:

1. C5054 (1,868 occurrences / 642 sites), comparisons between two
   different enumeration types, which C++20 deprecates. Three causes:

   - Classify() returns Class_T while the entity classes of this mod
     (CLASS_ASW_*, CLASS_FUNC_*) are a separate anonymous enum that
     continues the Class_T numbering from LAST_SHARED_ENTITY_CLASS.
     The enum is now named ASW_Class_T and the intended comparison with
     Class_T is stated once, as four inline ==/!= operators, instead of
     converting roughly 585 call sites.

   - ASW_COLLISION_GROUP_* continues Collision_Group_t and
     SHOULD_COLLIDE()/ASSERT_INVARIANT() compare the two numberings
     with <=. Those assertions now compare (int) values.

   - Six further sites (the COMPILE_TIME_ASSERT of bits_BUILD_* against
     bits_CAP_MOVE_*, MAX( ASW_NUM_EQUIP_* ), two shadow flag bitmasks
     and MB_FIELD_TEXCOORD_LAST) take an explicit conversion.

2. C4471 (1,150 / 12), forward declarations of unscoped enumerations
   without an underlying type: they now name : int, and the two enums
   that could be defined before their declaration (ASW_Skill,
   RD_Crafting_Material_t) received the same fixed type in their
   definition, so declaration order no longer matters. The unused
   DXGI_FORMAT forward declaration in bitmap/imageformat.h was removed:
   nothing in the tree refers to it and the DXGI headers define it
   without an explicit underlying type, so any fixed type would
   conflict with them.

3. C4456/C4457/C4458/C4459 (112), shadowing of class members, globals,
   parameters and enclosing locals. The shadowing declaration was
   renamed inside its own scope only (index -> iIndex, view ->
   viewSetup, effects -> nEffects, flags -> nFlags, model -> pModel,
   pRenderContext -> pRenderCtx, ...), leaving access to members and
   statements preceding the declaration untouched. One of the 112 was
   not renamed but removed: the inner declaration in
   CNPC_AttackHelicopter::Event_Killed re-fetched
   CSoundEnvelopeController::GetController(), a side-effect-free
   accessor that returns a reference to a file-static singleton
   (soundenvelope.cpp). The same object was therefore already in scope
   and the second declaration was redundant by construction. Every other
   case either declares a fresh object (a buffer, an RAII guard, a
   re-initialised local) or re-reads a value that is only incidentally
   identical, and keeps the rename. The five reports in
   CShadowDepthView::Draw come from VPROF_BUDGET() declaring a fixed
   local name, so nesting a budget inside the braces of an enclosing
   budget shadowed the outer scope object; the macro now uses the
   existing UNIQUE_ID.

4. C4864 (56 / 2), a dependent template name used without the template
   keyword in CSOAAttributeReference::operator=.

5. C5055 (21), enumeration constants used in floating point arithmetic,
   now converted to float explicitly.

6. The single groups: C5033 (register), C4189 (locals that are dead, or
   referenced by Assert() only in a release build), C4211 (command
   callbacks that a friend declaration introduces with external linkage
   now use CON_COMMAND_EXTERN_F), C4005 (INVALID_HANDLE_VALUE is left
   to the platform headers on Windows and the duplicate ALIGN_VALUE in
   video_material.cpp is dropped), C4018, C4706, C4554 (parentheses
   only, the existing evaluation order is kept), C4463 (a 1-bit signed
   bit-field is made unsigned) and the release-only C4756, where
   HUGE_VAL becomes std::numeric_limits<float>::infinity(): the same
   +inf value, but it does not overflow while the LTCG code generator
   folds the constant.

Not fixed: the five C5205 reports in asw_util_shared.cpp,
video_material.cpp and optionssubmultiplayer.cpp. IVguiMatInfo,
IVguiMatInfoVar and IDirectSoundBuffer are abstract interfaces
implemented outside this solution (the VGUI surface of the prebuilt
material system and the DirectSound SDK), and IVguiMatInfo.h explicitly
asks the caller to delete the object. Adding a virtual destructor would
change the vtable of an interface whose implementation is not rebuilt
here, and replacing the delete with a release call would change
ownership semantics, so both are worse than the diagnostic.

Verified with: MSBuild src\reactivedrop_vs13.sln /t:Rebuild for
Debug|Win32 and Release|Win32 - 0 errors in both, 5 warnings in both,
all of them C5205; client.dll, server.dll and missionchooser.dll
produced for each configuration. Both the Release and the Debug DLLs
were then swapped into the game installation and a smoke test was
played on one solo map.
@Kitsune44 Kitsune44 closed this Sep 28, 2026
@Kitsune44
Kitsune44 deleted the cpp20-build_warnings branch September 28, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants