clang: lib/Tooling/Refactoring/ASTSelection.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

12#include "llvm/Support/SaveAndRestore.h"

13#include

14

15using namespace clang;

16using namespace tooling;

17

18namespace {

19

22 if (!isa(D))

24

25

28 R.getEnd(), tok::raw_identifier, SM, LangOpts,

29 false);

30 return LocAfterEnd.isValid()

33}

34

35

36

37class ASTSelectionFinder

39public:

43 SelectionBegin(Selection.getBegin()),

44 SelectionEnd(Selection.getBegin() == Selection.getEnd()

46 : Selection.getEnd()),

47 TargetFile(TargetFile), Context(Context) {

48

49 SelectionStack.push_back(

51 SourceSelectionKind::None));

52 }

53

54 std::optional getSelectedASTNode() {

55 assert(SelectionStack.size() == 1 && "stack was not popped");

56 SelectedASTNode Result = std::move(SelectionStack.back());

57 SelectionStack.pop_back();

58 if (Result.Children.empty())

59 return std::nullopt;

60 return std::move(Result);

61 }

62

64

65

66

69 }

70

72 if (!LookThroughOpaqueValueExprs)

73 return true;

76 }

77

79 if (isa(D))

82 return true;

83

84

89 FileLoc = DeclRange.getEnd();

90 else

91 FileLoc = SM.getSpellingLoc(DeclRange.getBegin());

92 if (SM.getFileID(FileLoc) != TargetFile)

93 return true;

94

96 selectionKindFor(getLexicalDeclRange(D, SM, Context.getLangOpts()));

97 SelectionStack.push_back(

100 popAndAddToSelectionIfSelected(SelectionKind);

101

103 SM.isBeforeInTranslationUnit(SelectionEnd.isValid() ? SelectionEnd

104 : SelectionBegin,

105 DeclRange.getEnd())) {

106

107 return false;

108 }

109 return true;

110 }

111

113 if (!S)

114 return true;

115 if (auto *Opaque = dyn_cast(S))

116 return TraverseOpaqueValueExpr(Opaque);

117

118 if (auto *TE = dyn_cast(S)) {

119 if (TE->isImplicit())

120 return true;

121 }

122

125 SelectionStack.push_back(

128 popAndAddToSelectionIfSelected(SelectionKind);

129 return true;

130 }

131

132private:

135 SelectionStack.pop_back();

136 if (SelectionKind != SourceSelectionKind::None || Node.Children.empty())

137 SelectionStack.back().Children.push_back(std::move(Node));

138 }

139

143 if (Range.isTokenRange())

146 return SourceSelectionKind::None;

147 if (!SelectionEnd.isValid()) {

148

149 if (SM.isPointWithin(SelectionBegin, Range.getBegin(), End))

150 return SourceSelectionKind::ContainsSelection;

151 return SourceSelectionKind::None;

152 }

153 bool HasStart = SM.isPointWithin(SelectionBegin, Range.getBegin(), End);

154 bool HasEnd = SM.isPointWithin(SelectionEnd, Range.getBegin(), End);

155 if (HasStart && HasEnd)

156 return SourceSelectionKind::ContainsSelection;

157 if (SM.isPointWithin(Range.getBegin(), SelectionBegin, SelectionEnd) &&

158 SM.isPointWithin(End, SelectionBegin, SelectionEnd))

159 return SourceSelectionKind::InsideSelection;

160

161

162 if (HasStart && SelectionBegin != End)

163 return SourceSelectionKind::ContainsSelectionStart;

164 if (HasEnd && SelectionEnd != Range.getBegin())

165 return SourceSelectionKind::ContainsSelectionEnd;

166

167 return SourceSelectionKind::None;

168 }

169

173 std::vector SelectionStack;

174

175

176

177 bool LookThroughOpaqueValueExprs = false;

178};

179

180}

181

182std::optional

185 assert(SelectionRange.isValid() &&

187 SelectionRange.getEnd()) &&

188 "Expected a file range");

192 TargetFile &&

193 "selection range must span one file");

194

195 ASTSelectionFinder Visitor(SelectionRange, TargetFile, Context);

197 return Visitor.getSelectedASTNode();

198}

199

201 switch (Kind) {

202 case SourceSelectionKind::None:

203 return "none";

204 case SourceSelectionKind::ContainsSelection:

205 return "contains-selection";

206 case SourceSelectionKind::ContainsSelectionStart:

207 return "contains-selection-start";

208 case SourceSelectionKind::ContainsSelectionEnd:

209 return "contains-selection-end";

210 case SourceSelectionKind::InsideSelection:

211 return "inside";

212 }

213 llvm_unreachable("invalid selection kind");

214}

215

217 unsigned Indent = 0) {

218 OS.indent(Indent * 2);

221 if (const auto *ND = dyn_cast(D))

222 OS << " \"" << ND->getDeclName() << '"';

224 OS << S->getStmtClassName();

225 }

227 for (const auto &Child : Node.Children)

228 dump(Child, OS, Indent + 1);

229}

230

232

233

234

235

236

237

241 for (const auto &Child : Node.Children) {

242 if (Child.SelectionKind == Kind)

243 return true;

246 }

247 return false;

248}

249

250namespace {

251struct SelectedNodeWithParents {

254

255

256

257 void canonicalize();

258};

259

260enum SelectionCanonicalizationAction { KeepSelection, SelectParent };

261

262

263

264SelectionCanonicalizationAction

265getSelectionCanonizalizationAction(const Stmt *S, const Stmt *Parent) {

266

267

268

269

270 if (isa(S) && isa(Parent))

271 return SelectParent;

272

273

274

275

276

277

278

279 else if (const auto *CE = dyn_cast(Parent)) {

280 if ((isa(S) || isa(S)) &&

281 CE->getCallee()->IgnoreImpCasts() == S)

282 return SelectParent;

283 }

284

285 return KeepSelection;

286}

287

288}

289

290void SelectedNodeWithParents::canonicalize() {

292 assert(S && "non statement selection!");

293 const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get<Stmt>();

295 return;

296

297

298 unsigned ParentIndex = 1;

299 for (; (ParentIndex + 1) <= Parents.size() && isa(Parent);

300 ++ParentIndex) {

301 const Stmt *NewParent =

302 Parents[Parents.size() - ParentIndex - 1].get().Node.get<Stmt>();

303 if (!NewParent)

304 break;

306 }

307

308 switch (getSelectionCanonizalizationAction(S, Parent)) {

309 case SelectParent:

310 Node = Parents[Parents.size() - ParentIndex];

311 for (; ParentIndex != 0; --ParentIndex)

312 Parents.pop_back();

313 break;

314 case KeepSelection:

315 break;

316 }

317}

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

347

348

349 for (const auto &Child : ASTSelection.Children) {

351 MatchingNodes.push_back(SelectedNodeWithParents{

352 std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}});

353 return;

354 }

355 }

356 } else {

358

359 MatchingNodes.push_back(SelectedNodeWithParents{

360 std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}});

361 return;

362 }

363 }

364

365 ParentStack.push_back(std::cref(ASTSelection));

366 for (const auto &Child : ASTSelection.Children)

368 ParentStack.pop_back();

369}

370

377}

378

379std::optional

382

383 if (SelectionRange.getBegin() == SelectionRange.getEnd())

384 return std::nullopt;

388

389

390 if (ContainSelection.size() != 1)

391 return std::nullopt;

392 SelectedNodeWithParents &Selected = ContainSelection[0];

393 if (!Selected.Node.get().Node.get<Stmt>())

394 return std::nullopt;

395 const Stmt *CodeRangeStmt = Selected.Node.get().Node.get<Stmt>();

396 if (!isa(CodeRangeStmt)) {

397 Selected.canonicalize();

399 false);

400 }

401

402

403

404

405

406

407

408

409 Selected.Parents.push_back(Selected.Node);

411 true);

412}

413

415

416 return isa(D) || isa(D);

417}

418

420 bool IsPrevCompound = false;

421

422

423

424 for (const auto &Parent : llvm::reverse(Parents)) {

428 return IsPrevCompound;

429

430

431

432

433 if (isa(D))

434 return false;

435 }

437 }

438 return false;

439}

440

442 for (const auto &Parent : llvm::reverse(Parents)) {

446 return D;

447 }

448 }

449 return nullptr;

450}

static bool isFunctionLikeDeclaration(const Decl *D)

static const char * selectionKindToString(SourceSelectionKind Kind)

static void dump(const SelectedASTNode &Node, llvm::raw_ostream &OS, unsigned Indent=0)

static void findDeepestWithKind(const SelectedASTNode &ASTSelection, llvm::SmallVectorImpl< SelectedNodeWithParents > &MatchingNodes, SourceSelectionKind Kind, llvm::SmallVectorImpl< SelectedASTNode::ReferenceType > &ParentStack)

Finds the set of bottom-most selected AST nodes that are in the selection tree with the specified sel...

static bool hasAnyDirectChildrenWithKind(const SelectedASTNode &Node, SourceSelectionKind Kind)

Returns true if the given node has any direct children with the following selection kind.

Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...

SourceManager & getSourceManager()

TranslationUnitDecl * getTranslationUnitDecl() const

const LangOptions & getLangOpts() const

Represents a character-granular source range.

static CharSourceRange getCharRange(SourceRange R)

static CharSourceRange getTokenRange(SourceRange R)

CompoundStmt - This represents a group of statements like { stmt stmt }.

DeclStmt - Adaptor class for mixing declarations with statements and expressions.

Decl - This represents one declaration (or definition), e.g.

bool isImplicit() const

isImplicit - Indicates whether the declaration was implicitly generated by the implementation.

const char * getDeclKindName() const

virtual SourceRange getSourceRange() const LLVM_READONLY

Source range that this declaration covers.

A dynamically typed AST node container.

const T * get() const

Retrieve the stored node as type T.

static DynTypedNode create(const T &Node)

Creates a DynTypedNode from Node.

An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

static SourceLocation findLocationAfterToken(SourceLocation loc, tok::TokenKind TKind, const SourceManager &SM, const LangOptions &LangOpts, bool SkipTrailingWhitespaceAndNewLine)

Checks that the given token is the first token that occurs after the given location (this excludes co...

static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)

Computes the source location just past the end of the token at this source location.

A RecursiveASTVisitor subclass that guarantees that AST traversal is performed in a lexical order (i....

OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class.

PseudoObjectExpr - An expression which accesses a pseudo-object l-value.

bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue=nullptr)

Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...

bool TraverseDecl(Decl *D)

Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...

Encodes a location in the source.

static bool isPairOfFileLocations(SourceLocation Start, SourceLocation End)

bool isValid() const

Return true if this is a valid SourceLocation object.

This class handles loading and caching of source files into memory.

FileID getFileID(SourceLocation SpellingLoc) const

Return the FileID for a SourceLocation.

A trivial tuple used to represent a source range.

SourceLocation getEnd() const

SourceLocation getBegin() const

Stmt - This represents one statement.

The JSON file list parser is used to communicate input to InstallAPI.