https://github.com/hnrklssn updated 
https://github.com/llvm/llvm-project/pull/189105

>From 79e68fa0c9b830850516c4a4e1be4063d97fe2a2 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <[email protected]>
Date: Thu, 26 Mar 2026 18:49:52 -0700
Subject: [PATCH 1/5] [LLVM] remove redundant uses of dyn_cast (NFC)

This removes dyn_cast invocations where the argument is already of the
target type (including through subtyping). This was created by adding a
static assert in dyn_cast and letting an LLM iterate until the code base
compiled. I then went through each example and cleaned it up. This does
not commit the static assert in dyn_cast, because it would prevent a lot
of uses in templated code. To prevent backsliding we should instead add
an LLVM aware version of 
https://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-casting.html
(or expand the existing one).
---
 llvm/lib/Analysis/ConstantFolding.cpp         |  6 +-
 llvm/lib/Analysis/IVDescriptors.cpp           |  2 +-
 llvm/lib/Analysis/InlineCost.cpp              |  6 +-
 llvm/lib/Analysis/ModuleSummaryAnalysis.cpp   |  2 +-
 llvm/lib/Analysis/StackSafetyAnalysis.cpp     |  2 +-
 llvm/lib/Analysis/ValueTracking.cpp           |  8 +-
 llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp     |  3 +-
 llvm/lib/CodeGen/GlobalMerge.cpp              |  4 +-
 llvm/lib/CodeGen/InterleavedAccessPass.cpp    |  4 +-
 llvm/lib/TableGen/TGParser.cpp                |  4 +-
 llvm/lib/Target/ARM/ARMISelLowering.cpp       |  3 +-
 .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp |  6 +-
 llvm/lib/Transforms/IPO/FunctionImport.cpp    |  5 +-
 llvm/lib/Transforms/IPO/GlobalDCE.cpp         | 14 ++-
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         | 20 ++--
 llvm/lib/Transforms/IPO/LowerTypeTests.cpp    |  7 +-
 .../Transforms/InstCombine/InstCombinePHI.cpp | 14 ++-
 .../InstCombineSimplifyDemanded.cpp           |  8 +-
 .../Scalar/CorrelatedValuePropagation.cpp     |  3 +-
 .../Transforms/Scalar/DFAJumpThreading.cpp    |  4 +-
 llvm/lib/Transforms/Utils/SimplifyCFG.cpp     |  2 +-
 llvm/tools/llvm-objdump/MachODump.cpp         |  5 +-
 llvm/tools/llvm-size/llvm-size.cpp            | 95 +++++++++----------
 23 files changed, 101 insertions(+), 126 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp 
b/llvm/lib/Analysis/ConstantFolding.cpp
index 9dbab56348411..e9eea82d60116 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -3736,7 +3736,7 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID 
IntrinsicID, Type *Ty,
     case Intrinsic::amdgcn_wave_reduce_and:
     case Intrinsic::amdgcn_wave_reduce_or:
     case Intrinsic::amdgcn_wave_reduce_xor:
-      return dyn_cast<Constant>(Operands[0]);
+      return Operands[0];
     }
 
     return nullptr;
@@ -4324,8 +4324,8 @@ static Constant *ConstantFoldScalableVectorCall(
     const TargetLibraryInfo *TLI, const CallBase *Call) {
   switch (IntrinsicID) {
   case Intrinsic::aarch64_sve_convert_from_svbool: {
-    auto *Src = dyn_cast<Constant>(Operands[0]);
-    if (!Src || !Src->isNullValue())
+    Constant *Src = Operands[0];
+    if (!Src->isNullValue())
       break;
 
     return ConstantInt::getFalse(SVTy);
diff --git a/llvm/lib/Analysis/IVDescriptors.cpp 
b/llvm/lib/Analysis/IVDescriptors.cpp
index 86450c9ff6e76..e0255d482d876 100644
--- a/llvm/lib/Analysis/IVDescriptors.cpp
+++ b/llvm/lib/Analysis/IVDescriptors.cpp
@@ -1390,7 +1390,7 @@ InductionDescriptor::InductionDescriptor(Value *Start, 
InductionKind K,
 
 ConstantInt *InductionDescriptor::getConstIntStepValue() const {
   if (isa<SCEVConstant>(Step))
-    return dyn_cast<ConstantInt>(cast<SCEVConstant>(Step)->getValue());
+    return cast<SCEVConstant>(Step)->getValue();
   return nullptr;
 }
 
diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index d674743669ba6..f0ce00b24704f 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -1004,11 +1004,9 @@ class InlineCostCallAnalyzer final : public CallAnalyzer 
{
         } else if (SwitchInst *SI = dyn_cast<SwitchInst>(&I)) {
           if (getSimplifiedValue<ConstantInt>(SI->getCondition()))
             CurrentSavings += InstrCost;
-        } else if (Value *V = dyn_cast<Value>(&I)) {
+        } else if (SimplifiedValues.count(&I)) {
           // Count an instruction as savings if we can fold it.
-          if (SimplifiedValues.count(V)) {
-            CurrentSavings += InstrCost;
-          }
+          CurrentSavings += InstrCost;
         }
       }
 
diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp 
b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index f504519ebb5a6..0879fa627c709 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -766,7 +766,7 @@ static void findFuncPointers(const Constant *I, uint64_t 
StartingOffset,
   // look for virtual function pointers.
   const DataLayout &DL = M.getDataLayout();
   if (auto *C = dyn_cast<ConstantStruct>(I)) {
-    StructType *STy = dyn_cast<StructType>(C->getType());
+    StructType *STy = C->getType();
     assert(STy);
     const StructLayout *SL = DL.getStructLayout(C->getType());
 
diff --git a/llvm/lib/Analysis/StackSafetyAnalysis.cpp 
b/llvm/lib/Analysis/StackSafetyAnalysis.cpp
index fbe74d21c7199..024a30f10e8f0 100644
--- a/llvm/lib/Analysis/StackSafetyAnalysis.cpp
+++ b/llvm/lib/Analysis/StackSafetyAnalysis.cpp
@@ -899,7 +899,7 @@ const StackSafetyInfo::InfoTy &StackSafetyInfo::getInfo() 
const {
 }
 
 void StackSafetyInfo::print(raw_ostream &O) const {
-  getInfo().Info.print(O, F->getName(), dyn_cast<Function>(F));
+  getInfo().Info.print(O, F->getName(), F);
   O << "\n";
 }
 
diff --git a/llvm/lib/Analysis/ValueTracking.cpp 
b/llvm/lib/Analysis/ValueTracking.cpp
index 23b0e1de29931..3528cc963e434 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -632,11 +632,9 @@ static bool isEphemeralValueOf(const Instruction *I, const 
Value *E) {
       if (V == I || (!V->mayHaveSideEffects() && !V->isTerminator())) {
         EphValues.insert(V);
 
-        if (const User *U = dyn_cast<User>(V)) {
-          for (const Use &U : U->operands()) {
-            if (const auto *I = dyn_cast<Instruction>(U.get()))
-              WorkSet.push_back(I);
-          }
+        for (const Use &U : V->operands()) {
+          if (const auto *I = dyn_cast<Instruction>(U.get()))
+            WorkSet.push_back(I);
         }
       }
     }
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp 
b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 3eae524c53916..35663f2acaae9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -1144,8 +1144,7 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const 
DICompositeType *CTy) {
           constructTypeDIE(VariantPart, Composite);
         }
       } else if (Tag == dwarf::DW_TAG_namelist) {
-        auto *Var = dyn_cast<DINode>(Element);
-        auto *VarDIE = getDIE(Var);
+        auto *VarDIE = getDIE(Element);
         if (VarDIE) {
           DIE &ItemDie = createAndAddDIE(dwarf::DW_TAG_namelist_item, Buffer);
           addDIEEntry(ItemDie, dwarf::DW_AT_namelist_item, *VarDIE);
diff --git a/llvm/lib/CodeGen/GlobalMerge.cpp b/llvm/lib/CodeGen/GlobalMerge.cpp
index 37f0bcdee10db..d52706b5f9bef 100644
--- a/llvm/lib/CodeGen/GlobalMerge.cpp
+++ b/llvm/lib/CodeGen/GlobalMerge.cpp
@@ -701,9 +701,7 @@ bool GlobalMergeImpl::run(Module &M) {
         !GV.hasLocalLinkage())
       continue;
 
-    PointerType *PT = dyn_cast<PointerType>(GV.getType());
-    assert(PT && "Global variable is not a pointer!");
-
+    PointerType *PT = GV.getType();
     unsigned AddressSpace = PT->getAddressSpace();
     StringRef Section = GV.getSection();
 
diff --git a/llvm/lib/CodeGen/InterleavedAccessPass.cpp 
b/llvm/lib/CodeGen/InterleavedAccessPass.cpp
index 9df7d53c63ff3..bdfbeeaa1d6a1 100644
--- a/llvm/lib/CodeGen/InterleavedAccessPass.cpp
+++ b/llvm/lib/CodeGen/InterleavedAccessPass.cpp
@@ -741,9 +741,7 @@ bool InterleavedAccessImpl::lowerInterleaveIntrinsic(
     IntrinsicInst *IntII, SmallSetVector<Instruction *, 32> &DeadInsts) {
   if (!IntII->hasOneUse())
     return false;
-  Instruction *StoredBy = dyn_cast<Instruction>(IntII->user_back());
-  if (!StoredBy)
-    return false;
+  Instruction *StoredBy = IntII->user_back();
   auto *SI = dyn_cast<StoreInst>(StoredBy);
   auto *II = dyn_cast<IntrinsicInst>(StoredBy);
   if (!SI && !II)
diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index f7b17500d8042..25405eef60366 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -194,8 +194,8 @@ const Init *TGVarScope::getVar(RecordKeeper &Records,
   case SK_ForeachLoop: {
     // The variable is a loop iterator?
     if (CurLoop->IterVar) {
-      const auto *IterVar = dyn_cast<VarInit>(CurLoop->IterVar);
-      if (IterVar && IterVar->getNameInit() == Name)
+      const VarInit *IterVar = CurLoop->IterVar;
+      if (IterVar->getNameInit() == Name)
         return IterVar;
     }
     break;
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp 
b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index ffe4707fbd76b..87996a41c44b6 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -3205,8 +3205,7 @@ SDValue ARMTargetLowering::LowerConstantPool(SDValue Op,
         Twine(DAG.getDataLayout().getInternalSymbolPrefix()) + "CP" +
             Twine(DAG.getMachineFunction().getFunctionNumber()) + "_" +
             Twine(AFI->createPICLabelUId()));
-    SDValue GA = DAG.getTargetGlobalAddress(dyn_cast<GlobalValue>(GV),
-                                            dl, PtrVT);
+    SDValue GA = DAG.getTargetGlobalAddress(GV, dl, PtrVT);
     return LowerGlobalAddress(GA, DAG);
   }
 
diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp 
b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index 5dfec3f18814c..fa89a400bba5a 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -6880,7 +6880,7 @@ static bool isThumbI8Relocation(MCParsedAsmOperand &MCOp) 
{
   const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
   if (CE)
     return false;
-  const MCExpr *E = dyn_cast<MCExpr>(Op.getImm());
+  const MCExpr *E = Op.getImm();
   if (!E)
     return false;
   auto *ARM16Expr = dyn_cast<MCSpecifierExpr>(E);
@@ -7655,7 +7655,7 @@ static bool isARMMCExpr(MCParsedAsmOperand &MCOp) {
   const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
   if (CE)
     return false;
-  const MCExpr *E = dyn_cast<MCExpr>(Op.getImm());
+  const MCExpr *E = Op.getImm();
   if (!E)
     return false;
   return true;
@@ -8288,7 +8288,7 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
     ARMOperand &Op = static_cast<ARMOperand &>(*Operands[i]);
     const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
     if (CE) break;
-    const MCExpr *E = dyn_cast<MCExpr>(Op.getImm());
+    const MCExpr *E = Op.getImm();
     if (!E) break;
     auto *ARM16Expr = dyn_cast<MCSpecifierExpr>(E);
     if (!ARM16Expr || (ARM16Expr->getSpecifier() != ARM::S_HI16 &&
diff --git a/llvm/lib/Transforms/IPO/FunctionImport.cpp 
b/llvm/lib/Transforms/IPO/FunctionImport.cpp
index 4d4b32749a045..82fbab5e1857f 100644
--- a/llvm/lib/Transforms/IPO/FunctionImport.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionImport.cpp
@@ -1107,9 +1107,8 @@ void ModuleImportsManager::computeImportForModule(
     auto *Summary = std::get<0>(GVInfo);
     auto Threshold = std::get<1>(GVInfo);
 
-    if (auto *FS = dyn_cast<FunctionSummary>(Summary))
-      computeImportForFunction(*FS, Threshold, DefinedGVSummaries, Worklist,
-                               GVI, ImportList, ImportThresholds);
+    computeImportForFunction(*Summary, Threshold, DefinedGVSummaries, Worklist,
+                             GVI, ImportList, ImportThresholds);
   }
 
   // Print stats about functions considered but rejected for importing
diff --git a/llvm/lib/Transforms/IPO/GlobalDCE.cpp 
b/llvm/lib/Transforms/IPO/GlobalDCE.cpp
index d6b19bd4e204e..ad794a9f04111 100644
--- a/llvm/lib/Transforms/IPO/GlobalDCE.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalDCE.cpp
@@ -169,14 +169,12 @@ void GlobalDCEPass::ScanVTables(Module &M) {
     // If the type corresponding to the vtable is private to this translation
     // unit, we know that we can see all virtual functions which might use it,
     // so VFE is safe.
-    if (auto GO = dyn_cast<GlobalObject>(&GV)) {
-      GlobalObject::VCallVisibility TypeVis = GO->getVCallVisibility();
-      if (TypeVis == GlobalObject::VCallVisibilityTranslationUnit ||
-          (InLTOPostLink &&
-           TypeVis == GlobalObject::VCallVisibilityLinkageUnit)) {
-        LLVM_DEBUG(dbgs() << GV.getName() << " is safe for VFE\n");
-        VFESafeVTables.insert(&GV);
-      }
+    GlobalObject::VCallVisibility TypeVis = GV.getVCallVisibility();
+    if (TypeVis == GlobalObject::VCallVisibilityTranslationUnit ||
+        (InLTOPostLink &&
+         TypeVis == GlobalObject::VCallVisibilityLinkageUnit)) {
+      LLVM_DEBUG(dbgs() << GV.getName() << " is safe for VFE\n");
+      VFESafeVTables.insert(&GV);
     }
   }
 }
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp 
b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 3ef2d377b7a0f..75d0f27169822 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -2061,16 +2061,16 @@ OptimizeGlobalVars(Module &M,
     if (!GV.hasName() && !GV.isDeclaration() && !GV.hasLocalLinkage())
       GV.setLinkage(GlobalValue::InternalLinkage);
     // Simplify the initializer.
-    if (GV.hasInitializer())
-      if (auto *C = dyn_cast<Constant>(GV.getInitializer())) {
-        auto &DL = M.getDataLayout();
-        // TLI is not used in the case of a Constant, so use default nullptr
-        // for that optional parameter, since we don't have a Function to
-        // provide GetTLI anyway.
-        Constant *New = ConstantFoldConstant(C, DL, /*TLI*/ nullptr);
-        if (New != C)
-          GV.setInitializer(New);
-      }
+    if (GV.hasInitializer()) {
+      const Constant *C = GV.getInitializer();
+      auto &DL = M.getDataLayout();
+      // TLI is not used in the case of a Constant, so use default nullptr
+      // for that optional parameter, since we don't have a Function to
+      // provide GetTLI anyway.
+      Constant *New = ConstantFoldConstant(C, DL, /*TLI*/ nullptr);
+      if (New != C)
+        GV.setInitializer(New);
+    }
 
     if (deleteIfDead(GV, NotDiscardableComdats)) {
       Changed = true;
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp 
b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 00991cf73dafa..b3d317b1690c3 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -1945,12 +1945,7 @@ bool LowerTypeTestsModule::runForTesting(Module &M, 
ModuleAnalysisManager &AM) {
 
 static bool isDirectCall(Use& U) {
   auto *Usr = dyn_cast<CallInst>(U.getUser());
-  if (Usr) {
-    auto *CB = dyn_cast<CallBase>(Usr);
-    if (CB && CB->isCallee(&U))
-      return true;
-  }
-  return false;
+  return Usr && Usr->isCallee(&U);
 }
 
 void LowerTypeTestsModule::replaceCfiUses(Function *Old, Value *New,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp 
b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
index 44bd4d0e0f6b7..ea386df78bcba 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
@@ -1227,14 +1227,12 @@ Instruction 
*InstCombinerImpl::SliceUpIllegalIntegerPHI(PHINode &FirstPhi) {
           continue;
         }
 
-        if (PHINode *InPHI = dyn_cast<PHINode>(PN)) {
-          // If the incoming value was a PHI, and if it was one of the PHIs we
-          // already rewrote it, just use the lowered value.
-          if (Value *Res = ExtractedVals[LoweredPHIRecord(InPHI, Offset, Ty)]) 
{
-            PredVal = Res;
-            EltPHI->addIncoming(PredVal, Pred);
-            continue;
-          }
+        // If the incoming value was a PHI, and if it was one of the PHIs we
+        // already rewrote it, just use the lowered value.
+        if (Value *Res = ExtractedVals[LoweredPHIRecord(PN, Offset, Ty)]) {
+          PredVal = Res;
+          EltPHI->addIncoming(PredVal, Pred);
+          continue;
         }
 
         // Otherwise, do an extract in the predecessor.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp 
b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index e4a3144179c92..28cfa55b968dc 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -732,8 +732,8 @@ Value 
*InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
 
       // Do not simplify if shl is part of funnel-shift pattern
       if (I->hasOneUse()) {
-        auto *Inst = dyn_cast<Instruction>(I->user_back());
-        if (Inst && Inst->getOpcode() == BinaryOperator::Or) {
+        Instruction *Inst = I->user_back();
+        if (Inst->getOpcode() == BinaryOperator::Or) {
           if (auto Opt = convertOrOfShiftsToFunnelShift(*Inst)) {
             auto [IID, FShiftArgs] = *Opt;
             if ((IID == Intrinsic::fshl || IID == Intrinsic::fshr) &&
@@ -814,8 +814,8 @@ Value 
*InstCombinerImpl::SimplifyDemandedUseBits(Instruction *I,
 
       // Do not simplify if lshr is part of funnel-shift pattern
       if (I->hasOneUse()) {
-        auto *Inst = dyn_cast<Instruction>(I->user_back());
-        if (Inst && Inst->getOpcode() == BinaryOperator::Or) {
+        Instruction *Inst = I->user_back();
+        if (Inst->getOpcode() == BinaryOperator::Or) {
           if (auto Opt = convertOrOfShiftsToFunnelShift(*Inst)) {
             auto [IID, FShiftArgs] = *Opt;
             if ((IID == Intrinsic::fshl || IID == Intrinsic::fshr) &&
diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp 
b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 8eaf385065737..ff0b70b51e5f7 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -675,8 +675,7 @@ static bool processSaturatingInst(SaturatingInst *SI, 
LazyValueInfo *LVI) {
   ++NumSaturating;
 
   // See if we can infer the other no-wrap too.
-  if (auto *BO = dyn_cast<BinaryOperator>(BinOp))
-    processBinOp(BO, LVI);
+  processBinOp(BinOp, LVI);
 
   return true;
 }
diff --git a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp 
b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
index 2d8f730009f03..2be6c5ac2277d 100644
--- a/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/DFAJumpThreading.cpp
@@ -539,9 +539,9 @@ struct MainSwitch {
     if (!SI->hasOneUse())
       return false;
 
-    Instruction *SIUse = dyn_cast<Instruction>(SI->user_back());
+    Instruction *SIUse = SI->user_back();
     // The use of the select inst should be either a phi or another select.
-    if (!SIUse || !(isa<PHINode>(SIUse) || isa<SelectInst>(SIUse)))
+    if (!isa<PHINode, SelectInst>(SIUse))
       return false;
 
     BasicBlock *SIBB = SI->getParent();
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp 
b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 77800ed7c6926..be78d7350fff8 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -4816,7 +4816,7 @@ static bool SimplifyCondBranchToCondBranch(CondBrInst 
*PBI, CondBrInst *BI,
       if (auto *SI = dyn_cast<SelectInst>(Cond)) {
         assert(isSelectInRoleOfConjunctionOrDisjunction(SI));
         // The select is predicated on PBICond
-        assert(dyn_cast<SelectInst>(SI)->getCondition() == PBICond);
+        assert(SI->getCondition() == PBICond);
         // The corresponding probabilities are what was referred to above as
         // PredCommon and PredOther.
         setFittedBranchWeights(*SI, {PredCommon, PredOther},
diff --git a/llvm/tools/llvm-objdump/MachODump.cpp 
b/llvm/tools/llvm-objdump/MachODump.cpp
index 1a574997cf4d0..f5e7c94c32578 100644
--- a/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/llvm/tools/llvm-objdump/MachODump.cpp
@@ -2765,9 +2765,8 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) {
         }
         if (MachOObjectFile *O =
                 dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
-          if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O))
-            ProcessMachO(Filename, MachOOF, MachOOF->getFileName(),
-                          ArchitectureName);
+          ProcessMachO(Filename, O, O->getFileName(),
+                        ArchitectureName);
         }
       }
       if (Err)
diff --git a/llvm/tools/llvm-size/llvm-size.cpp 
b/llvm/tools/llvm-size/llvm-size.cpp
index 400fcdf7b6fa6..77058368bf4c3 100644
--- a/llvm/tools/llvm-size/llvm-size.cpp
+++ b/llvm/tools/llvm-size/llvm-size.cpp
@@ -608,22 +608,21 @@ static void printFileSectionSizes(StringRef file) {
             ArchFound = true;
             Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
             if (UO) {
-              if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
-                MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
-                if (OutputFormat == sysv)
-                  outs() << o->getFileName() << "  :\n";
-                else if (MachO && OutputFormat == darwin) {
-                  if (MoreThanOneFile || ArchFlags.size() > 1)
-                    outs() << o->getFileName() << " (for architecture "
-                           << I->getArchFlagName() << "): \n";
-                }
-                printObjectSectionSizes(o);
-                if (OutputFormat == berkeley) {
-                  if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
-                    outs() << o->getFileName() << " (for architecture "
-                           << I->getArchFlagName() << ")";
-                  outs() << "\n";
-                }
+              ObjectFile *o = &*UO.get();
+              MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+              if (OutputFormat == sysv)
+                outs() << o->getFileName() << "  :\n";
+              else if (MachO && OutputFormat == darwin) {
+                if (MoreThanOneFile || ArchFlags.size() > 1)
+                  outs() << o->getFileName() << " (for architecture "
+                         << I->getArchFlagName() << "): \n";
+              }
+              printObjectSectionSizes(o);
+              if (OutputFormat == berkeley) {
+                if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
+                  outs() << o->getFileName() << " (for architecture "
+                         << I->getArchFlagName() << ")";
+                outs() << "\n";
               }
             } else if (auto E = isNotObjectErrorInvalidFileType(
                        UO.takeError())) {
@@ -699,22 +698,21 @@ static void printFileSectionSizes(StringRef file) {
         if (HostArchName == I->getArchFlagName()) {
           Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
           if (UO) {
-            if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
-              MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
-              if (OutputFormat == sysv)
-                outs() << o->getFileName() << "  :\n";
-              else if (MachO && OutputFormat == darwin) {
-                if (MoreThanOneFile)
-                  outs() << o->getFileName() << " (for architecture "
-                         << I->getArchFlagName() << "):\n";
-              }
-              printObjectSectionSizes(o);
-              if (OutputFormat == berkeley) {
-                if (!MachO || MoreThanOneFile)
-                  outs() << o->getFileName() << " (for architecture "
-                         << I->getArchFlagName() << ")";
-                outs() << "\n";
-              }
+            ObjectFile *o = &*UO.get();
+            MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+            if (OutputFormat == sysv)
+              outs() << o->getFileName() << "  :\n";
+            else if (MachO && OutputFormat == darwin) {
+              if (MoreThanOneFile)
+                outs() << o->getFileName() << " (for architecture "
+                       << I->getArchFlagName() << "):\n";
+            }
+            printObjectSectionSizes(o);
+            if (OutputFormat == berkeley) {
+              if (!MachO || MoreThanOneFile)
+                outs() << o->getFileName() << " (for architecture "
+                       << I->getArchFlagName() << ")";
+              outs() << "\n";
             }
           } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) 
{
             error(std::move(E), file);
@@ -774,23 +772,22 @@ static void printFileSectionSizes(StringRef file) {
          I != E; ++I) {
       Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
       if (UO) {
-        if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
-          MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
-          if (OutputFormat == sysv)
-            outs() << o->getFileName() << "  :\n";
-          else if (MachO && OutputFormat == darwin) {
-            if (MoreThanOneFile || MoreThanOneArch)
-              outs() << o->getFileName() << " (for architecture "
-                     << I->getArchFlagName() << "):";
-            outs() << "\n";
-          }
-          printObjectSectionSizes(o);
-          if (OutputFormat == berkeley) {
-            if (!MachO || MoreThanOneFile || MoreThanOneArch)
-              outs() << o->getFileName() << " (for architecture "
-                     << I->getArchFlagName() << ")";
-            outs() << "\n";
-          }
+        ObjectFile *o = &*UO.get();
+        MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+        if (OutputFormat == sysv)
+          outs() << o->getFileName() << "  :\n";
+        else if (MachO && OutputFormat == darwin) {
+          if (MoreThanOneFile || MoreThanOneArch)
+            outs() << o->getFileName() << " (for architecture "
+                   << I->getArchFlagName() << "):";
+          outs() << "\n";
+        }
+        printObjectSectionSizes(o);
+        if (OutputFormat == berkeley) {
+          if (!MachO || MoreThanOneFile || MoreThanOneArch)
+            outs() << o->getFileName() << " (for architecture "
+                   << I->getArchFlagName() << ")";
+          outs() << "\n";
         }
       } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
         error(std::move(E), file, MoreThanOneArch ?

>From aec0f22142875824012f6b3e70bfd6a6b1b7b6ce Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <[email protected]>
Date: Fri, 27 Mar 2026 13:40:20 -0700
Subject: [PATCH 2/5] fix formatting

---
 llvm/tools/llvm-objdump/MachODump.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/tools/llvm-objdump/MachODump.cpp 
b/llvm/tools/llvm-objdump/MachODump.cpp
index f5e7c94c32578..c59a0c8721dd0 100644
--- a/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/llvm/tools/llvm-objdump/MachODump.cpp
@@ -2765,8 +2765,7 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) {
         }
         if (MachOObjectFile *O =
                 dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
-          ProcessMachO(Filename, O, O->getFileName(),
-                        ArchitectureName);
+          ProcessMachO(Filename, O, O->getFileName(), ArchitectureName);
         }
       }
       if (Err)

>From df3e4c949dd3dfe0b2b1aa55b1f1871105836f67 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <[email protected]>
Date: Fri, 27 Mar 2026 17:05:57 -0700
Subject: [PATCH 3/5] remove dead code (NFCi)

This removes some dead code that is overly defensive with checking for
cases that never occur. All callers to `isThumbI8Relocation` first call
`isARMMCExpr`, so some of the code in `isThumbI8Relocation` is replaced
with an assert.
---
 llvm/lib/Analysis/IVDescriptors.cpp           |  4 ++--
 .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 24 ++++---------------
 2 files changed, 7 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Analysis/IVDescriptors.cpp 
b/llvm/lib/Analysis/IVDescriptors.cpp
index e0255d482d876..185943e0a9d2e 100644
--- a/llvm/lib/Analysis/IVDescriptors.cpp
+++ b/llvm/lib/Analysis/IVDescriptors.cpp
@@ -1389,8 +1389,8 @@ InductionDescriptor::InductionDescriptor(Value *Start, 
InductionKind K,
 }
 
 ConstantInt *InductionDescriptor::getConstIntStepValue() const {
-  if (isa<SCEVConstant>(Step))
-    return cast<SCEVConstant>(Step)->getValue();
+  if (auto *ConstStep = dyn_cast<SCEVConstant>(Step))
+    return ConstStep->getValue();
   return nullptr;
 }
 
diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp 
b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index fa89a400bba5a..b8380e4067f7a 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -6871,19 +6871,13 @@ void ARMAsmParser::tryConvertingToTwoOperandForm(
   }
 }
 
+static bool isARMMCExpr(MCParsedAsmOperand &MCOp);
 // this function returns true if the operand is one of the following
 // relocations: :upper8_15:, :upper0_7:, :lower8_15: or :lower0_7:
 static bool isThumbI8Relocation(MCParsedAsmOperand &MCOp) {
+  assert(isARMMCExpr(MCOp));
   ARMOperand &Op = static_cast<ARMOperand &>(MCOp);
-  if (!Op.isImm())
-    return false;
-  const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
-  if (CE)
-    return false;
-  const MCExpr *E = Op.getImm();
-  if (!E)
-    return false;
-  auto *ARM16Expr = dyn_cast<MCSpecifierExpr>(E);
+  auto *ARM16Expr = dyn_cast<MCSpecifierExpr>(Op.getImm());
   if (ARM16Expr && (ARM16Expr->getSpecifier() == ARM::S_HI_8_15 ||
                     ARM16Expr->getSpecifier() == ARM::S_HI_0_7 ||
                     ARM16Expr->getSpecifier() == ARM::S_LO_8_15 ||
@@ -7652,13 +7646,7 @@ static bool isARMMCExpr(MCParsedAsmOperand &MCOp) {
   ARMOperand &Op = static_cast<ARMOperand &>(MCOp);
   if (!Op.isImm())
     return false;
-  const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
-  if (CE)
-    return false;
-  const MCExpr *E = Op.getImm();
-  if (!E)
-    return false;
-  return true;
+  return !isa<MCConstantExpr>(Op.getImm());
 }
 
 // FIXME: We would really like to be able to tablegen'erate this.
@@ -8286,10 +8274,8 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
     int i = (Operands[MnemonicOpsEndInd]->isImm()) ? MnemonicOpsEndInd
                                                    : MnemonicOpsEndInd + 1;
     ARMOperand &Op = static_cast<ARMOperand &>(*Operands[i]);
-    const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
-    if (CE) break;
     const MCExpr *E = Op.getImm();
-    if (!E) break;
+    if (isa<MCConstantExpr>(E)) break;
     auto *ARM16Expr = dyn_cast<MCSpecifierExpr>(E);
     if (!ARM16Expr || (ARM16Expr->getSpecifier() != ARM::S_HI16 &&
                        ARM16Expr->getSpecifier() != ARM::S_LO16))

>From ab4042426d0caf438d36f30c32eb97ec213e27a0 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <[email protected]>
Date: Fri, 27 Mar 2026 17:09:36 -0700
Subject: [PATCH 4/5] fix formatting

---
 llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp 
b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index b8380e4067f7a..c7dd09120b8a0 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -8275,7 +8275,8 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
                                                    : MnemonicOpsEndInd + 1;
     ARMOperand &Op = static_cast<ARMOperand &>(*Operands[i]);
     const MCExpr *E = Op.getImm();
-    if (isa<MCConstantExpr>(E)) break;
+    if (isa<MCConstantExpr>(E))
+      break;
     auto *ARM16Expr = dyn_cast<MCSpecifierExpr>(E);
     if (!ARM16Expr || (ARM16Expr->getSpecifier() != ARM::S_HI16 &&
                        ARM16Expr->getSpecifier() != ARM::S_LO16))

>From bff1e3f8cf1b530d2b66f8e5785f905cf2c80b33 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <[email protected]>
Date: Sun, 29 Mar 2026 10:39:21 -0700
Subject: [PATCH 5/5] [clang-tidy] detect redundant uses of LLVM's cast,
 dyn_cast

Warns when casting to the same pointee type, or when the pointee type is
a super type of the argument's super type.
Supported functions:
 - cast
 - cast_if_present
 - cast_or_null
 - dyn_cast
 - dyn_cast_if_present
 - dyn_cast_or_null
---
 .../clang-tidy/llvm/CMakeLists.txt            |   1 +
 .../clang-tidy/llvm/LLVMTidyModule.cpp        |   3 +
 .../clang-tidy/llvm/RedundantCastingCheck.cpp | 131 ++++++++++++++
 .../clang-tidy/llvm/RedundantCastingCheck.h   |  33 ++++
 clang-tools-extra/docs/ReleaseNotes.rst       |   5 +
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../checks/llvm/redundant-casting.rst         |  21 +++
 .../checkers/llvm/redundant-casting.cpp       | 165 ++++++++++++++++++
 8 files changed, 360 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp

diff --git a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
index a807f0ab65f87..c81882e0e2024 100644
--- a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
@@ -10,6 +10,7 @@ add_clang_library(clangTidyLLVMModule STATIC
   PreferIsaOrDynCastInConditionalsCheck.cpp
   PreferRegisterOverUnsignedCheck.cpp
   PreferStaticOverAnonymousNamespaceCheck.cpp
+  RedundantCastingCheck.cpp
   TwineLocalCheck.cpp
   TypeSwitchCaseTypesCheck.cpp
   UseNewMLIROpBuilderCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp 
b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
index c180574bdeed6..104fcf63712f7 100644
--- a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
@@ -16,6 +16,7 @@
 #include "PreferIsaOrDynCastInConditionalsCheck.h"
 #include "PreferRegisterOverUnsignedCheck.h"
 #include "PreferStaticOverAnonymousNamespaceCheck.h"
+#include "RedundantCastingCheck.h"
 #include "TwineLocalCheck.h"
 #include "TypeSwitchCaseTypesCheck.h"
 #include "UseNewMLIROpBuilderCheck.h"
@@ -43,6 +44,8 @@ class LLVMModule : public ClangTidyModule {
         "llvm-prefer-static-over-anonymous-namespace");
     CheckFactories.registerCheck<readability::QualifiedAutoCheck>(
         "llvm-qualified-auto");
+    CheckFactories.registerCheck<RedundantCastingCheck>(
+        "llvm-redundant-casting");
     CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
     CheckFactories.registerCheck<TypeSwitchCaseTypesCheck>(
         "llvm-type-switch-case-types");
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp 
b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
new file mode 100644
index 0000000000000..9f02782dea265
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantCastingCheck.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::llvm_check {
+
+namespace {
+AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }
+AST_MATCHER_P(OverloadExpr, hasAnyUnresolvedName, ArrayRef<StringRef>, Names) {
+  auto DeclName = Node.getName();
+  if (!DeclName.isIdentifier())
+    return false;
+  const IdentifierInfo *II = DeclName.getAsIdentifierInfo();
+  return llvm::any_of(Names, [II](StringRef Name) { return II->isStr(Name); });
+}
+} // namespace
+
+static StringRef FunctionNames[] = {
+    "cast",     "cast_or_null",     "cast_if_present",
+    "dyn_cast", "dyn_cast_or_null", "dyn_cast_if_present"};
+
+void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
+  auto AnyCalleeName = [](ArrayRef<StringRef> CalleeName) {
+    return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+                 callee(expr(ignoringImpCasts(
+                     declRefExpr(to(namedDecl(hasAnyName(CalleeName))),
+                                 hasAnyTemplateArgumentLoc(anything()))
+                         .bind("callee")))));
+  };
+  auto AnyCalleeNameInUninstantiatedTemplate =
+      [](ArrayRef<StringRef> CalleeName) {
+        return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+                     callee(expr(ignoringImpCasts(
+                         unresolvedLookupExpr(hasAnyUnresolvedName(CalleeName))
+                             .bind("callee")))));
+      };
+  Finder->addMatcher(
+      callExpr(
+          AnyCalleeName(FunctionNames),
+          hasAncestor(functionDecl().bind("context")))
+          .bind("call"),
+      this);
+  Finder->addMatcher(
+      callExpr(AnyCalleeNameInUninstantiatedTemplate(FunctionNames))
+          .bind("call"),
+      this);
+}
+
+static QualType stripPointerOrReference(QualType Ty) {
+  QualType Pointee = Ty->getPointeeType();
+  if (Pointee.isNull())
+    return Ty;
+  return Pointee;
+}
+
+void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto &Nodes = Result.Nodes;
+  const auto *Call = Nodes.getNodeAs<CallExpr>("call");
+  if (Call->getNumArgs() != 1)
+    return;
+
+  CanQualType RetTy;
+  std::string FuncName;
+  if (const auto *ResolvedCallee = Nodes.getNodeAs<DeclRefExpr>("callee")) {
+    const auto *F = dyn_cast<FunctionDecl>(ResolvedCallee->getDecl());
+    const auto *SurroundingFunc = Nodes.getNodeAs<FunctionDecl>("context");
+    // Casts can be redundant for some instantiations but not others.
+    // Only emit warnings in templates in the uninstantated versions.
+    if (SurroundingFunc->isTemplateInstantiation())
+      return;
+
+    RetTy = stripPointerOrReference(F->getReturnType())
+                ->getCanonicalTypeUnqualified();
+    FuncName = F->getName();
+  } else if (const auto *UnresolvedCallee =
+                 Nodes.getNodeAs<UnresolvedLookupExpr>("callee")) {
+    if (UnresolvedCallee->getNumTemplateArgs() != 1)
+      return;
+    auto TArg = UnresolvedCallee->template_arguments()[0].getArgument();
+    if (TArg.getKind() != TemplateArgument::Type)
+      return;
+
+    RetTy = TArg.getAsType()->getCanonicalTypeUnqualified();
+    FuncName = UnresolvedCallee->getName().getAsString();
+  } else {
+    return;
+  }
+
+  const auto *Arg = Call->getArg(0);
+  CanQualType FromTy =
+      stripPointerOrReference(Arg->getType())->getCanonicalTypeUnqualified();
+  const auto *FromDecl = FromTy->getAsCXXRecordDecl();
+  const auto *RetDecl = RetTy->getAsCXXRecordDecl();
+  bool IsDerived = FromDecl && RetDecl && FromDecl->isDerivedFrom(RetDecl);
+  if (FromTy != RetTy && !IsDerived)
+    return;
+
+  auto GetText = [&](SourceRange R) {
+    return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
+                                *Result.SourceManager, getLangOpts());
+  };
+  StringRef ArgText = GetText(Arg->getSourceRange());
+  diag(Call->getExprLoc(), "redundant use of '%0'")
+      << FuncName
+      << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
+  diag(Arg->getExprLoc(), "source expression %0 has type %1",
+       DiagnosticIDs::Note)
+      << Arg << Arg->IgnoreParenImpCasts()->getType();
+
+  if (FromTy != RetTy) {
+    diag(Arg->getExprLoc(), "%0 is a subtype of %1", DiagnosticIDs::Note)
+        << FromTy << RetTy;
+  }
+}
+
+} // namespace clang::tidy::llvm_check
diff --git a/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h 
b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
new file mode 100644
index 0000000000000..3243616976014
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/llvm/RedundantCastingCheck.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::llvm_check {
+
+/// Detect redundant uses of LLVM's cast and dyn_cast functions.
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/llvm/redundant-casting.html
+class RedundantCastingCheck : public ClangTidyCheck {
+public:
+  RedundantCastingCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+};
+
+} // namespace clang::tidy::llvm_check
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REDUNDANTCASTINGCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index f8550e72dcc85..d2bc9fe4d69cb 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -114,6 +114,11 @@ New checks
   Finds functions where throwing exceptions is unsafe but the function is still
   marked as potentially throwing.
 
+- New :doc:`llvm-redundant-casting
+  <clang-tidy/checks/llvm/redundant-casting>` check.
+
+  Detect redundant uses of LLVM's cast functions.
+
 - New :doc:`llvm-type-switch-case-types
   <clang-tidy/checks/llvm/type-switch-case-types>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index ceab1e9414951..03ed10217fe45 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -251,6 +251,7 @@ Clang-Tidy Checks
    :doc:`llvm-prefer-isa-or-dyn-cast-in-conditionals 
<llvm/prefer-isa-or-dyn-cast-in-conditionals>`, "Yes"
    :doc:`llvm-prefer-register-over-unsigned 
<llvm/prefer-register-over-unsigned>`, "Yes"
    :doc:`llvm-prefer-static-over-anonymous-namespace 
<llvm/prefer-static-over-anonymous-namespace>`,
+   :doc:`llvm-redundant-casting <llvm/redundant-casting>`, "Yes"
    :doc:`llvm-twine-local <llvm/twine-local>`, "Yes"
    :doc:`llvm-type-switch-case-types <llvm/type-switch-case-types>`, "Yes"
    :doc:`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>`, "Yes"
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst 
b/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
new file mode 100644
index 0000000000000..2cd66c1364f31
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/redundant-casting.rst
@@ -0,0 +1,21 @@
+.. title:: clang-tidy - llvm-redundant-casting
+
+llvm-redundant-casting
+======================
+
+Points out uses of ``cast<>``, ``dyn_cast<>`` and their ``or_null`` variants 
that are unnecessary because the argument already is of the target type, or a 
derived type thereof.
+
+.. code-block:: c++
+  struct A {};
+  A a;
+  // Finds:
+  A x = cast<A>(a);
+  // replaced by:
+  A x = a;
+
+  struct B : public A {};
+  B b;
+  // Finds:
+  A y = cast<A>(b);
+  // replaced by:
+  A y = b;
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
new file mode 100644
index 0000000000000..4cb1972b557ce
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/redundant-casting.cpp
@@ -0,0 +1,165 @@
+// RUN: %check_clang_tidy -std=c++17 %s llvm-redundant-casting %t -- -- 
-fno-delayed-template-parsing
+
+namespace llvm {
+#define CAST_FUNCTION(name)                                                   \
+template <typename To, typename From>                                         \
+[[nodiscard]] inline decltype(auto) name(const From &Val) {                   \
+  return static_cast<const To&>(Val);                      \
+} \
+template <typename To, typename From>                                         \
+[[nodiscard]] inline decltype(auto) name(From &Val) {                   \
+  return static_cast<To&>(Val);                      \
+} \
+template <typename To, typename From>                                         \
+[[nodiscard]] inline decltype(auto) name(const From *Val) {                   \
+  return static_cast<const To*>(Val);                      \
+} \
+template <typename To, typename From>                                         \
+[[nodiscard]] inline decltype(auto) name(From *Val) {                   \
+  return static_cast<To*>(Val);                      \
+}
+CAST_FUNCTION(cast)
+CAST_FUNCTION(dyn_cast)
+CAST_FUNCTION(cast_or_null)
+CAST_FUNCTION(cast_if_present)
+CAST_FUNCTION(dyn_cast_or_null)
+CAST_FUNCTION(dyn_cast_if_present)
+}
+
+struct A {};
+struct B : A {};
+A &getA();
+
+void testCast(A& value) {
+  A& a1 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression 'value' has type 
'A'
+  // CHECK-FIXES: A& a1 = value;
+  (void)a1;
+}
+
+void testDynCast(A& value) {
+  A& a2 = llvm::dyn_cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'dyn_cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:29: note: source expression 'value' has type 
'A'
+  // CHECK-FIXES: A& a2 = value;
+  (void)a2;
+}
+
+void testCastOrNull(A& value) {
+  A& a3 = llvm::cast_or_null<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast_or_null' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:33: note: source expression 'value' has type 
'A'
+  // CHECK-FIXES: A& a3 = value;
+  (void)a3;
+}
+
+void testCastIfPresent(A& value) {
+  A& a4 = llvm::cast_if_present<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 
'cast_if_present' [llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:36: note: source expression 'value' has type 
'A'
+  // CHECK-FIXES: A& a4 = value;
+  (void)a4;
+}
+
+void testDynCastOrNull(A& value) {
+  A& a5 = llvm::dyn_cast_or_null<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 
'dyn_cast_or_null' [llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:37: note: source expression 'value' has type 
'A'
+  // CHECK-FIXES: A& a5 = value;
+  (void)a5;
+}
+
+void testDynCastIfPresent(A& value) {
+  A& a6 = llvm::dyn_cast_if_present<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 
'dyn_cast_if_present' [llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:40: note: source expression 'value' has type 
'A'
+  // CHECK-FIXES: A& a6 = value;
+  (void)a6;
+}
+
+void testCastNonDeclRef() {
+  A& a7 = llvm::cast<A>((getA()));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression '(getA())' has 
type 'A'
+  // CHECK-FIXES: A& a7 = (getA());
+  (void)a7;
+}
+
+void testUpcast(B& value) {
+  A& a8 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: note: source expression 'value' has type 
'B'
+  // CHECK-MESSAGES: :[[@LINE-3]]:25: note: 'B' is a subtype of 'A'
+  // CHECK-FIXES: A& a8 = value;
+  (void)a8;
+}
+
+void testDowncast(A& value) {
+  B& a9 = llvm::cast<B>(value);
+  // CHECK-MESSAGES-NOT: warning
+  // CHECK-FIXES-NOT: A& a9 = value;
+  (void)a9;
+}
+
+namespace llvm {
+void testCastInLLVM(A& value) {
+  A& a11 = cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:20: note: source expression 'value' has type 
'A'
+  // CHECK-FIXES: A& a11 = value;
+  (void)a11;
+}
+} // namespace llvm
+
+void testCastPointer(A* value) {
+  A *a12 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression 'value' has type 
'A *'
+  // CHECK-FIXES: A *a12 = value;
+  (void)a12;
+}
+
+template <typename T>
+void testCastTemplateIgnore(T* value) {
+  A *a13 = llvm::cast<A>(value);
+  // CHECK-MESSAGES-NOT: warning
+  // CHECK-FIXES-NOT: A *a13 = value;
+  (void)a13;
+}
+template void testCastTemplateIgnore<A>(A *value);
+
+template <typename T>
+struct testCastTemplateIgnoreStruct {
+  void method(T* value) {
+    A *a14 = llvm::cast<A>(value);
+    // CHECK-MESSAGES-NOT: warning
+    // CHECK-FIXES-NOT: A *a14 = value;
+    (void)a14;
+  }
+};
+
+void call(testCastTemplateIgnoreStruct<A> s, A *a) {
+  s.method(a);
+}
+
+template <typename T>
+void testCastTemplateTrigger1(T* value) {
+  T *a15 = llvm::cast<T>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression 'value' has type 
'T *'
+  // CHECK-FIXES: T *a15 = value;
+  (void)a15;
+}
+template void testCastTemplateTrigger1<A>(A *value);
+
+template <typename T>
+void testCastTemplateTrigger2(A* value, T other) {
+  A *a16 = llvm::cast<A>(value);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant use of 'cast' 
[llvm-redundant-casting]
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: note: source expression 'value' has type 
'A *'
+  // CHECK-FIXES: A *a16 = value;
+  (void)a16; (void) other;
+}
+template void testCastTemplateTrigger2<int>(A *value, int other);
+

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to