/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- SourceExtraction.cpp - Clang refactoring library -----------------===// |
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/Tooling/Refactoring/Extract/SourceExtraction.h" |
10 | | #include "clang/AST/Stmt.h" |
11 | | #include "clang/AST/StmtCXX.h" |
12 | | #include "clang/AST/StmtObjC.h" |
13 | | #include "clang/Basic/SourceManager.h" |
14 | | #include "clang/Lex/Lexer.h" |
15 | | |
16 | | using namespace clang; |
17 | | |
18 | | namespace { |
19 | | |
20 | | /// Returns true if the token at the given location is a semicolon. |
21 | | bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM, |
22 | 5 | const LangOptions &LangOpts) { |
23 | 5 | return Lexer::getSourceText( |
24 | 5 | CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM, |
25 | 5 | LangOpts) == ";"; |
26 | 5 | } |
27 | | |
28 | | /// Returns true if there should be a semicolon after the given statement. |
29 | 31 | bool isSemicolonRequiredAfter(const Stmt *S) { |
30 | 31 | if (isa<CompoundStmt>(S)) |
31 | 7 | return false; |
32 | 24 | if (const auto *If = dyn_cast<IfStmt>(S)) |
33 | 3 | return isSemicolonRequiredAfter(If->getElse() ? If->getElse()0 |
34 | 3 | : If->getThen()); |
35 | 21 | if (const auto *While = dyn_cast<WhileStmt>(S)) |
36 | 2 | return isSemicolonRequiredAfter(While->getBody()); |
37 | 19 | if (const auto *For = dyn_cast<ForStmt>(S)) |
38 | 2 | return isSemicolonRequiredAfter(For->getBody()); |
39 | 17 | if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S)) |
40 | 1 | return isSemicolonRequiredAfter(CXXFor->getBody()); |
41 | 16 | if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S)) |
42 | 1 | return isSemicolonRequiredAfter(ObjCFor->getBody()); |
43 | 15 | if(const auto *Switch = dyn_cast<SwitchStmt>(S)) |
44 | 2 | return isSemicolonRequiredAfter(Switch->getBody()); |
45 | 13 | if(const auto *Case = dyn_cast<SwitchCase>(S)) |
46 | 1 | return isSemicolonRequiredAfter(Case->getSubStmt()); |
47 | 12 | switch (S->getStmtClass()) { |
48 | 3 | case Stmt::DeclStmtClass: |
49 | 4 | case Stmt::CXXTryStmtClass: |
50 | 5 | case Stmt::ObjCAtSynchronizedStmtClass: |
51 | 6 | case Stmt::ObjCAutoreleasePoolStmtClass: |
52 | 7 | case Stmt::ObjCAtTryStmtClass: |
53 | 7 | return false; |
54 | 5 | default: |
55 | 5 | return true; |
56 | 12 | } |
57 | 12 | } |
58 | | |
59 | | /// Returns true if the two source locations are on the same line. |
60 | | bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, |
61 | 4 | const SourceManager &SM) { |
62 | 4 | return !Loc1.isMacroID() && !Loc2.isMacroID() && |
63 | 4 | SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); |
64 | 4 | } |
65 | | |
66 | | } // end anonymous namespace |
67 | | |
68 | | namespace clang { |
69 | | namespace tooling { |
70 | | |
71 | | ExtractionSemicolonPolicy |
72 | | ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange, |
73 | | const SourceManager &SM, |
74 | 26 | const LangOptions &LangOpts) { |
75 | 7 | auto neededInExtractedFunction = []() { |
76 | 7 | return ExtractionSemicolonPolicy(true, false); |
77 | 7 | }; |
78 | 18 | auto neededInOriginalFunction = []() { |
79 | 18 | return ExtractionSemicolonPolicy(false, true); |
80 | 18 | }; |
81 | | |
82 | | /// The extracted expression should be terminated with a ';'. The call to |
83 | | /// the extracted function will replace this expression, so it won't need |
84 | | /// a terminating ';'. |
85 | 26 | if (isa<Expr>(S)) |
86 | 7 | return neededInExtractedFunction(); |
87 | | |
88 | | /// Some statements don't need to be terminated with ';'. The call to the |
89 | | /// extracted function will be a standalone statement, so it should be |
90 | | /// terminated with a ';'. |
91 | 19 | bool NeedsSemi = isSemicolonRequiredAfter(S); |
92 | 19 | if (!NeedsSemi) |
93 | 14 | return neededInOriginalFunction(); |
94 | | |
95 | | /// Some statements might end at ';'. The extraction will move that ';', so |
96 | | /// the call to the extracted function should be terminated with a ';'. |
97 | 5 | SourceLocation End = ExtractedRange.getEnd(); |
98 | 5 | if (isSemicolonAtLocation(End, SM, LangOpts)) |
99 | 1 | return neededInOriginalFunction(); |
100 | | |
101 | | /// Other statements should generally have a trailing ';'. We can try to find |
102 | | /// it and move it together it with the extracted code. |
103 | 4 | Optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts); |
104 | 4 | if (NextToken && NextToken->is(tok::semi) && |
105 | 4 | areOnSameLine(NextToken->getLocation(), End, SM)) { |
106 | 3 | ExtractedRange.setEnd(NextToken->getLocation()); |
107 | 3 | return neededInOriginalFunction(); |
108 | 3 | } |
109 | | |
110 | | /// Otherwise insert semicolons in both places. |
111 | 1 | return ExtractionSemicolonPolicy(true, true); |
112 | 1 | } |
113 | | |
114 | | } // end namespace tooling |
115 | | } // end namespace clang |