1//===- CppBoundedBuffers.cpp ----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/ScalableStaticAnalysis/SourceTransformation/Transformations/CppBoundedBuffers.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/DeclBase.h"
13#include "clang/AST/DeclCXX.h"
14#include "clang/AST/DynamicRecursiveASTVisitor.h"
15#include "clang/AST/Type.h"
16#include "clang/AST/TypeLoc.h"
17#include "clang/Basic/LangOptions.h"
18#include "clang/Basic/SourceLocation.h"
19#include "clang/Basic/SourceManager.h"
20#include "clang/Frontend/SSAFOptions.h"
21#include "clang/Lex/Lexer.h"
22#include "clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h"
23#include "clang/ScalableStaticAnalysis/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h"
24#include "clang/ScalableStaticAnalysis/Core/ASTEntityMapping.h"
25#include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h"
26#include "clang/ScalableStaticAnalysis/Core/Model/EntityIdTable.h"
27#include "clang/ScalableStaticAnalysis/Core/Model/EntityName.h"
28#include "clang/ScalableStaticAnalysis/SourceTransformation/TransformationRegistry.h"
29#include "clang/Tooling/Core/Replacement.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/SmallVector.h"
32#include <cassert>
33#include <map>
34#include <optional>
35#include <string>
36
37using namespace clang;
38using namespace clang::ssaf;
39
40static constexpr llvm::StringLiteral SkippedRuleId =
41 "cpp-bounded-buffers-skipped";
42
43namespace {
44
45/// A declarator whose type can carry pointer levels.
46bool isCandidateType(QualType T) {
47 QualType U = T.getNonReferenceType();
48 return U->isPointerType() || U->isArrayType();
49}
50
51std::string spell(QualType T, const ASTContext &Ctx) {
52 return T.getAsString(Policy: Ctx.getPrintingPolicy());
53}
54
55/// Whether \p T is a type with a name that can be used in template arguments.
56bool isNamable(QualType T) {
57 if (!T->isTypedefNameType())
58 if (const auto *RT = T->getAs<RecordType>()) {
59 const RecordDecl *RD = RT->getDecl();
60 return RD->getIdentifier() || RD->getTypedefNameForAnonDecl();
61 }
62 return true;
63}
64
65std::string renderNewType(const ClassifyResult &R, QualType T,
66 const ASTContext &Ctx) {
67 assert(!R.Skip);
68 if (R.NewType == BoundedType::Ptr)
69 return "bounded_ptr<" + R.InnerSpelling + "> ";
70 const auto *CAT = Ctx.getAsConstantArrayType(T);
71 std::string N = std::to_string(val: CAT->getSize().getZExtValue());
72 return "bounded_array<" + R.InnerSpelling + ", " + N + ">";
73}
74
75/// Whether another declarator in \p D's lexical context shares its type
76/// specifier, i.e. \p D is one declarator of a multi-declarator group.
77bool sharesTypeSpecifier(const DeclaratorDecl *D) {
78 const TypeSourceInfo *TSI = D->getTypeSourceInfo();
79 const DeclContext *DC = D->getLexicalDeclContext();
80 if (!TSI || !DC)
81 return false;
82 SourceLocation Begin = TSI->getTypeLoc().getBeginLoc();
83 for (const Decl *Sibling : DC->decls()) {
84 if (Sibling == D)
85 continue;
86 const auto *Other = dyn_cast<DeclaratorDecl>(Val: Sibling);
87 if (Other && Other->getTypeSourceInfo() &&
88 Other->getTypeSourceInfo()->getTypeLoc().getBeginLoc() == Begin)
89 return true;
90 }
91 return false;
92}
93
94bool hasTrailingReturnType(const FunctionDecl *FD) {
95 const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
96 return FPT && FPT->hasTrailingReturn();
97}
98
99CharSourceRange declTypeRange(const DeclaratorDecl *D) {
100 if (const TypeSourceInfo *TSI = D->getTypeSourceInfo())
101 return CharSourceRange::getTokenRange(R: TSI->getTypeLoc().getSourceRange());
102 return CharSourceRange::getTokenRange(R: D->getSourceRange());
103}
104
105/// \return the pointee or element types TypeLoc if TL is a (qualified) pointer
106/// or array type.
107TypeLoc getInnerTypeLoc(TypeLoc TL) {
108 TL = TL.getUnqualifiedLoc();
109 if (auto PTL = TL.getAs<PointerTypeLoc>())
110 return PTL.getPointeeLoc();
111 if (auto ATL = TL.getAs<ArrayTypeLoc>())
112 return ATL.getElementLoc();
113 return {};
114}
115
116/// Whether \p T spells a cv-qualifier keyword.
117bool isCVQualifier(const Token &T) {
118 return T.is(K: tok::raw_identifier) && (T.getRawIdentifier() == "const" ||
119 T.getRawIdentifier() == "volatile");
120}
121
122/// Probe leading qualifiers for a type 'T'. The probe is bounded in the range
123/// [ \p DeclBegin, \p TypeBegin ), where the lower bound is the begin location
124/// of the declaration where 'T' is spelled and the upper bound is the begin of
125/// the spell of 'T'.
126///
127/// The function updates \p TypeBegin if it finds cv-qualifiers preceding the
128/// original \p TypeBegin without any other token intervening in between. \p
129/// TypeBegin is not updated if there is no leading cv-qualifier. Otherwise,
130/// returns the probe failed reason.
131///
132/// \p TypeBegin is always token location.
133std::optional<ReportReason> extendLeadingQualifiers(SourceLocation DeclBegin,
134 SourceLocation &TypeBegin,
135 const ASTContext &Ctx) {
136 const SourceManager &SM = Ctx.getSourceManager();
137 const LangOptions &LangOpts = Ctx.getLangOpts();
138
139 std::optional<SourceLocation> FirstCVBegin;
140 std::optional<Token> Tok = Token();
141
142 if (Lexer::getRawToken(Loc: DeclBegin, Result&: *Tok, SM, LangOpts,
143 /*IgnoreWhiteSpace=*/true))
144 return ReportReason::EmissionFailed;
145 while (SM.isBeforeInTranslationUnit(LHS: Tok->getLocation(), RHS: TypeBegin)) {
146 if (isCVQualifier(T: *Tok)) {
147 if (!FirstCVBegin) {
148 // Found first cv-qualifier, set `FirstCVBegin`.
149 FirstCVBegin = Tok->getLocation();
150 }
151 } else if (FirstCVBegin)
152 // Bail when there is unexpected token between cv-qualifiers and the
153 // original TypeBegin:
154 return ReportReason::UnexpectedLeadingQualifier;
155 Tok = Lexer::findNextToken(Loc: Tok->getEndLoc(), SM, LangOpts,
156 /*IncludeComments=*/true);
157 if (!Tok)
158 return ReportReason::EmissionFailed;
159 }
160 if (FirstCVBegin)
161 TypeBegin = *FirstCVBegin; // set the real TypeBegin after propagation
162 return std::nullopt;
163}
164
165/// Probe trailing qualifiers for a type 'T'. The probe is bounded in the range
166/// ( \p TypeEnd, \p UpperBound ), where the lower bound is the end location
167/// of 'T' and the upper bound should be a location within the declaration where
168/// 'T' is spelled.
169///
170/// The function updates \p TypeEnd if it finds cv-qualifiers following the
171/// original \p TypeEnd without any other token intervening in between.
172/// \p TypeEnd is not updated if there is no following cv-qualifier. Otherwise,
173/// returns the probe failed reason.
174///
175/// \p TypeBegin is always token location.
176std::optional<ReportReason> extendTrailingQualifiers(SourceLocation &TypeEnd,
177 SourceLocation UpperBound,
178 const ASTContext &Ctx) {
179 const SourceManager &SM = Ctx.getSourceManager();
180 const LangOptions &LangOpts = Ctx.getLangOpts();
181
182 std::optional<SourceLocation> LastCVBegin;
183 bool RunEnded = false;
184
185 std::optional<Token> Tok = Lexer::findNextToken(Loc: TypeEnd, SM, LangOpts,
186 /*IncludeComments=*/true);
187 if (!Tok)
188 return ReportReason::EmissionFailed;
189 while (SM.isBeforeInTranslationUnit(LHS: Tok->getLocation(), RHS: UpperBound)) {
190 if (isCVQualifier(T: *Tok)) {
191 // Bail if there is anything unexpected between TypeEnd and a
192 // cv-qualifier.
193 if (RunEnded)
194 return ReportReason::UnexpectedTrailingQualifier;
195 LastCVBegin = Tok->getLocation();
196 } else
197 RunEnded = true;
198 Tok = Lexer::findNextToken(Loc: Tok->getEndLoc(), SM, LangOpts,
199 /*IncludeComments=*/true);
200 if (!Tok)
201 return ReportReason::EmissionFailed;
202 }
203 if (LastCVBegin)
204 TypeEnd = *LastCVBegin; // set the real TypeEnd after propagation
205 return std::nullopt;
206}
207
208using Levels = llvm::SmallSet<unsigned, 4>;
209using DeclLevels = std::map<const Decl *, Levels>;
210using ReturnLevels = std::map<const FunctionDecl *, Levels>;
211
212/// Reverse index from the whole-program reachability result onto entity names,
213/// so a declaration in this TU can look up its reachable pointer levels.
214class ReachabilityMap {
215 const EntityPointerLevelSet &Reachables;
216 std::map<EntityName, EntityId> NameToId;
217
218public:
219 ReachabilityMap(const WPASuite &Suite,
220 const EntityPointerLevelSet &Reachables)
221 : Reachables(Reachables) {
222 Suite.getIdTable().forEach(Callback: [this](const EntityName &Name, EntityId Id) {
223 NameToId.emplace(args: Name, args&: Id);
224 });
225 }
226
227 llvm::SmallSet<unsigned, 4> levelsFor(std::optional<EntityName> Name) const {
228 llvm::SmallSet<unsigned, 4> Levels;
229 if (!Name)
230 return Levels;
231 auto NameIt = NameToId.find(x: *Name);
232 if (NameIt == NameToId.end())
233 return Levels;
234 auto [Begin, End] = Reachables.equal_range(x: NameIt->second);
235 for (const EntityPointerLevel &EPL : llvm::make_range(x: Begin, y: End))
236 Levels.insert(V: EPL.getPointerLevel());
237 return Levels;
238 }
239};
240
241/// Collects the reachable pointer/array declarators and function returns
242/// declared in this TU.
243class CollectVisitor : public DynamicRecursiveASTVisitor {
244public:
245 CollectVisitor(const ReachabilityMap &Reach,
246 const NestedBuildNamespace &TUNamespace,
247 const NestedBuildNamespace &LUNamespace, DeclLevels &Decls,
248 ReturnLevels &Returns)
249 : Reach(Reach), TUNamespace(TUNamespace), LUNamespace(LUNamespace),
250 Decls(Decls), Returns(Returns) {}
251
252 bool VisitVarDecl(VarDecl *D) override {
253 collect(D, T: D->getType(),
254 Name: getQualifiedEntityName(D, TUNamespace, LUNamespace));
255 return true;
256 }
257
258 bool VisitFieldDecl(FieldDecl *D) override {
259 collect(D, T: D->getType(),
260 Name: getQualifiedEntityName(D, TUNamespace, LUNamespace));
261 return true;
262 }
263
264 bool VisitFunctionDecl(FunctionDecl *FD) override {
265 if (!FD->isTemplated() && isCandidateType(T: FD->getReturnType())) {
266 llvm::SmallSet<unsigned, 4> Levels = Reach.levelsFor(
267 Name: getQualifiedEntityNameForReturn(FD, TUNamespace, LUNamespace));
268 if (!Levels.empty())
269 Returns[FD] = std::move(Levels);
270 }
271 return true;
272 }
273
274private:
275 void collect(const Decl *D, QualType T, std::optional<EntityName> Name) {
276 if (D->isTemplated() || !isCandidateType(T))
277 return;
278 llvm::SmallSet<unsigned, 4> Levels = Reach.levelsFor(Name);
279 if (!Levels.empty())
280 Decls[D] = std::move(Levels);
281 }
282
283 const ReachabilityMap &Reach;
284 NestedBuildNamespace TUNamespace;
285 NestedBuildNamespace LUNamespace;
286 DeclLevels &Decls;
287 ReturnLevels &Returns;
288};
289
290/// Rewrites or reports every collected declarator and function return.
291class RewriteVisitor : public DynamicRecursiveASTVisitor {
292public:
293 RewriteVisitor(ASTContext &Ctx, DeclLevels &Decls, ReturnLevels &Returns,
294 SourceEditEmitter &Edits, TransformationReportEmitter &Report)
295 : Ctx(Ctx), Decls(Decls), Returns(Returns), Edits(Edits), Report(Report) {
296 }
297
298 bool VisitVarDecl(VarDecl *D) override {
299 processDecl(D, T: D->getType());
300 return true;
301 }
302
303 bool VisitFieldDecl(FieldDecl *D) override {
304 processDecl(D, T: D->getType());
305 return true;
306 }
307
308 bool VisitFunctionDecl(FunctionDecl *FD) override {
309 auto It = Returns.find(x: FD);
310 if (It == Returns.end())
311 return true;
312 const Levels &ReachableLevels = It->second;
313 if (hasTrailingReturnType(FD))
314 return report(D: FD, Reason: ReportReason::TrailingReturnType);
315
316 SourceLocation NameLoc = FD->getLocation();
317
318 ClassifyResult R =
319 classifyDeclType(T: FD->getReturnType(), ReachableLevels, Ctx);
320 if (R.Skip)
321 return report(D: FD, Reason: *R.Skip);
322
323 FunctionTypeLoc FunTypeLoc = FD->getFunctionTypeLoc();
324
325 if (!FunTypeLoc)
326 return report(D: FD, Reason: ReportReason::EmissionFailed);
327 return report(D: FD, Reason: emit(DeclBegin: FD->getBeginLoc(), NameLoc,
328 TLoc: FunTypeLoc.getReturnLoc(), T: FD->getReturnType(), R));
329 }
330
331private:
332 void processDecl(DeclaratorDecl *D, QualType T) {
333 auto It = Decls.find(x: D);
334 if (It == Decls.end())
335 return;
336 const Levels &ReachableLevels = It->second;
337 if (sharesTypeSpecifier(D))
338 return (void)report(D, Reason: ReportReason::DeclarationGroup);
339
340 const TypeSourceInfo *TSI = D->getTypeSourceInfo();
341
342 if (!TSI)
343 return (void)report(D, Reason: ReportReason::EmissionFailed);
344
345 SourceLocation NameLoc = D->getLocation();
346 ClassifyResult R = classifyDeclType(T, ReachableLevels, Ctx);
347
348 if (R.Skip)
349 return (void)report(D, Reason: *R.Skip);
350 report(D, Reason: emit(DeclBegin: D->getBeginLoc(), NameLoc, TLoc: TSI->getTypeLoc(), T, R));
351 }
352
353 /// Compute the precise source range for rewriting. The produced range is
354 /// token range.
355 ///
356 /// For pointer types, the rewrite range is from the leading cv-qualifier of
357 /// the pointee type to the '*' token of the pointer type.
358 ///
359 /// For array types, the rewrite range is from the leading cv-qualifier to the
360 /// trailing cv-qualifier around the element type. It stops short of the
361 /// declarator name, leaving the name and the extent that follows it to be
362 /// handled separately.
363 ///
364 /// \param DeclBegin the begin location of the declaration, the lower bound of
365 /// the source range before narrowing down to the precise one.
366 /// \param NameLoc the location of the name of the declaration, the upper
367 /// bound of the source range before narrowing down to the precise one.
368 /// \param TLoc the TypeLoc of the type of the declaration
369 /// \param BoundedType indicates whether it is a pointer or an array
370 /// \return ReportReason if it cannot narrow down the rewrite range to the
371 /// aforementioned range. std::nullopt and updated \p Result otherwise.
372 std::optional<ReportReason>
373 computeRewriteRange(SourceLocation DeclBegin, SourceLocation NameLoc,
374 TypeLoc TLoc, BoundedType BoundedType,
375 const ASTContext &Ctx, SourceRange &RewriteRange) {
376 TypeLoc InnerTypeLoc = getInnerTypeLoc(TL: TLoc);
377
378 if (!InnerTypeLoc)
379 return ReportReason::NoInnerTypeLoc;
380
381 SourceLocation RewriteRangeBegin = InnerTypeLoc.getBeginLoc();
382 SourceRange Result;
383
384 if (BoundedType == BoundedType::Ptr) {
385 auto PTL = TLoc.getUnqualifiedLoc().getAs<PointerTypeLoc>();
386
387 if (!PTL || TLoc.getEndLoc() != PTL.getStarLoc())
388 return ReportReason::NotPointerTypeEndWithStar;
389 if (auto Reason =
390 extendLeadingQualifiers(DeclBegin, TypeBegin&: RewriteRangeBegin, Ctx))
391 return Reason;
392 Result = {RewriteRangeBegin, PTL.getStarLoc()};
393 } else {
394 SourceLocation RewriteRangeEnd = InnerTypeLoc.getEndLoc();
395
396 if (auto Reason =
397 extendLeadingQualifiers(DeclBegin, TypeBegin&: RewriteRangeBegin, Ctx))
398 return Reason;
399 if (auto Reason = extendTrailingQualifiers(TypeEnd&: RewriteRangeEnd, UpperBound: NameLoc, Ctx))
400 return Reason;
401 Result = {RewriteRangeBegin, RewriteRangeEnd};
402 }
403
404 if (Result.getBegin().isMacroID() || Result.getEnd().isMacroID())
405 return ReportReason::MacroExpansion;
406 if (Result.getBegin().isInvalid() || Result.getEnd().isInvalid())
407 return ReportReason::EmissionFailed;
408
409 const SourceManager &SM = Ctx.getSourceManager();
410 if (SM.getFileID(SpellingLoc: Result.getBegin()) != SM.getFileID(SpellingLoc: Result.getEnd()))
411 return ReportReason::EmissionFailed;
412 RewriteRange = Result;
413 return std::nullopt;
414 }
415
416 /// Emits the type-token replacement (and, for arrays, deletes the trailing
417 /// extent). Returns false without emitting anything if a valid,
418 /// self-contained edit cannot be formed.
419 std::optional<ReportReason> emit(SourceLocation DeclBegin,
420 SourceLocation NameLoc, TypeLoc TLoc,
421 QualType T, const ClassifyResult &R) {
422 const SourceManager &SM = Ctx.getSourceManager();
423 SourceRange TypeRewriteRange;
424
425 if (auto Reason = computeRewriteRange(DeclBegin, NameLoc, TLoc, BoundedType: R.NewType,
426 Ctx, RewriteRange&: TypeRewriteRange))
427 return Reason;
428
429 // TypeRewriteRange is bounded by the tokens (begin location) of the two
430 // ends. Now convert it to char range for source edit, which requires the
431 // bounds to be the characters of the two ends.
432 CharSourceRange TypeRewriteCharRange =
433 Lexer::getAsCharRange(Range: TypeRewriteRange, SM, LangOpts: Ctx.getLangOpts());
434 llvm::SmallVector<tooling::Replacement, 2> Edited;
435
436 Edited.emplace_back(Args: SM, Args&: TypeRewriteCharRange, Args: renderNewType(R, T, Ctx),
437 Args: Ctx.getLangOpts());
438
439 if (R.NewType == BoundedType::Array) {
440 ArrayTypeLoc ATL = TLoc.getUnqualifiedLoc().getAs<ArrayTypeLoc>();
441
442 if (!ATL)
443 return ReportReason::EmissionFailed;
444
445 SourceLocation LBracket = ATL.getLBracketLoc();
446 SourceLocation RBracket = ATL.getRBracketLoc();
447 // A clean array declarator ends at its closing bracket; otherwise the
448 // element spelling wraps the name (e.g. an array of function pointers)
449 // and cannot be rewritten by stripping a trailing extent.
450 if (ATL.getEndLoc() != RBracket)
451 return ReportReason::ArrayNotEndInBracket;
452 if (LBracket.isInvalid() || RBracket.isInvalid())
453 return ReportReason::EmissionFailed;
454 Edited.emplace_back(Args: SM,
455 Args: CharSourceRange::getTokenRange(B: LBracket, E: RBracket),
456 Args: "", Args: Ctx.getLangOpts());
457 }
458
459 if (!llvm::all_of(Range&: Edited, P: std::mem_fn(pm: &tooling::Replacement::isApplicable)))
460 return ReportReason::EmissionFailed;
461 for (tooling::Replacement &Repl : Edited)
462 Edits.addReplacement(R: std::move(Repl));
463 return std::nullopt;
464 }
465
466 /// Reports \p Reason for \p D, if one is given. Always returns true so that
467 /// visitors can tail-call it.
468 bool report(const DeclaratorDecl *D, std::optional<ReportReason> Reason) {
469 if (Reason) {
470 CharSourceRange Range = Lexer::getAsCharRange(
471 Range: declTypeRange(D), SM: Ctx.getSourceManager(), LangOpts: Ctx.getLangOpts());
472 Report.addResult(RuleId: SkippedRuleId, Level: SarifResultLevel::Note, Range,
473 Message: messageFor(Reason: *Reason));
474 }
475 return true;
476 }
477
478 ASTContext &Ctx;
479 DeclLevels &Decls;
480 ReturnLevels &Returns;
481 SourceEditEmitter &Edits;
482 TransformationReportEmitter &Report;
483};
484
485} // namespace
486
487namespace clang::ssaf {
488
489llvm::StringRef messageFor(ReportReason Reason) {
490 switch (Reason) {
491 case ReportReason::ArrayNotEndInBracket:
492 return "the array type does not end in a closing bracket";
493 case ReportReason::DeclarationGroup:
494 return "declarator of a multi-declarator group is not yet rewritten";
495 case ReportReason::EmissionFailed:
496 return "no source edit could be formed for this declarator";
497 case ReportReason::IncompleteArray:
498 return "array of unknown bound is not yet rewritten";
499 case ReportReason::MacroExpansion:
500 return "declarator spelled through a macro is not yet rewritten";
501 case ReportReason::MultiDimensionalArray:
502 return "multi-dimensional array is not yet rewritten";
503 case ReportReason::MultiLevelPointer:
504 return "multi-level pointer indirection is not yet rewritten";
505 case ReportReason::NoInnerTypeLoc:
506 return "no TypeLoc for the pointee or array element type";
507 case ReportReason::NotPointerTypeEndWithStar:
508 return "pointer declarator does not end at its '*'";
509 case ReportReason::NotTransformed:
510 return "this declaration was not transformed";
511 case ReportReason::PointerToArray:
512 return "pointer to array is not yet rewritten";
513 case ReportReason::ReferenceToPointer:
514 return "reference to pointer is not yet rewritten";
515 case ReportReason::TrailingReturnType:
516 return "trailing return type is not yet rewritten";
517 case ReportReason::UnexpectedLeadingQualifier:
518 return "unexpected token between a leading cv-qualifier and the type";
519 case ReportReason::UnexpectedTrailingQualifier:
520 return "unexpected token between the type and a trailing cv-qualifier";
521 case ReportReason::UnnamableType:
522 return "the pointee or array element type has no name that can be written "
523 "as a template argument";
524 }
525 llvm_unreachable("unhandled ReportReason");
526}
527
528ClassifyResult
529classifyDeclType(QualType T, const llvm::SmallSet<unsigned, 4> &ReachableLevels,
530 const ASTContext &Ctx) {
531 ClassifyResult R;
532 if (!ReachableLevels.count(V: 1))
533 return R;
534
535 // A deeper indirection level is reachable too; that is a multi-level rewrite,
536 // which is not yet supported.
537 if (llvm::any_of(Range: ReachableLevels, P: [](unsigned L) { return L > 1; })) {
538 R.Skip = ReportReason::MultiLevelPointer;
539 return R;
540 }
541
542 if (T->isReferenceType()) {
543 QualType Pointee = T.getNonReferenceType();
544 if (Pointee->isPointerType() || Pointee->isArrayType())
545 R.Skip = ReportReason::ReferenceToPointer;
546 return R;
547 }
548
549 if (const auto *PT = T->getAs<PointerType>()) {
550 QualType Pointee = PT->getPointeeType();
551 if (Pointee->isFunctionType()) {
552 assert(false &&
553 "function pointer entities are not expected to be reachable");
554 return R;
555 }
556 if (Pointee->isPointerType()) {
557 R.Skip = ReportReason::MultiLevelPointer;
558 return R;
559 }
560 if (Pointee->isArrayType()) {
561 R.Skip = ReportReason::PointerToArray;
562 return R;
563 }
564 if (!isNamable(T: Pointee)) {
565 R.Skip = ReportReason::UnnamableType;
566 return R;
567 }
568 R.NewType = BoundedType::Ptr;
569 R.InnerSpelling = Pointee->isVoidType() ? "char" : spell(T: Pointee, Ctx);
570 R.Skip = std::nullopt;
571 return R;
572 }
573
574 if (const auto *CAT = Ctx.getAsConstantArrayType(T)) {
575 QualType Element = CAT->getElementType();
576 if (Element->isArrayType()) {
577 R.Skip = ReportReason::MultiDimensionalArray;
578 return R;
579 }
580 if (!isNamable(T: Element)) {
581 R.Skip = ReportReason::UnnamableType;
582 return R;
583 }
584 R.NewType = BoundedType::Array;
585 R.InnerSpelling = spell(T: Element, Ctx);
586 R.Skip = std::nullopt;
587 return R;
588 }
589
590 if (T->isArrayType())
591 R.Skip = ReportReason::IncompleteArray;
592 return R;
593}
594
595void CppBoundedBuffers::HandleTranslationUnit(ASTContext &Ctx) {
596 auto Reachable = Suite.get<UnsafeBufferReachableAnalysisResult>();
597 if (!Reachable) {
598 llvm::consumeError(Err: Reachable.takeError());
599 return;
600 }
601
602 ReachabilityMap Reach(Suite, Reachable->Reachables);
603 NestedBuildNamespace TUNamespace =
604 NestedBuildNamespace::makeCompilationUnit(CompilationId: Opts.CompilationUnitId);
605 NestedBuildNamespace LUNamespace =
606 NestedBuildNamespace::makeLinkUnit(LinkUnitId: Opts.LinkUnitId);
607 DeclLevels Decls;
608 ReturnLevels Returns;
609
610 Decl *TU = Ctx.getTranslationUnitDecl();
611 CollectVisitor(Reach, TUNamespace, LUNamespace, Decls, Returns)
612 .TraverseDecl(D: TU);
613 RewriteVisitor(Ctx, Decls, Returns, Edits, Report).TraverseDecl(D: TU);
614}
615
616} // namespace clang::ssaf
617
618namespace clang::ssaf {
619// NOLINTNEXTLINE(misc-use-internal-linkage)
620volatile int CppBoundedBuffersAnchorSource = 0;
621} // namespace clang::ssaf
622
623static clang::ssaf::TransformationRegistry::Add<CppBoundedBuffers>
624 RegisterCppBoundedBuffers("cpp-bounded-buffers",
625 "Rewrites buffers into bounded types");
626