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