Skip to content

Commit e92dd11

Browse files
committed
[BoundsSafety] get counted_by spelling from source range
This replaces the count expr source location used for note_counted_by_consider_using_sized_by with the actual attribute location fetched from TypeLof, except in the rare case where we couldn't find a TypeLoc. It also attaches a fix-it hint, now that we have the proper source range. Fixes #113585
1 parent 8804ce9 commit e92dd11

File tree

2 files changed

+109
-16
lines changed

2 files changed

+109
-16
lines changed

clang/include/clang/AST/TypeLoc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,7 @@ class ObjCInterfaceTypeLoc : public ConcreteTypeLoc<ObjCObjectTypeLoc,
13041304
}
13051305
};
13061306

1307+
class Sema;
13071308
struct BoundsAttributedLocInfo {
13081309
SourceRange Range;
13091310
};
@@ -1321,6 +1322,9 @@ class BoundsAttributedTypeLoc
13211322
}
13221323
SourceRange getAttrRange() const { return getLocalData()->Range; }
13231324

1325+
StringRef getAttrNameAsWritten(Sema &S) const;
1326+
SourceRange getAttrNameRange(Sema &S) const;
1327+
13241328
unsigned getLocalDataSize() const { return sizeof(BoundsAttributedLocInfo); }
13251329
};
13261330

clang/lib/Sema/SemaBoundsSafety.cpp

Lines changed: 105 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
/// (e.g. `counted_by`)
1212
///
1313
//===----------------------------------------------------------------------===//
14+
#include "clang/AST/Decl.h"
15+
#include "clang/AST/Expr.h"
16+
#include "clang/AST/StmtVisitor.h"
17+
#include "clang/AST/TypeBase.h"
18+
#include "clang/AST/TypeLoc.h"
19+
#include "clang/Basic/Diagnostic.h"
20+
#include "clang/Basic/SourceLocation.h"
21+
#include "clang/Basic/SourceManager.h"
1422
#include "clang/Lex/Lexer.h"
1523
#include "clang/Sema/Initialization.h"
1624
#include "clang/Sema/Sema.h"
@@ -231,9 +239,69 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
231239
return false;
232240
}
233241

242+
// FIXME: for some reason diagnostics highlight the end character, while
243+
// getSourceText() does not include the end character.
244+
static SourceRange getAttrNameRangeImpl(Sema &S, SourceLocation Begin,
245+
bool IsForDiagnostics) {
246+
SourceManager &SM = S.getSourceManager();
247+
SourceLocation TokenStart = Begin;
248+
while (TokenStart.isMacroID())
249+
TokenStart = SM.getImmediateExpansionRange(TokenStart).getBegin();
250+
unsigned Offset = IsForDiagnostics ? 1 : 0;
251+
SourceLocation End = S.getLocForEndOfToken(TokenStart, Offset);
252+
return {TokenStart, End};
253+
}
254+
255+
StringRef BoundsAttributedTypeLoc::getAttrNameAsWritten(Sema &S) const {
256+
SourceRange Range = getAttrNameRangeImpl(S, getAttrRange().getBegin(), false);
257+
CharSourceRange NameRange = CharSourceRange::getCharRange(Range);
258+
return Lexer::getSourceText(NameRange, S.getSourceManager(), S.getLangOpts());
259+
}
260+
261+
SourceRange BoundsAttributedTypeLoc::getAttrNameRange(Sema &S) const {
262+
return getAttrNameRangeImpl(S, getAttrRange().getBegin(), true);
263+
}
264+
265+
static TypeSourceInfo *getTSI(const Decl *D) {
266+
if (const auto* DD = dyn_cast<DeclaratorDecl>(D)) {
267+
return DD->getTypeSourceInfo();
268+
}
269+
return nullptr;
270+
}
271+
272+
struct TypeLocFinder : public ConstStmtVisitor<TypeLocFinder, TypeLoc> {
273+
TypeLoc VisitParenExpr(const ParenExpr* E) {
274+
return Visit(E->getSubExpr());
275+
}
276+
277+
TypeLoc VisitDeclRefExpr(const DeclRefExpr *E) {
278+
return getTSI(E->getDecl())->getTypeLoc();
279+
}
280+
281+
TypeLoc VisitMemberExpr(const MemberExpr *E) {
282+
return getTSI(E->getMemberDecl())->getTypeLoc();
283+
}
284+
285+
TypeLoc VisitExplicitCastExpr(const ExplicitCastExpr *E) {
286+
return E->getTypeInfoAsWritten()->getTypeLoc();
287+
}
288+
289+
TypeLoc VisitCallExpr(const CallExpr *E) {
290+
if (const auto *D = E->getCalleeDecl()) {
291+
FunctionTypeLoc FTL = getTSI(D)->getTypeLoc().getAs<FunctionTypeLoc>();
292+
if (FTL.isNull()) {
293+
return FTL;
294+
}
295+
return FTL.getReturnLoc();
296+
}
297+
return {};
298+
}
299+
};
300+
234301
static void EmitIncompleteCountedByPointeeNotes(Sema &S,
235302
const CountAttributedType *CATy,
236-
NamedDecl *IncompleteTyDecl) {
303+
NamedDecl *IncompleteTyDecl,
304+
TypeLoc TL) {
237305
assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));
238306

239307
if (IncompleteTyDecl) {
@@ -253,20 +321,36 @@ static void EmitIncompleteCountedByPointeeNotes(Sema &S,
253321
<< CATy->getPointeeType();
254322
}
255323

256-
// Suggest using __sized_by(_or_null) instead of __counted_by(_or_null) as
257-
// __sized_by(_or_null) doesn't have the complete type restriction.
258-
//
259-
// We use the source range of the expression on the CountAttributedType as an
260-
// approximation for the source range of the attribute. This isn't quite right
261-
// but isn't easy to fix right now.
262-
//
263-
// TODO: Implement logic to find the relevant TypeLoc for the attribute and
264-
// get the SourceRange from that (#113582).
265-
//
266-
// TODO: We should emit a fix-it here.
267-
SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange();
324+
CountAttributedTypeLoc CATL;
325+
if (!TL.isNull())
326+
CATL = TL.getAs<CountAttributedTypeLoc>();
327+
328+
if (CATL.isNull()) {
329+
// Fall back to pointing to the count expr - not great, but close enough.
330+
// This should happen rarely, if ever.
331+
S.Diag(CATy->getCountExpr()->getExprLoc(),
332+
diag::note_counted_by_consider_using_sized_by)
333+
<< CATy->isOrNull();
334+
return;
335+
}
336+
SourceRange AttrSrcRange = CATL.getAttrNameRange(S);
337+
338+
StringRef Spelling = CATL.getAttrNameAsWritten(S);
339+
StringRef FixedSpelling;
340+
if (Spelling == "__counted_by")
341+
FixedSpelling = "__sized_by";
342+
else if (Spelling == "counted_by")
343+
FixedSpelling = "sized_by";
344+
else if (Spelling == "__counted_by_or_null")
345+
FixedSpelling = "__sized_by_or_null";
346+
else if (Spelling == "counted_by_or_null")
347+
FixedSpelling = "sized_by_or_null";
348+
FixItHint Fix;
349+
if (!FixedSpelling.empty())
350+
Fix = FixItHint::CreateReplacement(AttrSrcRange, FixedSpelling);
351+
268352
S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by)
269-
<< CATy->isOrNull() << AttrSrcRange;
353+
<< CATy->isOrNull() << AttrSrcRange << Fix;
270354
}
271355

272356
static std::tuple<const CountAttributedType *, QualType>
@@ -335,7 +419,11 @@ static bool CheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
335419
<< CATy->getAttributeName(/*WithMacroPrefix=*/true) << PointeeTy
336420
<< CATy->isOrNull() << RHSExpr->getSourceRange();
337421

338-
EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl);
422+
TypeLoc TL;
423+
if (TypeSourceInfo *TSI = getTSI(Assignee))
424+
TL = TSI->getTypeLoc();
425+
426+
EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl, TL);
339427
return false; // check failed
340428
}
341429

@@ -408,7 +496,8 @@ bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) {
408496
<< CATy->getAttributeName(/*WithMacroPrefix=*/true) << CATy->isOrNull()
409497
<< E->getSourceRange();
410498

411-
EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl);
499+
TypeLoc TL = TypeLocFinder().Visit(E);
500+
EmitIncompleteCountedByPointeeNotes(*this, CATy, IncompleteTyDecl, TL);
412501
return false;
413502
}
414503

0 commit comments

Comments
 (0)