xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric //  This file defines a checker that checks for padding that could be
10*0b57cec5SDimitry Andric //  removed by re-ordering members.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15*0b57cec5SDimitry Andric #include "clang/AST/CharUnits.h"
16*0b57cec5SDimitry Andric #include "clang/AST/DeclTemplate.h"
17*0b57cec5SDimitry Andric #include "clang/AST/RecordLayout.h"
18*0b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
19*0b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
20*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
23*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
24*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
25*0b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
26*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
27*0b57cec5SDimitry Andric #include <numeric>
28*0b57cec5SDimitry Andric 
29*0b57cec5SDimitry Andric using namespace clang;
30*0b57cec5SDimitry Andric using namespace ento;
31*0b57cec5SDimitry Andric 
32*0b57cec5SDimitry Andric namespace {
33*0b57cec5SDimitry Andric class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
34*0b57cec5SDimitry Andric private:
35*0b57cec5SDimitry Andric   mutable std::unique_ptr<BugType> PaddingBug;
36*0b57cec5SDimitry Andric   mutable BugReporter *BR;
37*0b57cec5SDimitry Andric 
38*0b57cec5SDimitry Andric public:
39*0b57cec5SDimitry Andric   int64_t AllowedPad;
40*0b57cec5SDimitry Andric 
41*0b57cec5SDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42*0b57cec5SDimitry Andric                     BugReporter &BRArg) const {
43*0b57cec5SDimitry Andric     BR = &BRArg;
44*0b57cec5SDimitry Andric 
45*0b57cec5SDimitry Andric     // The calls to checkAST* from AnalysisConsumer don't
46*0b57cec5SDimitry Andric     // visit template instantiations or lambda classes. We
47*0b57cec5SDimitry Andric     // want to visit those, so we make our own RecursiveASTVisitor.
48*0b57cec5SDimitry Andric     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
49*0b57cec5SDimitry Andric       const PaddingChecker *Checker;
50*0b57cec5SDimitry Andric       bool shouldVisitTemplateInstantiations() const { return true; }
51*0b57cec5SDimitry Andric       bool shouldVisitImplicitCode() const { return true; }
52*0b57cec5SDimitry Andric       explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {}
53*0b57cec5SDimitry Andric       bool VisitRecordDecl(const RecordDecl *RD) {
54*0b57cec5SDimitry Andric         Checker->visitRecord(RD);
55*0b57cec5SDimitry Andric         return true;
56*0b57cec5SDimitry Andric       }
57*0b57cec5SDimitry Andric       bool VisitVarDecl(const VarDecl *VD) {
58*0b57cec5SDimitry Andric         Checker->visitVariable(VD);
59*0b57cec5SDimitry Andric         return true;
60*0b57cec5SDimitry Andric       }
61*0b57cec5SDimitry Andric       // TODO: Visit array new and mallocs for arrays.
62*0b57cec5SDimitry Andric     };
63*0b57cec5SDimitry Andric 
64*0b57cec5SDimitry Andric     LocalVisitor visitor(this);
65*0b57cec5SDimitry Andric     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
66*0b57cec5SDimitry Andric   }
67*0b57cec5SDimitry Andric 
68*0b57cec5SDimitry Andric   /// Look for records of overly padded types. If padding *
69*0b57cec5SDimitry Andric   /// PadMultiplier exceeds AllowedPad, then generate a report.
70*0b57cec5SDimitry Andric   /// PadMultiplier is used to share code with the array padding
71*0b57cec5SDimitry Andric   /// checker.
72*0b57cec5SDimitry Andric   void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const {
73*0b57cec5SDimitry Andric     if (shouldSkipDecl(RD))
74*0b57cec5SDimitry Andric       return;
75*0b57cec5SDimitry Andric 
76*0b57cec5SDimitry Andric     // TODO: Figure out why we are going through declarations and not only
77*0b57cec5SDimitry Andric     // definitions.
78*0b57cec5SDimitry Andric     if (!(RD = RD->getDefinition()))
79*0b57cec5SDimitry Andric       return;
80*0b57cec5SDimitry Andric 
81*0b57cec5SDimitry Andric     // This is the simplest correct case: a class with no fields and one base
82*0b57cec5SDimitry Andric     // class. Other cases are more complicated because of how the base classes
83*0b57cec5SDimitry Andric     // & fields might interact, so we don't bother dealing with them.
84*0b57cec5SDimitry Andric     // TODO: Support other combinations of base classes and fields.
85*0b57cec5SDimitry Andric     if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
86*0b57cec5SDimitry Andric       if (CXXRD->field_empty() && CXXRD->getNumBases() == 1)
87*0b57cec5SDimitry Andric         return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(),
88*0b57cec5SDimitry Andric                            PadMultiplier);
89*0b57cec5SDimitry Andric 
90*0b57cec5SDimitry Andric     auto &ASTContext = RD->getASTContext();
91*0b57cec5SDimitry Andric     const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD);
92*0b57cec5SDimitry Andric     assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity()));
93*0b57cec5SDimitry Andric 
94*0b57cec5SDimitry Andric     CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL);
95*0b57cec5SDimitry Andric     if (BaselinePad.isZero())
96*0b57cec5SDimitry Andric       return;
97*0b57cec5SDimitry Andric 
98*0b57cec5SDimitry Andric     CharUnits OptimalPad;
99*0b57cec5SDimitry Andric     SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
100*0b57cec5SDimitry Andric     std::tie(OptimalPad, OptimalFieldsOrder) =
101*0b57cec5SDimitry Andric         calculateOptimalPad(RD, ASTContext, RL);
102*0b57cec5SDimitry Andric 
103*0b57cec5SDimitry Andric     CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
104*0b57cec5SDimitry Andric     if (DiffPad.getQuantity() <= AllowedPad) {
105*0b57cec5SDimitry Andric       assert(!DiffPad.isNegative() && "DiffPad should not be negative");
106*0b57cec5SDimitry Andric       // There is not enough excess padding to trigger a warning.
107*0b57cec5SDimitry Andric       return;
108*0b57cec5SDimitry Andric     }
109*0b57cec5SDimitry Andric     reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
110*0b57cec5SDimitry Andric   }
111*0b57cec5SDimitry Andric 
112*0b57cec5SDimitry Andric   /// Look for arrays of overly padded types. If the padding of the
113*0b57cec5SDimitry Andric   /// array type exceeds AllowedPad, then generate a report.
114*0b57cec5SDimitry Andric   void visitVariable(const VarDecl *VD) const {
115*0b57cec5SDimitry Andric     const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe();
116*0b57cec5SDimitry Andric     if (ArrTy == nullptr)
117*0b57cec5SDimitry Andric       return;
118*0b57cec5SDimitry Andric     uint64_t Elts = 0;
119*0b57cec5SDimitry Andric     if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy))
120*0b57cec5SDimitry Andric       Elts = CArrTy->getSize().getZExtValue();
121*0b57cec5SDimitry Andric     if (Elts == 0)
122*0b57cec5SDimitry Andric       return;
123*0b57cec5SDimitry Andric     const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>();
124*0b57cec5SDimitry Andric     if (RT == nullptr)
125*0b57cec5SDimitry Andric       return;
126*0b57cec5SDimitry Andric 
127*0b57cec5SDimitry Andric     // TODO: Recurse into the fields to see if they have excess padding.
128*0b57cec5SDimitry Andric     visitRecord(RT->getDecl(), Elts);
129*0b57cec5SDimitry Andric   }
130*0b57cec5SDimitry Andric 
131*0b57cec5SDimitry Andric   bool shouldSkipDecl(const RecordDecl *RD) const {
132*0b57cec5SDimitry Andric     // TODO: Figure out why we are going through declarations and not only
133*0b57cec5SDimitry Andric     // definitions.
134*0b57cec5SDimitry Andric     if (!(RD = RD->getDefinition()))
135*0b57cec5SDimitry Andric       return true;
136*0b57cec5SDimitry Andric     auto Location = RD->getLocation();
137*0b57cec5SDimitry Andric     // If the construct doesn't have a source file, then it's not something
138*0b57cec5SDimitry Andric     // we want to diagnose.
139*0b57cec5SDimitry Andric     if (!Location.isValid())
140*0b57cec5SDimitry Andric       return true;
141*0b57cec5SDimitry Andric     SrcMgr::CharacteristicKind Kind =
142*0b57cec5SDimitry Andric         BR->getSourceManager().getFileCharacteristic(Location);
143*0b57cec5SDimitry Andric     // Throw out all records that come from system headers.
144*0b57cec5SDimitry Andric     if (Kind != SrcMgr::C_User)
145*0b57cec5SDimitry Andric       return true;
146*0b57cec5SDimitry Andric 
147*0b57cec5SDimitry Andric     // Not going to attempt to optimize unions.
148*0b57cec5SDimitry Andric     if (RD->isUnion())
149*0b57cec5SDimitry Andric       return true;
150*0b57cec5SDimitry Andric     if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
151*0b57cec5SDimitry Andric       // Tail padding with base classes ends up being very complicated.
152*0b57cec5SDimitry Andric       // We will skip objects with base classes for now, unless they do not
153*0b57cec5SDimitry Andric       // have fields.
154*0b57cec5SDimitry Andric       // TODO: Handle more base class scenarios.
155*0b57cec5SDimitry Andric       if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0)
156*0b57cec5SDimitry Andric         return true;
157*0b57cec5SDimitry Andric       if (CXXRD->field_empty() && CXXRD->getNumBases() != 1)
158*0b57cec5SDimitry Andric         return true;
159*0b57cec5SDimitry Andric       // Virtual bases are complicated, skipping those for now.
160*0b57cec5SDimitry Andric       if (CXXRD->getNumVBases() != 0)
161*0b57cec5SDimitry Andric         return true;
162*0b57cec5SDimitry Andric       // Can't layout a template, so skip it. We do still layout the
163*0b57cec5SDimitry Andric       // instantiations though.
164*0b57cec5SDimitry Andric       if (CXXRD->getTypeForDecl()->isDependentType())
165*0b57cec5SDimitry Andric         return true;
166*0b57cec5SDimitry Andric       if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
167*0b57cec5SDimitry Andric         return true;
168*0b57cec5SDimitry Andric     }
169*0b57cec5SDimitry Andric     // How do you reorder fields if you haven't got any?
170*0b57cec5SDimitry Andric     else if (RD->field_empty())
171*0b57cec5SDimitry Andric       return true;
172*0b57cec5SDimitry Andric 
173*0b57cec5SDimitry Andric     auto IsTrickyField = [](const FieldDecl *FD) -> bool {
174*0b57cec5SDimitry Andric       // Bitfield layout is hard.
175*0b57cec5SDimitry Andric       if (FD->isBitField())
176*0b57cec5SDimitry Andric         return true;
177*0b57cec5SDimitry Andric 
178*0b57cec5SDimitry Andric       // Variable length arrays are tricky too.
179*0b57cec5SDimitry Andric       QualType Ty = FD->getType();
180*0b57cec5SDimitry Andric       if (Ty->isIncompleteArrayType())
181*0b57cec5SDimitry Andric         return true;
182*0b57cec5SDimitry Andric       return false;
183*0b57cec5SDimitry Andric     };
184*0b57cec5SDimitry Andric 
185*0b57cec5SDimitry Andric     if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField))
186*0b57cec5SDimitry Andric       return true;
187*0b57cec5SDimitry Andric     return false;
188*0b57cec5SDimitry Andric   }
189*0b57cec5SDimitry Andric 
190*0b57cec5SDimitry Andric   static CharUnits calculateBaselinePad(const RecordDecl *RD,
191*0b57cec5SDimitry Andric                                         const ASTContext &ASTContext,
192*0b57cec5SDimitry Andric                                         const ASTRecordLayout &RL) {
193*0b57cec5SDimitry Andric     CharUnits PaddingSum;
194*0b57cec5SDimitry Andric     CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
195*0b57cec5SDimitry Andric     for (const FieldDecl *FD : RD->fields()) {
196*0b57cec5SDimitry Andric       // This checker only cares about the padded size of the
197*0b57cec5SDimitry Andric       // field, and not the data size. If the field is a record
198*0b57cec5SDimitry Andric       // with tail padding, then we won't put that number in our
199*0b57cec5SDimitry Andric       // total because reordering fields won't fix that problem.
200*0b57cec5SDimitry Andric       CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType());
201*0b57cec5SDimitry Andric       auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex());
202*0b57cec5SDimitry Andric       CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits);
203*0b57cec5SDimitry Andric       PaddingSum += (FieldOffset - Offset);
204*0b57cec5SDimitry Andric       Offset = FieldOffset + FieldSize;
205*0b57cec5SDimitry Andric     }
206*0b57cec5SDimitry Andric     PaddingSum += RL.getSize() - Offset;
207*0b57cec5SDimitry Andric     return PaddingSum;
208*0b57cec5SDimitry Andric   }
209*0b57cec5SDimitry Andric 
210*0b57cec5SDimitry Andric   /// Optimal padding overview:
211*0b57cec5SDimitry Andric   /// 1.  Find a close approximation to where we can place our first field.
212*0b57cec5SDimitry Andric   ///     This will usually be at offset 0.
213*0b57cec5SDimitry Andric   /// 2.  Try to find the best field that can legally be placed at the current
214*0b57cec5SDimitry Andric   ///     offset.
215*0b57cec5SDimitry Andric   ///   a.  "Best" is the largest alignment that is legal, but smallest size.
216*0b57cec5SDimitry Andric   ///       This is to account for overly aligned types.
217*0b57cec5SDimitry Andric   /// 3.  If no fields can fit, pad by rounding the current offset up to the
218*0b57cec5SDimitry Andric   ///     smallest alignment requirement of our fields. Measure and track the
219*0b57cec5SDimitry Andric   //      amount of padding added. Go back to 2.
220*0b57cec5SDimitry Andric   /// 4.  Increment the current offset by the size of the chosen field.
221*0b57cec5SDimitry Andric   /// 5.  Remove the chosen field from the set of future possibilities.
222*0b57cec5SDimitry Andric   /// 6.  Go back to 2 if there are still unplaced fields.
223*0b57cec5SDimitry Andric   /// 7.  Add tail padding by rounding the current offset up to the structure
224*0b57cec5SDimitry Andric   ///     alignment. Track the amount of padding added.
225*0b57cec5SDimitry Andric 
226*0b57cec5SDimitry Andric   static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
227*0b57cec5SDimitry Andric   calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext,
228*0b57cec5SDimitry Andric                       const ASTRecordLayout &RL) {
229*0b57cec5SDimitry Andric     struct FieldInfo {
230*0b57cec5SDimitry Andric       CharUnits Align;
231*0b57cec5SDimitry Andric       CharUnits Size;
232*0b57cec5SDimitry Andric       const FieldDecl *Field;
233*0b57cec5SDimitry Andric       bool operator<(const FieldInfo &RHS) const {
234*0b57cec5SDimitry Andric         // Order from small alignments to large alignments,
235*0b57cec5SDimitry Andric         // then large sizes to small sizes.
236*0b57cec5SDimitry Andric         // then large field indices to small field indices
237*0b57cec5SDimitry Andric         return std::make_tuple(Align, -Size,
238*0b57cec5SDimitry Andric                                Field ? -static_cast<int>(Field->getFieldIndex())
239*0b57cec5SDimitry Andric                                      : 0) <
240*0b57cec5SDimitry Andric                std::make_tuple(
241*0b57cec5SDimitry Andric                    RHS.Align, -RHS.Size,
242*0b57cec5SDimitry Andric                    RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex())
243*0b57cec5SDimitry Andric                              : 0);
244*0b57cec5SDimitry Andric       }
245*0b57cec5SDimitry Andric     };
246*0b57cec5SDimitry Andric     SmallVector<FieldInfo, 20> Fields;
247*0b57cec5SDimitry Andric     auto GatherSizesAndAlignments = [](const FieldDecl *FD) {
248*0b57cec5SDimitry Andric       FieldInfo RetVal;
249*0b57cec5SDimitry Andric       RetVal.Field = FD;
250*0b57cec5SDimitry Andric       auto &Ctx = FD->getASTContext();
251*0b57cec5SDimitry Andric       std::tie(RetVal.Size, RetVal.Align) =
252*0b57cec5SDimitry Andric           Ctx.getTypeInfoInChars(FD->getType());
253*0b57cec5SDimitry Andric       assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
254*0b57cec5SDimitry Andric       if (auto Max = FD->getMaxAlignment())
255*0b57cec5SDimitry Andric         RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);
256*0b57cec5SDimitry Andric       return RetVal;
257*0b57cec5SDimitry Andric     };
258*0b57cec5SDimitry Andric     std::transform(RD->field_begin(), RD->field_end(),
259*0b57cec5SDimitry Andric                    std::back_inserter(Fields), GatherSizesAndAlignments);
260*0b57cec5SDimitry Andric     llvm::sort(Fields);
261*0b57cec5SDimitry Andric     // This lets us skip over vptrs and non-virtual bases,
262*0b57cec5SDimitry Andric     // so that we can just worry about the fields in our object.
263*0b57cec5SDimitry Andric     // Note that this does cause us to miss some cases where we
264*0b57cec5SDimitry Andric     // could pack more bytes in to a base class's tail padding.
265*0b57cec5SDimitry Andric     CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
266*0b57cec5SDimitry Andric     CharUnits NewPad;
267*0b57cec5SDimitry Andric     SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
268*0b57cec5SDimitry Andric     while (!Fields.empty()) {
269*0b57cec5SDimitry Andric       unsigned TrailingZeros =
270*0b57cec5SDimitry Andric           llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity());
271*0b57cec5SDimitry Andric       // If NewOffset is zero, then countTrailingZeros will be 64. Shifting
272*0b57cec5SDimitry Andric       // 64 will overflow our unsigned long long. Shifting 63 will turn
273*0b57cec5SDimitry Andric       // our long long (and CharUnits internal type) negative. So shift 62.
274*0b57cec5SDimitry Andric       long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
275*0b57cec5SDimitry Andric       CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits);
276*0b57cec5SDimitry Andric       FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr};
277*0b57cec5SDimitry Andric 
278*0b57cec5SDimitry Andric       // In the typical case, this will find the last element
279*0b57cec5SDimitry Andric       // of the vector. We won't find a middle element unless
280*0b57cec5SDimitry Andric       // we started on a poorly aligned address or have an overly
281*0b57cec5SDimitry Andric       // aligned field.
282*0b57cec5SDimitry Andric       auto Iter = llvm::upper_bound(Fields, InsertPoint);
283*0b57cec5SDimitry Andric       if (Iter != Fields.begin()) {
284*0b57cec5SDimitry Andric         // We found a field that we can layout with the current alignment.
285*0b57cec5SDimitry Andric         --Iter;
286*0b57cec5SDimitry Andric         NewOffset += Iter->Size;
287*0b57cec5SDimitry Andric         OptimalFieldsOrder.push_back(Iter->Field);
288*0b57cec5SDimitry Andric         Fields.erase(Iter);
289*0b57cec5SDimitry Andric       } else {
290*0b57cec5SDimitry Andric         // We are poorly aligned, and we need to pad in order to layout another
291*0b57cec5SDimitry Andric         // field. Round up to at least the smallest field alignment that we
292*0b57cec5SDimitry Andric         // currently have.
293*0b57cec5SDimitry Andric         CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align);
294*0b57cec5SDimitry Andric         NewPad += NextOffset - NewOffset;
295*0b57cec5SDimitry Andric         NewOffset = NextOffset;
296*0b57cec5SDimitry Andric       }
297*0b57cec5SDimitry Andric     }
298*0b57cec5SDimitry Andric     // Calculate tail padding.
299*0b57cec5SDimitry Andric     CharUnits NewSize = NewOffset.alignTo(RL.getAlignment());
300*0b57cec5SDimitry Andric     NewPad += NewSize - NewOffset;
301*0b57cec5SDimitry Andric     return {NewPad, std::move(OptimalFieldsOrder)};
302*0b57cec5SDimitry Andric   }
303*0b57cec5SDimitry Andric 
304*0b57cec5SDimitry Andric   void reportRecord(
305*0b57cec5SDimitry Andric       const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad,
306*0b57cec5SDimitry Andric       const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {
307*0b57cec5SDimitry Andric     if (!PaddingBug)
308*0b57cec5SDimitry Andric       PaddingBug =
309*0b57cec5SDimitry Andric           llvm::make_unique<BugType>(this, "Excessive Padding", "Performance");
310*0b57cec5SDimitry Andric 
311*0b57cec5SDimitry Andric     SmallString<100> Buf;
312*0b57cec5SDimitry Andric     llvm::raw_svector_ostream Os(Buf);
313*0b57cec5SDimitry Andric     Os << "Excessive padding in '";
314*0b57cec5SDimitry Andric     Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(),
315*0b57cec5SDimitry Andric                                 LangOptions())
316*0b57cec5SDimitry Andric        << "'";
317*0b57cec5SDimitry Andric 
318*0b57cec5SDimitry Andric     if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
319*0b57cec5SDimitry Andric       // TODO: make this show up better in the console output and in
320*0b57cec5SDimitry Andric       // the HTML. Maybe just make it show up in HTML like the path
321*0b57cec5SDimitry Andric       // diagnostics show.
322*0b57cec5SDimitry Andric       SourceLocation ILoc = TSD->getPointOfInstantiation();
323*0b57cec5SDimitry Andric       if (ILoc.isValid())
324*0b57cec5SDimitry Andric         Os << " instantiated here: "
325*0b57cec5SDimitry Andric            << ILoc.printToString(BR->getSourceManager());
326*0b57cec5SDimitry Andric     }
327*0b57cec5SDimitry Andric 
328*0b57cec5SDimitry Andric     Os << " (" << BaselinePad.getQuantity() << " padding bytes, where "
329*0b57cec5SDimitry Andric        << OptimalPad.getQuantity() << " is optimal). \n"
330*0b57cec5SDimitry Andric        << "Optimal fields order: \n";
331*0b57cec5SDimitry Andric     for (const auto *FD : OptimalFieldsOrder)
332*0b57cec5SDimitry Andric       Os << FD->getName() << ", \n";
333*0b57cec5SDimitry Andric     Os << "consider reordering the fields or adding explicit padding "
334*0b57cec5SDimitry Andric           "members.";
335*0b57cec5SDimitry Andric 
336*0b57cec5SDimitry Andric     PathDiagnosticLocation CELoc =
337*0b57cec5SDimitry Andric         PathDiagnosticLocation::create(RD, BR->getSourceManager());
338*0b57cec5SDimitry Andric     auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc);
339*0b57cec5SDimitry Andric     Report->setDeclWithIssue(RD);
340*0b57cec5SDimitry Andric     Report->addRange(RD->getSourceRange());
341*0b57cec5SDimitry Andric     BR->emitReport(std::move(Report));
342*0b57cec5SDimitry Andric   }
343*0b57cec5SDimitry Andric };
344*0b57cec5SDimitry Andric } // namespace
345*0b57cec5SDimitry Andric 
346*0b57cec5SDimitry Andric void ento::registerPaddingChecker(CheckerManager &Mgr) {
347*0b57cec5SDimitry Andric   auto *Checker = Mgr.registerChecker<PaddingChecker>();
348*0b57cec5SDimitry Andric   Checker->AllowedPad = Mgr.getAnalyzerOptions()
349*0b57cec5SDimitry Andric           .getCheckerIntegerOption(Checker, "AllowedPad");
350*0b57cec5SDimitry Andric   if (Checker->AllowedPad < 0)
351*0b57cec5SDimitry Andric     Mgr.reportInvalidCheckerOptionValue(
352*0b57cec5SDimitry Andric         Checker, "AllowedPad", "a non-negative value");
353*0b57cec5SDimitry Andric }
354*0b57cec5SDimitry Andric 
355*0b57cec5SDimitry Andric bool ento::shouldRegisterPaddingChecker(const LangOptions &LO) {
356*0b57cec5SDimitry Andric   return true;
357*0b57cec5SDimitry Andric }
358