aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Basic/Attributes.cpp
blob: 81b186f844b8a531b4184d5130f740628b0c9176 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
//===--- Attributes.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the AttributeCommonInfo interface.
//
//===----------------------------------------------------------------------===//

#include "clang/Basic/Attributes.h"
#include "clang/Basic/AttrSubjectMatchRules.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/ParsedAttrInfo.h"
#include "clang/Basic/SimpleTypoCorrection.h"
#include "clang/Basic/TargetInfo.h"

#include "llvm/ADT/StringSwitch.h"

using namespace clang;

static StringRef canonicalizeScopeName(StringRef Name) {
  // Normalize the scope name, but only for gnu and clang attributes.
  if (Name == "__gnu__")
    return "gnu";

  if (Name == "_Clang")
    return "clang";

  return Name;
}

static StringRef canonicalizeAttrName(StringRef Name) {
  // Normalize the attribute name, __foo__ becomes foo.
  if (Name.size() >= 4 && Name.starts_with("__") && Name.ends_with("__"))
    return Name.substr(2, Name.size() - 4);

  return Name;
}

static int hasAttributeImpl(AttributeCommonInfo::Syntax Syntax, StringRef Name,
                            StringRef ScopeName, const TargetInfo &Target,
                            const LangOptions &LangOpts) {
#include "clang/Basic/AttrHasAttributeImpl.inc"
  return 0;
}

int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, StringRef ScopeName,
                        StringRef Name, const TargetInfo &Target,
                        const LangOptions &LangOpts, bool CheckPlugins) {
  ScopeName = canonicalizeScopeName(ScopeName);
  Name = canonicalizeAttrName(Name);

  // As a special case, look for the omp::sequence and omp::directive
  // attributes. We support those, but not through the typical attribute
  // machinery that goes through TableGen. We support this in all OpenMP modes
  // so long as double square brackets are enabled.
  //
  // Other OpenMP attributes (e.g. [[omp::assume]]) are handled via the
  // regular attribute parsing machinery.
  if (LangOpts.OpenMP && ScopeName == "omp" &&
      (Name == "directive" || Name == "sequence"))
    return 1;

  int res = hasAttributeImpl(Syntax, Name, ScopeName, Target, LangOpts);
  if (res)
    return res;

  if (CheckPlugins) {
    // Check if any plugin provides this attribute.
    for (auto &Ptr : getAttributePluginInstances())
      if (Ptr->hasSpelling(Syntax, Name))
        return 1;
  }

  return 0;
}

int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax,
                        const IdentifierInfo *Scope, const IdentifierInfo *Attr,
                        const TargetInfo &Target, const LangOptions &LangOpts,
                        bool CheckPlugins) {
  return hasAttribute(Syntax, Scope ? Scope->getName() : "", Attr->getName(),
                      Target, LangOpts, CheckPlugins);
}

int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax,
                        const IdentifierInfo *Scope, const IdentifierInfo *Attr,
                        const TargetInfo &Target, const LangOptions &LangOpts) {
  return hasAttribute(Syntax, Scope, Attr, Target, LangOpts,
                      /*CheckPlugins=*/true);
}

const char *attr::getSubjectMatchRuleSpelling(attr::SubjectMatchRule Rule) {
  switch (Rule) {
#define ATTR_MATCH_RULE(NAME, SPELLING, IsAbstract)                            \
  case attr::NAME:                                                             \
    return SPELLING;
#include "clang/Basic/AttrSubMatchRulesList.inc"
  }
  llvm_unreachable("Invalid subject match rule");
}

static StringRef
normalizeAttrScopeName(StringRef ScopeName,
                       AttributeCommonInfo::Syntax SyntaxUsed) {
  if (SyntaxUsed == AttributeCommonInfo::AS_CXX11 ||
      SyntaxUsed == AttributeCommonInfo::AS_C23)
    return canonicalizeScopeName(ScopeName);

  return ScopeName;
}

static StringRef
normalizeAttrScopeName(const IdentifierInfo *ScopeName,
                       AttributeCommonInfo::Syntax SyntaxUsed) {
  if (ScopeName)
    return normalizeAttrScopeName(ScopeName->getName(), SyntaxUsed);
  return "";
}

static StringRef normalizeAttrName(StringRef AttrName,
                                   StringRef NormalizedScopeName,
                                   AttributeCommonInfo::Syntax SyntaxUsed) {
  // Normalize the attribute name, __foo__ becomes foo. This is only allowable
  // for GNU attributes, and attributes using the double square bracket syntax.
  bool ShouldNormalize =
      SyntaxUsed == AttributeCommonInfo::AS_GNU ||
      ((SyntaxUsed == AttributeCommonInfo::AS_CXX11 ||
        SyntaxUsed == AttributeCommonInfo::AS_C23) &&
       (NormalizedScopeName.empty() || NormalizedScopeName == "gnu" ||
        NormalizedScopeName == "clang"));

  if (ShouldNormalize)
    return canonicalizeAttrName(AttrName);

  return AttrName;
}

StringRef AttributeCommonInfo::getNormalizedScopeName() const {
  return normalizeAttrScopeName(getScopeName(), getSyntax());
}

StringRef
AttributeCommonInfo::getNormalizedAttrName(StringRef ScopeName) const {
  return normalizeAttrName(getAttrName()->getName(), ScopeName, getSyntax());
}

bool AttributeCommonInfo::isGNUScope() const {
  return AttrScope.isValid() && (AttrScope.getName()->isStr("gnu") ||
                                 AttrScope.getName()->isStr("__gnu__"));
}

bool AttributeCommonInfo::isClangScope() const {
  return AttrScope.isValid() && (AttrScope.getName()->isStr("clang") ||
                                 AttrScope.getName()->isStr("_Clang"));
}

#include "clang/Sema/AttrParsedAttrKinds.inc"

static SmallString<64> normalizeName(StringRef AttrName, StringRef ScopeName,
                                     AttributeCommonInfo::Syntax SyntaxUsed) {
  std::string StrAttrName = SyntaxUsed == AttributeCommonInfo::AS_HLSLAnnotation
                                ? AttrName.lower()
                                : AttrName.str();
  SmallString<64> FullName = ScopeName;
  if (!ScopeName.empty()) {
    assert(SyntaxUsed == AttributeCommonInfo::AS_CXX11 ||
           SyntaxUsed == AttributeCommonInfo::AS_C23);
    FullName += "::";
  }
  FullName += StrAttrName;
  return FullName;
}

static SmallString<64> normalizeName(const IdentifierInfo *Name,
                                     const IdentifierInfo *Scope,
                                     AttributeCommonInfo::Syntax SyntaxUsed) {
  StringRef ScopeName = normalizeAttrScopeName(Scope, SyntaxUsed);
  StringRef AttrName =
      normalizeAttrName(Name->getName(), ScopeName, SyntaxUsed);
  return normalizeName(AttrName, ScopeName, SyntaxUsed);
}

AttributeCommonInfo::Kind
AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name,
                                   const IdentifierInfo *ScopeName,
                                   Syntax SyntaxUsed) {
  return ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed);
}

AttributeCommonInfo::AttrArgsInfo
AttributeCommonInfo::getCXX11AttrArgsInfo(const IdentifierInfo *Name) {
  StringRef AttrName = normalizeAttrName(
      Name->getName(), /*NormalizedScopeName*/ "", Syntax::AS_CXX11);
#define CXX11_ATTR_ARGS_INFO
  return llvm::StringSwitch<AttributeCommonInfo::AttrArgsInfo>(AttrName)
#include "clang/Basic/CXX11AttributeInfo.inc"
      .Default(AttributeCommonInfo::AttrArgsInfo::None);
#undef CXX11_ATTR_ARGS_INFO
}

std::string AttributeCommonInfo::getNormalizedFullName() const {
  return static_cast<std::string>(
      normalizeName(getAttrName(), getScopeName(), getSyntax()));
}

std::string
AttributeCommonInfo::getNormalizedFullName(StringRef ScopeName,
                                           StringRef AttrName) const {
  return static_cast<std::string>(
      normalizeName(AttrName, ScopeName, getSyntax()));
}

SourceRange AttributeCommonInfo::getNormalizedRange() const {
  return hasScope() ? SourceRange(AttrScope.getNameLoc(), AttrRange.getEnd())
                    : AttrRange;
}

static AttributeCommonInfo::Scope
getScopeFromNormalizedScopeName(StringRef ScopeName) {
  return llvm::StringSwitch<AttributeCommonInfo::Scope>(ScopeName)
      .Case("", AttributeCommonInfo::Scope::NONE)
      .Case("clang", AttributeCommonInfo::Scope::CLANG)
      .Case("gnu", AttributeCommonInfo::Scope::GNU)
      .Case("gsl", AttributeCommonInfo::Scope::GSL)
      .Case("hlsl", AttributeCommonInfo::Scope::HLSL)
      .Case("vk", AttributeCommonInfo::Scope::VK)
      .Case("msvc", AttributeCommonInfo::Scope::MSVC)
      .Case("omp", AttributeCommonInfo::Scope::OMP)
      .Case("riscv", AttributeCommonInfo::Scope::RISCV);
}

unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const {
  // Both variables will be used in tablegen generated
  // attribute spell list index matching code.
  auto Syntax = static_cast<AttributeCommonInfo::Syntax>(getSyntax());
  StringRef ScopeName = normalizeAttrScopeName(getScopeName(), Syntax);
  StringRef Name =
      normalizeAttrName(getAttrName()->getName(), ScopeName, Syntax);
  AttributeCommonInfo::Scope ComputedScope =
      getScopeFromNormalizedScopeName(ScopeName);

#include "clang/Sema/AttrSpellingListIndex.inc"
}

#define ATTR_NAME(NAME) NAME,
static constexpr const char *AttrSpellingList[] = {
#include "clang/Basic/AttributeSpellingList.inc"
};

#define ATTR_SCOPE_NAME(SCOPE_NAME) SCOPE_NAME,
static constexpr const char *AttrScopeSpellingList[] = {
#include "clang/Basic/AttributeSpellingList.inc"
};

std::optional<StringRef>
AttributeCommonInfo::tryGetCorrectedScopeName(StringRef ScopeName) const {
  if (ScopeName.size() > 0 &&
      !llvm::is_contained(AttrScopeSpellingList, ScopeName)) {
    SimpleTypoCorrection STC(ScopeName);
    for (const auto &Scope : AttrScopeSpellingList)
      STC.add(Scope);

    if (auto CorrectedScopeName = STC.getCorrection())
      return CorrectedScopeName;
  }
  return std::nullopt;
}

std::optional<StringRef> AttributeCommonInfo::tryGetCorrectedAttrName(
    StringRef ScopeName, StringRef AttrName, const TargetInfo &Target,
    const LangOptions &LangOpts) const {
  if (!llvm::is_contained(AttrSpellingList, AttrName)) {
    SimpleTypoCorrection STC(AttrName);
    for (const auto &Attr : AttrSpellingList)
      STC.add(Attr);

    if (auto CorrectedAttrName = STC.getCorrection()) {
      if (hasAttribute(getSyntax(), ScopeName, *CorrectedAttrName, Target,
                       LangOpts,
                       /*CheckPlugins=*/true))
        return CorrectedAttrName;
    }
  }
  return std::nullopt;
}