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
