Skip to content

Commit de5b099

Browse files
[-Wunsafe-buffer-usage][NFC] Factor out FixitUtil (#135100)
1 parent 68ab45f commit de5b099

File tree

4 files changed

+308
-139
lines changed

4 files changed

+308
-139
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===- FixitUtil.h ----------------------------------------------*- C++ -*-===//
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+
#ifndef LLVM_CLANG_ANALYSIS_SUPPORT_FIXITUTIL_H
10+
#define LLVM_CLANG_ANALYSIS_SUPPORT_FIXITUTIL_H
11+
12+
#include "clang/AST/Decl.h"
13+
#include "clang/AST/Expr.h"
14+
#include "clang/Basic/SourceLocation.h"
15+
#include "clang/Lex/Lexer.h"
16+
#include <optional>
17+
#include <string>
18+
19+
namespace clang {
20+
21+
// Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
22+
// type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not
23+
// have source ranges of qualifiers ( The `QualTypeLoc` looks hacky too me
24+
// :( ), `Qualifiers` of the pointee type is returned separately through the
25+
// output parameter `QualifiersToAppend`.
26+
std::optional<std::string>
27+
getPointeeTypeText(const DeclaratorDecl *VD, const SourceManager &SM,
28+
const LangOptions &LangOpts,
29+
std::optional<Qualifiers> *QualifiersToAppend);
30+
31+
// returns text of pointee to pointee (T*&)
32+
std::optional<std::string>
33+
getPointee2TypeText(const DeclaratorDecl *VD, const SourceManager &SM,
34+
const LangOptions &LangOpts,
35+
std::optional<Qualifiers> *QualifiersToAppend);
36+
37+
SourceLocation getBeginLocOfNestedIdentifier(const DeclaratorDecl *D);
38+
39+
// Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
40+
std::optional<StringRef> getRangeText(SourceRange SR, const SourceManager &SM,
41+
const LangOptions &LangOpts);
42+
43+
// Returns the literal text of the identifier of the given variable declaration.
44+
std::optional<StringRef> getVarDeclIdentifierText(const DeclaratorDecl *VD,
45+
const SourceManager &SM,
46+
const LangOptions &LangOpts);
47+
48+
// Return text representation of an `Expr`.
49+
std::optional<StringRef> getExprText(const Expr *E, const SourceManager &SM,
50+
const LangOptions &LangOpts);
51+
52+
// Return the source location just past the last character of the AST `Node`.
53+
template <typename NodeTy>
54+
std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
55+
const SourceManager &SM,
56+
const LangOptions &LangOpts) {
57+
SourceLocation Loc =
58+
Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
59+
if (Loc.isValid())
60+
return Loc;
61+
return std::nullopt;
62+
}
63+
64+
// Returns the begin location of the identifier of the given variable
65+
// declaration.
66+
SourceLocation getVarDeclIdentifierLoc(const DeclaratorDecl *VD);
67+
68+
} // end namespace clang
69+
70+
#endif /* LLVM_CLANG_ANALYSIS_SUPPORT_FIXITUTIL_H */

clang/lib/Analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_clang_library(clangAnalysis
1818
CodeInjector.cpp
1919
Dominators.cpp
2020
ExprMutationAnalyzer.cpp
21+
FixitUtil.cpp
2122
IntervalPartition.cpp
2223
IssueHash.cpp
2324
LiveVariables.cpp

clang/lib/Analysis/FixitUtil.cpp

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
//===- FixitUtil.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/Analysis/Support/FixitUtil.h"
10+
#include "clang/ASTMatchers/ASTMatchers.h"
11+
12+
using namespace llvm;
13+
using namespace clang;
14+
using namespace ast_matchers;
15+
16+
// Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
17+
// type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not
18+
// have source ranges of qualifiers ( The `QualTypeLoc` looks hacky too me
19+
// :( ), `Qualifiers` of the pointee type is returned separately through the
20+
// output parameter `QualifiersToAppend`.
21+
std::optional<std::string>
22+
clang::getPointeeTypeText(const DeclaratorDecl *VD, const SourceManager &SM,
23+
const LangOptions &LangOpts,
24+
std::optional<Qualifiers> *QualifiersToAppend) {
25+
QualType Ty = VD->getType();
26+
QualType PteTy;
27+
28+
assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
29+
"Expecting a VarDecl of type of pointer to object type");
30+
PteTy = Ty->getPointeeType();
31+
32+
TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
33+
TypeLoc PteTyLoc;
34+
35+
// We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
36+
// the `TypeLoc` of the pointee type:
37+
switch (TyLoc.getTypeLocClass()) {
38+
case TypeLoc::ConstantArray:
39+
case TypeLoc::IncompleteArray:
40+
case TypeLoc::VariableArray:
41+
case TypeLoc::DependentSizedArray:
42+
case TypeLoc::Decayed:
43+
assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
44+
"pointer type unless it decays.");
45+
PteTyLoc = TyLoc.getNextTypeLoc();
46+
break;
47+
case TypeLoc::Pointer:
48+
PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
49+
break;
50+
default:
51+
return std::nullopt;
52+
}
53+
if (PteTyLoc.isNull())
54+
// Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
55+
// when the pointer type is `auto`.
56+
return std::nullopt;
57+
58+
// TODO check
59+
SourceLocation IdentLoc = VD->getLocation();
60+
61+
if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
62+
// We are expecting these locations to be valid. But in some cases, they are
63+
// not all valid. It is a Clang bug to me and we are not responsible for
64+
// fixing it. So we will just give up for now when it happens.
65+
return std::nullopt;
66+
}
67+
68+
// Note that TypeLoc.getEndLoc() returns the begin location of the last token:
69+
SourceLocation PteEndOfTokenLoc =
70+
Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
71+
72+
if (!PteEndOfTokenLoc.isValid())
73+
// Sometimes we cannot get the end location of the pointee type, e.g., when
74+
// there are macros involved.
75+
return std::nullopt;
76+
if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc) &&
77+
PteEndOfTokenLoc != IdentLoc) {
78+
// We only deal with the cases where the source text of the pointee type
79+
// appears on the left-hand side of the variable identifier completely,
80+
// including the following forms:
81+
// `T ident`,
82+
// `T ident[]`, where `T` is any type.
83+
// Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
84+
return std::nullopt;
85+
}
86+
if (PteTy.hasQualifiers()) {
87+
// TypeLoc does not provide source ranges for qualifiers (it says it's
88+
// intentional but seems fishy to me), so we cannot get the full text
89+
// `PteTy` via source ranges.
90+
*QualifiersToAppend = PteTy.getQualifiers();
91+
}
92+
return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
93+
->str();
94+
}
95+
96+
// returns text of pointee to pointee (T*&)
97+
std::optional<std::string>
98+
getPointee2TypeText(const DeclaratorDecl *VD, const SourceManager &SM,
99+
const LangOptions &LangOpts,
100+
std::optional<Qualifiers> *QualifiersToAppend) {
101+
102+
QualType Ty = VD->getType();
103+
assert(Ty->isReferenceType() &&
104+
"Expecting a VarDecl of reference to pointer type");
105+
106+
Ty = Ty->getPointeeType();
107+
QualType PteTy;
108+
109+
assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
110+
"Expecting a VarDecl of type of pointer to object type");
111+
PteTy = Ty->getPointeeType();
112+
113+
TypeLoc TyLoc = VD->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
114+
TypeLoc PtrTyLoc;
115+
TypeLoc PteTyLoc;
116+
117+
// We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
118+
// the `TypeLoc` of the pointee type:
119+
switch (TyLoc.getTypeLocClass()) {
120+
case TypeLoc::ConstantArray:
121+
case TypeLoc::IncompleteArray:
122+
case TypeLoc::VariableArray:
123+
case TypeLoc::DependentSizedArray:
124+
case TypeLoc::LValueReference:
125+
PtrTyLoc = TyLoc.castAs<ReferenceTypeLoc>().getPointeeLoc();
126+
if (PtrTyLoc.getTypeLocClass() == TypeLoc::Pointer) {
127+
PteTyLoc = PtrTyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
128+
break;
129+
}
130+
return std::nullopt;
131+
break;
132+
default:
133+
return std::nullopt;
134+
}
135+
if (PteTyLoc.isNull())
136+
// Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
137+
// when the pointer type is `auto`.
138+
return std::nullopt;
139+
140+
// TODO make sure this works
141+
SourceLocation IdentLoc = VD->getLocation();
142+
143+
if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
144+
// We are expecting these locations to be valid. But in some cases, they are
145+
// not all valid. It is a Clang bug to me and we are not responsible for
146+
// fixing it. So we will just give up for now when it happens.
147+
return std::nullopt;
148+
}
149+
150+
// Note that TypeLoc.getEndLoc() returns the begin location of the last token:
151+
SourceLocation PteEndOfTokenLoc =
152+
Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
153+
154+
if (!PteEndOfTokenLoc.isValid())
155+
// Sometimes we cannot get the end location of the pointee type, e.g., when
156+
// there are macros involved.
157+
return std::nullopt;
158+
if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
159+
// We only deal with the cases where the source text of the pointee type
160+
// appears on the left-hand side of the variable identifier completely,
161+
// including the following forms:
162+
// `T ident`,
163+
// `T ident[]`, where `T` is any type.
164+
// Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
165+
return std::nullopt;
166+
}
167+
if (PteTy.hasQualifiers()) {
168+
// TypeLoc does not provide source ranges for qualifiers (it says it's
169+
// intentional but seems fishy to me), so we cannot get the full text
170+
// `PteTy` via source ranges.
171+
*QualifiersToAppend = PteTy.getQualifiers();
172+
}
173+
return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
174+
->str();
175+
}
176+
177+
SourceLocation clang::getBeginLocOfNestedIdentifier(const DeclaratorDecl *D) {
178+
if (D->getQualifier()) {
179+
return D->getQualifierLoc().getBeginLoc();
180+
}
181+
return getVarDeclIdentifierLoc(D);
182+
}
183+
184+
// Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
185+
std::optional<StringRef> clang::getRangeText(SourceRange SR,
186+
const SourceManager &SM,
187+
const LangOptions &LangOpts) {
188+
bool Invalid = false;
189+
CharSourceRange CSR = CharSourceRange::getCharRange(SR);
190+
StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
191+
192+
if (!Invalid)
193+
return Text;
194+
return std::nullopt;
195+
}
196+
197+
// Returns the literal text of the identifier of the given variable declaration.
198+
std::optional<StringRef>
199+
clang::getVarDeclIdentifierText(const DeclaratorDecl *VD,
200+
const SourceManager &SM,
201+
const LangOptions &LangOpts) {
202+
SourceLocation ParmIdentBeginLoc = getBeginLocOfNestedIdentifier(VD);
203+
SourceLocation ParmIdentEndLoc =
204+
Lexer::getLocForEndOfToken(getVarDeclIdentifierLoc(VD), 0, SM, LangOpts);
205+
206+
if (VD->getQualifier()) {
207+
ParmIdentBeginLoc = VD->getQualifierLoc().getBeginLoc();
208+
}
209+
210+
if (ParmIdentEndLoc.isMacroID() &&
211+
!Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
212+
return std::nullopt;
213+
return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
214+
}
215+
216+
// Return text representation of an `Expr`.
217+
std::optional<StringRef> clang::getExprText(const Expr *E,
218+
const SourceManager &SM,
219+
const LangOptions &LangOpts) {
220+
std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
221+
222+
if (LastCharLoc)
223+
return Lexer::getSourceText(
224+
CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
225+
LangOpts);
226+
227+
return std::nullopt;
228+
}
229+
230+
// Returns the begin location of the identifier of the given variable
231+
// declaration.
232+
SourceLocation clang::getVarDeclIdentifierLoc(const DeclaratorDecl *VD) {
233+
// According to the implementation of `VarDecl`, `VD->getLocation()` actually
234+
// returns the begin location of the identifier of the declaration:
235+
return VD->getLocation();
236+
}

0 commit comments

Comments
 (0)