1//===--- Marshallers.cpp ----------------------------------------*- 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#include "Marshallers.h"
10#include "llvm/ADT/ArrayRef.h"
11#include "llvm/ADT/Optional.h"
12#include "llvm/ADT/StringRef.h"
13#include "llvm/Support/Regex.h"
14#include <string>
15
16static llvm::Optional<std::string>
17getBestGuess(llvm::StringRef Search, llvm::ArrayRef<llvm::StringRef> Allowed,
18             llvm::StringRef DropPrefix = "", unsigned MaxEditDistance = 3) {
19  if (MaxEditDistance != ~0U)
20    ++MaxEditDistance;
21  llvm::StringRef Res;
22  for (const llvm::StringRef &Item : Allowed) {
23    if (Item.equals_lower(Search)) {
24      assert(!Item.equals(Search) && "This should be handled earlier on.");
25      MaxEditDistance = 1;
26      Res = Item;
27      continue;
28    }
29    unsigned Distance = Item.edit_distance(Search);
30    if (Distance < MaxEditDistance) {
31      MaxEditDistance = Distance;
32      Res = Item;
33    }
34  }
35  if (!Res.empty())
36    return Res.str();
37  if (!DropPrefix.empty()) {
38    --MaxEditDistance; // Treat dropping the prefix as 1 edit
39    for (const llvm::StringRef &Item : Allowed) {
40      auto NoPrefix = Item;
41      if (!NoPrefix.consume_front(DropPrefix))
42        continue;
43      if (NoPrefix.equals_lower(Search)) {
44        if (NoPrefix.equals(Search))
45          return Item.str();
46        MaxEditDistance = 1;
47        Res = Item;
48        continue;
49      }
50      unsigned Distance = NoPrefix.edit_distance(Search);
51      if (Distance < MaxEditDistance) {
52        MaxEditDistance = Distance;
53        Res = Item;
54      }
55    }
56    if (!Res.empty())
57      return Res.str();
58  }
59  return llvm::None;
60}
61
62llvm::Optional<std::string>
63clang::ast_matchers::dynamic::internal::ArgTypeTraits<
64    clang::attr::Kind>::getBestGuess(const VariantValue &Value) {
65  static constexpr llvm::StringRef Allowed[] = {
66#define ATTR(X) "attr::" #X,
67#include "clang/Basic/AttrList.inc"
68  };
69  if (Value.isString())
70    return ::getBestGuess(Value.getString(), llvm::makeArrayRef(Allowed),
71                          "attr::");
72  return llvm::None;
73}
74
75llvm::Optional<std::string>
76clang::ast_matchers::dynamic::internal::ArgTypeTraits<
77    clang::CastKind>::getBestGuess(const VariantValue &Value) {
78  static constexpr llvm::StringRef Allowed[] = {
79#define CAST_OPERATION(Name) "CK_" #Name,
80#include "clang/AST/OperationKinds.def"
81  };
82  if (Value.isString())
83    return ::getBestGuess(Value.getString(), llvm::makeArrayRef(Allowed),
84                          "CK_");
85  return llvm::None;
86}
87
88llvm::Optional<std::string>
89clang::ast_matchers::dynamic::internal::ArgTypeTraits<
90    clang::OpenMPClauseKind>::getBestGuess(const VariantValue &Value) {
91  static constexpr llvm::StringRef Allowed[] = {
92#define GEN_CLANG_CLAUSE_CLASS
93#define CLAUSE_CLASS(Enum, Str, Class) #Enum,
94#include "llvm/Frontend/OpenMP/OMP.inc"
95  };
96  if (Value.isString())
97    return ::getBestGuess(Value.getString(), llvm::makeArrayRef(Allowed),
98                          "OMPC_");
99  return llvm::None;
100}
101
102llvm::Optional<std::string>
103clang::ast_matchers::dynamic::internal::ArgTypeTraits<
104    clang::UnaryExprOrTypeTrait>::getBestGuess(const VariantValue &Value) {
105  static constexpr llvm::StringRef Allowed[] = {
106#define UNARY_EXPR_OR_TYPE_TRAIT(Spelling, Name, Key) "UETT_" #Name,
107#define CXX11_UNARY_EXPR_OR_TYPE_TRAIT(Spelling, Name, Key) "UETT_" #Name,
108#include "clang/Basic/TokenKinds.def"
109  };
110  if (Value.isString())
111    return ::getBestGuess(Value.getString(), llvm::makeArrayRef(Allowed),
112                          "UETT_");
113  return llvm::None;
114}
115
116static constexpr std::pair<llvm::StringRef, llvm::Regex::RegexFlags>
117    RegexMap[] = {
118        {"NoFlags", llvm::Regex::RegexFlags::NoFlags},
119        {"IgnoreCase", llvm::Regex::RegexFlags::IgnoreCase},
120        {"Newline", llvm::Regex::RegexFlags::Newline},
121        {"BasicRegex", llvm::Regex::RegexFlags::BasicRegex},
122};
123
124static llvm::Optional<llvm::Regex::RegexFlags>
125getRegexFlag(llvm::StringRef Flag) {
126  for (const auto &StringFlag : RegexMap) {
127    if (Flag == StringFlag.first)
128      return StringFlag.second;
129  }
130  return llvm::None;
131}
132
133static llvm::Optional<llvm::StringRef>
134getCloseRegexMatch(llvm::StringRef Flag) {
135  for (const auto &StringFlag : RegexMap) {
136    if (Flag.edit_distance(StringFlag.first) < 3)
137      return StringFlag.first;
138  }
139  return llvm::None;
140}
141
142llvm::Optional<llvm::Regex::RegexFlags>
143clang::ast_matchers::dynamic::internal::ArgTypeTraits<
144    llvm::Regex::RegexFlags>::getFlags(llvm::StringRef Flags) {
145  llvm::Optional<llvm::Regex::RegexFlags> Flag;
146  SmallVector<StringRef, 4> Split;
147  Flags.split(Split, '|', -1, false);
148  for (StringRef OrFlag : Split) {
149    if (llvm::Optional<llvm::Regex::RegexFlags> NextFlag =
150            getRegexFlag(OrFlag.trim()))
151      Flag = Flag.getValueOr(llvm::Regex::NoFlags) | *NextFlag;
152    else
153      return None;
154  }
155  return Flag;
156}
157
158llvm::Optional<std::string>
159clang::ast_matchers::dynamic::internal::ArgTypeTraits<
160    llvm::Regex::RegexFlags>::getBestGuess(const VariantValue &Value) {
161  if (!Value.isString())
162    return llvm::None;
163  SmallVector<StringRef, 4> Split;
164  llvm::StringRef(Value.getString()).split(Split, '|', -1, false);
165  for (llvm::StringRef &Flag : Split) {
166    if (llvm::Optional<llvm::StringRef> BestGuess =
167            getCloseRegexMatch(Flag.trim()))
168      Flag = *BestGuess;
169    else
170      return None;
171  }
172  if (Split.empty())
173    return None;
174  return llvm::join(Split, " | ");
175}
176