/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/TokenAnnotator.h
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- TokenAnnotator.h - Format C++ code ---------------------*- 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 | | /// \file |
10 | | /// This file implements a token annotator, i.e. creates |
11 | | /// \c AnnotatedTokens out of \c FormatTokens with required extra information. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #ifndef LLVM_CLANG_LIB_FORMAT_TOKENANNOTATOR_H |
16 | | #define LLVM_CLANG_LIB_FORMAT_TOKENANNOTATOR_H |
17 | | |
18 | | #include "UnwrappedLineParser.h" |
19 | | #include "clang/Format/Format.h" |
20 | | |
21 | | namespace clang { |
22 | | namespace format { |
23 | | |
24 | | enum LineType { |
25 | | LT_Invalid, |
26 | | LT_ImportStatement, |
27 | | LT_ObjCDecl, // An @interface, @implementation, or @protocol line. |
28 | | LT_ObjCMethodDecl, |
29 | | LT_ObjCProperty, // An @property line. |
30 | | LT_Other, |
31 | | LT_PreprocessorDirective, |
32 | | LT_VirtualFunctionDecl, |
33 | | LT_ArrayOfStructInitializer, |
34 | | }; |
35 | | |
36 | | class AnnotatedLine { |
37 | | public: |
38 | | AnnotatedLine(const UnwrappedLine &Line) |
39 | | : First(Line.Tokens.front().Tok), Level(Line.Level), |
40 | | MatchingOpeningBlockLineIndex(Line.MatchingOpeningBlockLineIndex), |
41 | | MatchingClosingBlockLineIndex(Line.MatchingClosingBlockLineIndex), |
42 | | InPPDirective(Line.InPPDirective), |
43 | | MustBeDeclaration(Line.MustBeDeclaration), MightBeFunctionDecl(false), |
44 | | IsMultiVariableDeclStmt(false), Affected(false), |
45 | | LeadingEmptyLinesAffected(false), ChildrenAffected(false), |
46 | 254k | FirstStartColumn(Line.FirstStartColumn) { |
47 | 254k | assert(!Line.Tokens.empty()); |
48 | | |
49 | | // Calculate Next and Previous for all tokens. Note that we must overwrite |
50 | | // Next and Previous for every token, as previous formatting runs might have |
51 | | // left them in a different state. |
52 | 0 | First->Previous = nullptr; |
53 | 254k | FormatToken *Current = First; |
54 | 862k | for (const UnwrappedLineNode &Node : llvm::drop_begin(Line.Tokens)) { |
55 | 862k | Current->Next = Node.Tok; |
56 | 862k | Node.Tok->Previous = Current; |
57 | 862k | Current = Current->Next; |
58 | 862k | Current->Children.clear(); |
59 | 862k | for (const auto &Child : Node.Children) { |
60 | 6.77k | Children.push_back(new AnnotatedLine(Child)); |
61 | 6.77k | Current->Children.push_back(Children.back()); |
62 | 6.77k | } |
63 | 862k | } |
64 | 254k | Last = Current; |
65 | 254k | Last->Next = nullptr; |
66 | 254k | } |
67 | | |
68 | 254k | ~AnnotatedLine() { |
69 | 254k | for (AnnotatedLine *Child : Children) |
70 | 6.77k | delete Child; |
71 | 254k | FormatToken *Current = First; |
72 | 1.38M | while (Current) { |
73 | 1.13M | Current->Children.clear(); |
74 | 1.13M | Current->Role.reset(); |
75 | 1.13M | Current = Current->Next; |
76 | 1.13M | } |
77 | 254k | } |
78 | | |
79 | 49.8k | bool isComment() const { |
80 | 49.8k | return First && First->is(tok::comment) && !First->getNextNonComment()1.49k ; |
81 | 49.8k | } |
82 | | |
83 | | /// \c true if this line starts with the given tokens in order, ignoring |
84 | | /// comments. |
85 | 1.67M | template <typename... Ts> bool startsWith(Ts... Tokens) const { |
86 | 1.67M | return First && First->startsSequence(Tokens...); |
87 | 1.67M | } bool clang::format::AnnotatedLine::startsWith<clang::tok::TokenKind>(clang::tok::TokenKind) const Line | Count | Source | 85 | 566k | template <typename... Ts> bool startsWith(Ts... Tokens) const { | 86 | 566k | return First && First->startsSequence(Tokens...); | 87 | 566k | } |
bool clang::format::AnnotatedLine::startsWith<clang::format::TokenType>(clang::format::TokenType) const Line | Count | Source | 85 | 1.08M | template <typename... Ts> bool startsWith(Ts... Tokens) const { | 86 | 1.08M | return First && First->startsSequence(Tokens...); | 87 | 1.08M | } |
bool clang::format::AnnotatedLine::startsWith<clang::tok::TokenKind, clang::tok::TokenKind>(clang::tok::TokenKind, clang::tok::TokenKind) const Line | Count | Source | 85 | 16.5k | template <typename... Ts> bool startsWith(Ts... Tokens) const { | 86 | 16.5k | return First && First->startsSequence(Tokens...); | 87 | 16.5k | } |
bool clang::format::AnnotatedLine::startsWith<clang::IdentifierInfo*, clang::tok::TokenKind>(clang::IdentifierInfo*, clang::tok::TokenKind) const Line | Count | Source | 85 | 3.98k | template <typename... Ts> bool startsWith(Ts... Tokens) const { | 86 | 3.98k | return First && First->startsSequence(Tokens...); | 87 | 3.98k | } |
bool clang::format::AnnotatedLine::startsWith<clang::tok::TokenKind, clang::IdentifierInfo*, clang::tok::TokenKind>(clang::tok::TokenKind, clang::IdentifierInfo*, clang::tok::TokenKind) const Line | Count | Source | 85 | 3.92k | template <typename... Ts> bool startsWith(Ts... Tokens) const { | 86 | 3.92k | return First && First->startsSequence(Tokens...); | 87 | 3.92k | } |
bool clang::format::AnnotatedLine::startsWith<clang::tok::TokenKind, clang::tok::TokenKind, clang::tok::TokenKind>(clang::tok::TokenKind, clang::tok::TokenKind, clang::tok::TokenKind) const Line | Count | Source | 85 | 469 | template <typename... Ts> bool startsWith(Ts... Tokens) const { | 86 | 469 | return First && First->startsSequence(Tokens...); | 87 | 469 | } |
bool clang::format::AnnotatedLine::startsWith<clang::IdentifierInfo*>(clang::IdentifierInfo*) const Line | Count | Source | 85 | 62 | template <typename... Ts> bool startsWith(Ts... Tokens) const { | 86 | 62 | return First && First->startsSequence(Tokens...); | 87 | 62 | } |
|
88 | | |
89 | | /// \c true if this line ends with the given tokens in reversed order, |
90 | | /// ignoring comments. |
91 | | /// For example, given tokens [T1, T2, T3, ...], the function returns true if |
92 | | /// this line is like "... T3 T2 T1". |
93 | 9.16k | template <typename... Ts> bool endsWith(Ts... Tokens) const { |
94 | 9.16k | return Last && Last->endsSequence(Tokens...); |
95 | 9.16k | } |
96 | | |
97 | | /// \c true if this line looks like a function definition instead of a |
98 | | /// function declaration. Asserts MightBeFunctionDecl. |
99 | 6.50k | bool mightBeFunctionDefinition() const { |
100 | 6.50k | assert(MightBeFunctionDecl); |
101 | | // Try to determine if the end of a stream of tokens is either the |
102 | | // Definition or the Declaration for a function. It does this by looking for |
103 | | // the ';' in foo(); and using that it ends with a ; to know this is the |
104 | | // Definition, however the line could end with |
105 | | // foo(); /* comment */ |
106 | | // or |
107 | | // foo(); // comment |
108 | | // or |
109 | | // foo() // comment |
110 | | // endsWith() ignores the comment. |
111 | 0 | return !endsWith(tok::semi); |
112 | 6.50k | } |
113 | | |
114 | | /// \c true if this line starts a namespace definition. |
115 | 9.09k | bool startsWithNamespace() const { |
116 | 9.09k | return startsWith(tok::kw_namespace) || startsWith(TT_NamespaceMacro)7.80k || |
117 | 9.09k | startsWith(tok::kw_inline, tok::kw_namespace)7.77k || |
118 | 9.09k | startsWith(tok::kw_export, tok::kw_namespace)7.75k ; |
119 | 9.09k | } |
120 | | |
121 | | FormatToken *First; |
122 | | FormatToken *Last; |
123 | | |
124 | | SmallVector<AnnotatedLine *, 0> Children; |
125 | | |
126 | | LineType Type; |
127 | | unsigned Level; |
128 | | size_t MatchingOpeningBlockLineIndex; |
129 | | size_t MatchingClosingBlockLineIndex; |
130 | | bool InPPDirective; |
131 | | bool MustBeDeclaration; |
132 | | bool MightBeFunctionDecl; |
133 | | bool IsMultiVariableDeclStmt; |
134 | | |
135 | | /// \c True if this line should be formatted, i.e. intersects directly or |
136 | | /// indirectly with one of the input ranges. |
137 | | bool Affected; |
138 | | |
139 | | /// \c True if the leading empty lines of this line intersect with one of the |
140 | | /// input ranges. |
141 | | bool LeadingEmptyLinesAffected; |
142 | | |
143 | | /// \c True if one of this line's children intersects with an input range. |
144 | | bool ChildrenAffected; |
145 | | |
146 | | unsigned FirstStartColumn; |
147 | | |
148 | | private: |
149 | | // Disallow copying. |
150 | | AnnotatedLine(const AnnotatedLine &) = delete; |
151 | | void operator=(const AnnotatedLine &) = delete; |
152 | | }; |
153 | | |
154 | | /// Determines extra information about the tokens comprising an |
155 | | /// \c UnwrappedLine. |
156 | | class TokenAnnotator { |
157 | | public: |
158 | | TokenAnnotator(const FormatStyle &Style, const AdditionalKeywords &Keywords) |
159 | 66.7k | : Style(Style), Keywords(Keywords) {} |
160 | | |
161 | | /// Adapts the indent levels of comment lines to the indent of the |
162 | | /// subsequent line. |
163 | | // FIXME: Can/should this be done in the UnwrappedLineParser? |
164 | | void setCommentLineLevels(SmallVectorImpl<AnnotatedLine *> &Lines) const; |
165 | | |
166 | | void annotate(AnnotatedLine &Line) const; |
167 | | void calculateFormattingInformation(AnnotatedLine &Line) const; |
168 | | |
169 | | private: |
170 | | /// Calculate the penalty for splitting before \c Tok. |
171 | | unsigned splitPenalty(const AnnotatedLine &Line, const FormatToken &Tok, |
172 | | bool InFunctionDecl) const; |
173 | | |
174 | | bool spaceRequiredBeforeParens(const FormatToken &Right) const; |
175 | | |
176 | | bool spaceRequiredBetween(const AnnotatedLine &Line, const FormatToken &Left, |
177 | | const FormatToken &Right) const; |
178 | | |
179 | | bool spaceRequiredBefore(const AnnotatedLine &Line, |
180 | | const FormatToken &Right) const; |
181 | | |
182 | | bool mustBreakBefore(const AnnotatedLine &Line, |
183 | | const FormatToken &Right) const; |
184 | | |
185 | | bool canBreakBefore(const AnnotatedLine &Line, |
186 | | const FormatToken &Right) const; |
187 | | |
188 | | bool mustBreakForReturnType(const AnnotatedLine &Line) const; |
189 | | |
190 | | void printDebugInfo(const AnnotatedLine &Line) const; |
191 | | |
192 | | void calculateUnbreakableTailLengths(AnnotatedLine &Line) const; |
193 | | |
194 | | void calculateArrayInitializerColumnList(AnnotatedLine &Line) const; |
195 | | |
196 | | FormatToken *calculateInitializerColumnList(AnnotatedLine &Line, |
197 | | FormatToken *CurrentToken, |
198 | | unsigned Depth) const; |
199 | | FormatStyle::PointerAlignmentStyle |
200 | | getTokenReferenceAlignment(const FormatToken &PointerOrReference) const; |
201 | | |
202 | | FormatStyle::PointerAlignmentStyle getTokenPointerOrReferenceAlignment( |
203 | | const FormatToken &PointerOrReference) const; |
204 | | |
205 | | const FormatStyle &Style; |
206 | | |
207 | | const AdditionalKeywords &Keywords; |
208 | | }; |
209 | | |
210 | | } // end namespace format |
211 | | } // end namespace clang |
212 | | |
213 | | #endif |