1
2//===--- CommandLineSourceLoc.h - Parsing for source locations-*- C++ -*---===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// Command line parsing for source locations.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_CLANG_FRONTEND_COMMANDLINESOURCELOC_H
15#define LLVM_CLANG_FRONTEND_COMMANDLINESOURCELOC_H
16
17#include "clang/Basic/LLVM.h"
18#include "llvm/Support/CommandLine.h"
19#include "llvm/Support/raw_ostream.h"
20#include <optional>
21
22namespace clang {
23
24/// A source location that has been parsed on the command line.
25struct ParsedSourceLocation {
26  std::string FileName;
27  unsigned Line;
28  unsigned Column;
29
30public:
31  /// Construct a parsed source location from a string; the Filename is empty on
32  /// error.
33  static ParsedSourceLocation FromString(StringRef Str) {
34    ParsedSourceLocation PSL;
35    std::pair<StringRef, StringRef> ColSplit = Str.rsplit(':');
36    std::pair<StringRef, StringRef> LineSplit =
37      ColSplit.first.rsplit(':');
38
39    // If both tail splits were valid integers, return success.
40    if (!ColSplit.second.getAsInteger(10, PSL.Column) &&
41        !LineSplit.second.getAsInteger(10, PSL.Line)) {
42      PSL.FileName = std::string(LineSplit.first);
43
44      // On the command-line, stdin may be specified via "-". Inside the
45      // compiler, stdin is called "<stdin>".
46      if (PSL.FileName == "-")
47        PSL.FileName = "<stdin>";
48    }
49
50    return PSL;
51  }
52
53  /// Serialize ParsedSourceLocation back to a string.
54  std::string ToString() const {
55    return (llvm::Twine(FileName == "<stdin>" ? "-" : FileName) + ":" +
56            Twine(Line) + ":" + Twine(Column))
57        .str();
58  }
59};
60
61/// A source range that has been parsed on the command line.
62struct ParsedSourceRange {
63  std::string FileName;
64  /// The starting location of the range. The first element is the line and
65  /// the second element is the column.
66  std::pair<unsigned, unsigned> Begin;
67  /// The ending location of the range. The first element is the line and the
68  /// second element is the column.
69  std::pair<unsigned, unsigned> End;
70
71  /// Returns a parsed source range from a string or std::nullopt if the string
72  /// is invalid.
73  ///
74  /// These source string has the following format:
75  ///
76  /// file:start_line:start_column[-end_line:end_column]
77  ///
78  /// If the end line and column are omitted, the starting line and columns
79  /// are used as the end values.
80  static std::optional<ParsedSourceRange> fromString(StringRef Str) {
81    std::pair<StringRef, StringRef> RangeSplit = Str.rsplit('-');
82    unsigned EndLine, EndColumn;
83    bool HasEndLoc = false;
84    if (!RangeSplit.second.empty()) {
85      std::pair<StringRef, StringRef> Split = RangeSplit.second.rsplit(':');
86      if (Split.first.getAsInteger(10, EndLine) ||
87          Split.second.getAsInteger(10, EndColumn)) {
88        // The string does not end in end_line:end_column, so the '-'
89        // probably belongs to the filename which menas the whole
90        // string should be parsed.
91        RangeSplit.first = Str;
92      } else
93        HasEndLoc = true;
94    }
95    auto Begin = ParsedSourceLocation::FromString(RangeSplit.first);
96    if (Begin.FileName.empty())
97      return std::nullopt;
98    if (!HasEndLoc) {
99      EndLine = Begin.Line;
100      EndColumn = Begin.Column;
101    }
102    return ParsedSourceRange{std::move(Begin.FileName),
103                             {Begin.Line, Begin.Column},
104                             {EndLine, EndColumn}};
105  }
106};
107}
108
109namespace llvm {
110  namespace cl {
111    /// Command-line option parser that parses source locations.
112    ///
113    /// Source locations are of the form filename:line:column.
114    template<>
115    class parser<clang::ParsedSourceLocation> final
116      : public basic_parser<clang::ParsedSourceLocation> {
117    public:
118      inline bool parse(Option &O, StringRef ArgName, StringRef ArgValue,
119                 clang::ParsedSourceLocation &Val);
120    };
121
122    bool
123    parser<clang::ParsedSourceLocation>::
124    parse(Option &O, StringRef ArgName, StringRef ArgValue,
125          clang::ParsedSourceLocation &Val) {
126      using namespace clang;
127
128      Val = ParsedSourceLocation::FromString(ArgValue);
129      if (Val.FileName.empty()) {
130        errs() << "error: "
131               << "source location must be of the form filename:line:column\n";
132        return true;
133      }
134
135      return false;
136    }
137  }
138}
139
140#endif
141