xref: /llvm-project/clang/lib/Tooling/Transformer/SourceCode.cpp (revision 0e480b39c66143ad142f9a30d8d40e49d7d7b0ce)
1 //===--- SourceCode.cpp - 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 #include "clang/Tooling/Transformer/SourceCode.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/Support/Errc.h"
15 
16 using namespace clang;
17 
18 using llvm::errc;
19 using llvm::StringError;
20 
21 StringRef clang::tooling::getText(CharSourceRange Range,
22                                   const ASTContext &Context) {
23   return Lexer::getSourceText(Range, Context.getSourceManager(),
24                               Context.getLangOpts());
25 }
26 
27 CharSourceRange clang::tooling::maybeExtendRange(CharSourceRange Range,
28                                                  tok::TokenKind Next,
29                                                  ASTContext &Context) {
30   Optional<Token> Tok = Lexer::findNextToken(
31       Range.getEnd(), Context.getSourceManager(), Context.getLangOpts());
32   if (!Tok || !Tok->is(Next))
33     return Range;
34   return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation());
35 }
36 
37 llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
38                                               const SourceManager &SM) {
39   if (Range.isInvalid())
40     return llvm::make_error<StringError>(errc::invalid_argument,
41                                          "Invalid range");
42 
43   if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
44     return llvm::make_error<StringError>(
45         errc::invalid_argument, "Range starts or ends in a macro expansion");
46 
47   if (SM.isInSystemHeader(Range.getBegin()) ||
48       SM.isInSystemHeader(Range.getEnd()))
49     return llvm::make_error<StringError>(errc::invalid_argument,
50                                          "Range is in system header");
51 
52   std::pair<FileID, unsigned> BeginInfo = SM.getDecomposedLoc(Range.getBegin());
53   std::pair<FileID, unsigned> EndInfo = SM.getDecomposedLoc(Range.getEnd());
54   if (BeginInfo.first != EndInfo.first)
55     return llvm::make_error<StringError>(
56         errc::invalid_argument, "Range begins and ends in different files");
57 
58   if (BeginInfo.second > EndInfo.second)
59     return llvm::make_error<StringError>(
60         errc::invalid_argument, "Range's begin is past its end");
61 
62   return llvm::Error::success();
63 }
64 
65 llvm::Optional<CharSourceRange>
66 clang::tooling::getRangeForEdit(const CharSourceRange &EditRange,
67                                 const SourceManager &SM,
68                                 const LangOptions &LangOpts) {
69   // FIXME: makeFileCharRange() has the disadvantage of stripping off "identity"
70   // macros. For example, if we're looking to rewrite the int literal 3 to 6,
71   // and we have the following definition:
72   //    #define DO_NOTHING(x) x
73   // then
74   //    foo(DO_NOTHING(3))
75   // will be rewritten to
76   //    foo(6)
77   // rather than the arguably better
78   //    foo(DO_NOTHING(6))
79   // Decide whether the current behavior is desirable and modify if not.
80   CharSourceRange Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
81   bool IsInvalid = llvm::errorToBool(validateEditRange(Range, SM));
82   if (IsInvalid)
83     return llvm::None;
84   return Range;
85 
86 }
87