1 | //===--- SourceCode.h - Source code manipulation routines -------*- 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 | // This file provides functions that simplify extraction of source code. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef LLVM_CLANG_TOOLING_TRANSFORMER_SOURCECODE_H |
14 | #define LLVM_CLANG_TOOLING_TRANSFORMER_SOURCECODE_H |
15 | |
16 | #include "clang/AST/ASTContext.h" |
17 | #include "clang/Basic/SourceLocation.h" |
18 | #include "clang/Basic/TokenKinds.h" |
19 | #include <optional> |
20 | |
21 | namespace clang { |
22 | namespace tooling { |
23 | |
24 | /// Extends \p Range to include the token \p Terminator, if it immediately |
25 | /// follows the end of the range. Otherwise, returns \p Range unchanged. |
26 | CharSourceRange maybeExtendRange(CharSourceRange Range, |
27 | tok::TokenKind Terminator, |
28 | ASTContext &Context); |
29 | |
30 | /// Returns the source range spanning the node, extended to include \p Next, if |
31 | /// it immediately follows \p Node. Otherwise, returns the normal range of \p |
32 | /// Node. See comments on `getExtendedText()` for examples. |
33 | template <typename T> |
34 | CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next, |
35 | ASTContext &Context) { |
36 | return maybeExtendRange(CharSourceRange::getTokenRange(Node.getSourceRange()), |
37 | Next, Context); |
38 | } |
39 | |
40 | /// Returns the logical source range of the node extended to include associated |
41 | /// comments and whitespace before and after the node, and associated |
42 | /// terminators. The returned range consists of file locations, if valid file |
43 | /// locations can be found for the associated content; otherwise, an invalid |
44 | /// range is returned. |
45 | /// |
46 | /// Note that parsing comments is disabled by default. In order to select a |
47 | /// range containing associated comments, you may need to invoke the tool with |
48 | /// `-fparse-all-comments`. |
49 | CharSourceRange getAssociatedRange(const Decl &D, ASTContext &Context); |
50 | |
51 | /// Returns the source-code text in the specified range. |
52 | StringRef getText(CharSourceRange Range, const ASTContext &Context); |
53 | |
54 | /// Returns the source-code text corresponding to \p Node. |
55 | template <typename T> |
56 | StringRef getText(const T &Node, const ASTContext &Context) { |
57 | return getText(CharSourceRange::getTokenRange(Node.getSourceRange()), |
58 | Context); |
59 | } |
60 | |
61 | /// Returns the source text of the node, extended to include \p Next, if it |
62 | /// immediately follows the node. Otherwise, returns the text of just \p Node. |
63 | /// |
64 | /// For example, given statements S1 and S2 below: |
65 | /// \code |
66 | /// { |
67 | /// // S1: |
68 | /// if (!x) return foo(); |
69 | /// // S2: |
70 | /// if (!x) { return 3; } |
71 | /// } |
72 | /// \endcode |
73 | /// then |
74 | /// \code |
75 | /// getText(S1, Context) = "if (!x) return foo()" |
76 | /// getExtendedText(S1, tok::TokenKind::semi, Context) |
77 | /// = "if (!x) return foo();" |
78 | /// getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context) |
79 | /// = "return foo();" |
80 | /// getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context) |
81 | /// = getText(S2, Context) = "{ return 3; }" |
82 | /// \endcode |
83 | template <typename T> |
84 | StringRef getExtendedText(const T &Node, tok::TokenKind Next, |
85 | ASTContext &Context) { |
86 | return getText(getExtendedRange(Node, Next, Context), Context); |
87 | } |
88 | |
89 | /// Determines whether \p Range is one that can be edited by a rewrite; |
90 | /// generally, one that starts and ends within a particular file. |
91 | llvm::Error validateEditRange(const CharSourceRange &Range, |
92 | const SourceManager &SM); |
93 | |
94 | /// Determines whether \p Range is one that can be read from. If |
95 | /// `AllowSystemHeaders` is false, a range that falls within a system header |
96 | /// fails validation. |
97 | llvm::Error validateRange(const CharSourceRange &Range, const SourceManager &SM, |
98 | bool ); |
99 | |
100 | /// Attempts to resolve the given range to one that can be edited by a rewrite; |
101 | /// generally, one that starts and ends within a particular file. If a value is |
102 | /// returned, it satisfies \c validateEditRange. |
103 | /// |
104 | /// If \c IncludeMacroExpansion is true, a limited set of cases involving source |
105 | /// locations in macro expansions is supported. For example, if we're looking to |
106 | /// rewrite the int literal 3 to 6, and we have the following definition: |
107 | /// #define DO_NOTHING(x) x |
108 | /// then |
109 | /// foo(DO_NOTHING(3)) |
110 | /// will be rewritten to |
111 | /// foo(6) |
112 | std::optional<CharSourceRange> |
113 | getFileRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM, |
114 | const LangOptions &LangOpts, |
115 | bool IncludeMacroExpansion = true); |
116 | inline std::optional<CharSourceRange> |
117 | getFileRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context, |
118 | bool IncludeMacroExpansion = true) { |
119 | return getFileRangeForEdit(EditRange, SM: Context.getSourceManager(), |
120 | LangOpts: Context.getLangOpts(), IncludeMacroExpansion); |
121 | } |
122 | |
123 | /// Attempts to resolve the given range to one that starts and ends in a |
124 | /// particular file. |
125 | /// |
126 | /// If \c IncludeMacroExpansion is true, a limited set of cases involving source |
127 | /// locations in macro expansions is supported. For example, if we're looking to |
128 | /// get the range of the int literal 3, and we have the following definition: |
129 | /// #define DO_NOTHING(x) x |
130 | /// foo(DO_NOTHING(3)) |
131 | /// the returned range will hold the source text `DO_NOTHING(3)`. |
132 | std::optional<CharSourceRange> getFileRange(const CharSourceRange &EditRange, |
133 | const SourceManager &SM, |
134 | const LangOptions &LangOpts, |
135 | bool IncludeMacroExpansion); |
136 | inline std::optional<CharSourceRange> |
137 | getFileRange(const CharSourceRange &EditRange, const ASTContext &Context, |
138 | bool IncludeMacroExpansion) { |
139 | return getFileRange(EditRange, SM: Context.getSourceManager(), |
140 | LangOpts: Context.getLangOpts(), IncludeMacroExpansion); |
141 | } |
142 | |
143 | } // namespace tooling |
144 | } // namespace clang |
145 | #endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCECODE_H |
146 | |