1 //===--- Attributes.cpp ---------------------------------------------------===// 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 implements the AttributeCommonInfo interface. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/Basic/Attributes.h" 14 #include "clang/Basic/AttrSubjectMatchRules.h" 15 #include "clang/Basic/IdentifierTable.h" 16 #include "clang/Basic/LangOptions.h" 17 #include "clang/Basic/ParsedAttrInfo.h" 18 #include "clang/Basic/SimpleTypoCorrection.h" 19 #include "clang/Basic/TargetInfo.h" 20 21 #include "llvm/ADT/StringSwitch.h" 22 23 using namespace clang; 24 25 static StringRef canonicalizeScopeName(StringRef Name) { 26 // Normalize the scope name, but only for gnu and clang attributes. 27 if (Name == "__gnu__") 28 return "gnu"; 29 30 if (Name == "_Clang") 31 return "clang"; 32 33 return Name; 34 } 35 36 static StringRef canonicalizeAttrName(StringRef Name) { 37 // Normalize the attribute name, __foo__ becomes foo. 38 if (Name.size() >= 4 && Name.starts_with("__") && Name.ends_with("__")) 39 return Name.substr(2, Name.size() - 4); 40 41 return Name; 42 } 43 44 static int hasAttributeImpl(AttributeCommonInfo::Syntax Syntax, StringRef Name, 45 StringRef ScopeName, const TargetInfo &Target, 46 const LangOptions &LangOpts) { 47 #include "clang/Basic/AttrHasAttributeImpl.inc" 48 return 0; 49 } 50 51 int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, StringRef ScopeName, 52 StringRef Name, const TargetInfo &Target, 53 const LangOptions &LangOpts, bool CheckPlugins) { 54 ScopeName = canonicalizeScopeName(ScopeName); 55 Name = canonicalizeAttrName(Name); 56 57 // As a special case, look for the omp::sequence and omp::directive 58 // attributes. We support those, but not through the typical attribute 59 // machinery that goes through TableGen. We support this in all OpenMP modes 60 // so long as double square brackets are enabled. 61 // 62 // Other OpenMP attributes (e.g. [[omp::assume]]) are handled via the 63 // regular attribute parsing machinery. 64 if (LangOpts.OpenMP && ScopeName == "omp" && 65 (Name == "directive" || Name == "sequence")) 66 return 1; 67 68 int res = hasAttributeImpl(Syntax, Name, ScopeName, Target, LangOpts); 69 if (res) 70 return res; 71 72 if (CheckPlugins) { 73 // Check if any plugin provides this attribute. 74 for (auto &Ptr : getAttributePluginInstances()) 75 if (Ptr->hasSpelling(Syntax, Name)) 76 return 1; 77 } 78 79 return 0; 80 } 81 82 int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, 83 const IdentifierInfo *Scope, const IdentifierInfo *Attr, 84 const TargetInfo &Target, const LangOptions &LangOpts, 85 bool CheckPlugins) { 86 return hasAttribute(Syntax, Scope ? Scope->getName() : "", Attr->getName(), 87 Target, LangOpts, CheckPlugins); 88 } 89 90 int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, 91 const IdentifierInfo *Scope, const IdentifierInfo *Attr, 92 const TargetInfo &Target, const LangOptions &LangOpts) { 93 return hasAttribute(Syntax, Scope, Attr, Target, LangOpts, 94 /*CheckPlugins=*/true); 95 } 96 97 const char *attr::getSubjectMatchRuleSpelling(attr::SubjectMatchRule Rule) { 98 switch (Rule) { 99 #define ATTR_MATCH_RULE(NAME, SPELLING, IsAbstract) \ 100 case attr::NAME: \ 101 return SPELLING; 102 #include "clang/Basic/AttrSubMatchRulesList.inc" 103 } 104 llvm_unreachable("Invalid subject match rule"); 105 } 106 107 static StringRef 108 normalizeAttrScopeName(StringRef ScopeName, 109 AttributeCommonInfo::Syntax SyntaxUsed) { 110 if (SyntaxUsed == AttributeCommonInfo::AS_CXX11 || 111 SyntaxUsed == AttributeCommonInfo::AS_C23) 112 return canonicalizeScopeName(ScopeName); 113 114 return ScopeName; 115 } 116 117 static StringRef 118 normalizeAttrScopeName(const IdentifierInfo *ScopeName, 119 AttributeCommonInfo::Syntax SyntaxUsed) { 120 if (ScopeName) 121 return normalizeAttrScopeName(ScopeName->getName(), SyntaxUsed); 122 return ""; 123 } 124 125 static StringRef normalizeAttrName(StringRef AttrName, 126 StringRef NormalizedScopeName, 127 AttributeCommonInfo::Syntax SyntaxUsed) { 128 // Normalize the attribute name, __foo__ becomes foo. This is only allowable 129 // for GNU attributes, and attributes using the double square bracket syntax. 130 bool ShouldNormalize = 131 SyntaxUsed == AttributeCommonInfo::AS_GNU || 132 ((SyntaxUsed == AttributeCommonInfo::AS_CXX11 || 133 SyntaxUsed == AttributeCommonInfo::AS_C23) && 134 (NormalizedScopeName.empty() || NormalizedScopeName == "gnu" || 135 NormalizedScopeName == "clang")); 136 137 if (ShouldNormalize) 138 return canonicalizeAttrName(AttrName); 139 140 return AttrName; 141 } 142 143 StringRef AttributeCommonInfo::getNormalizedScopeName() const { 144 return normalizeAttrScopeName(getScopeName(), getSyntax()); 145 } 146 147 StringRef 148 AttributeCommonInfo::getNormalizedAttrName(StringRef ScopeName) const { 149 return normalizeAttrName(getAttrName()->getName(), ScopeName, getSyntax()); 150 } 151 152 bool AttributeCommonInfo::isGNUScope() const { 153 return AttrScope.isValid() && (AttrScope.getName()->isStr("gnu") || 154 AttrScope.getName()->isStr("__gnu__")); 155 } 156 157 bool AttributeCommonInfo::isClangScope() const { 158 return AttrScope.isValid() && (AttrScope.getName()->isStr("clang") || 159 AttrScope.getName()->isStr("_Clang")); 160 } 161 162 #include "clang/Sema/AttrParsedAttrKinds.inc" 163 164 static SmallString<64> normalizeName(StringRef AttrName, StringRef ScopeName, 165 AttributeCommonInfo::Syntax SyntaxUsed) { 166 std::string StrAttrName = SyntaxUsed == AttributeCommonInfo::AS_HLSLAnnotation 167 ? AttrName.lower() 168 : AttrName.str(); 169 SmallString<64> FullName = ScopeName; 170 if (!ScopeName.empty()) { 171 assert(SyntaxUsed == AttributeCommonInfo::AS_CXX11 || 172 SyntaxUsed == AttributeCommonInfo::AS_C23); 173 FullName += "::"; 174 } 175 FullName += StrAttrName; 176 return FullName; 177 } 178 179 static SmallString<64> normalizeName(const IdentifierInfo *Name, 180 const IdentifierInfo *Scope, 181 AttributeCommonInfo::Syntax SyntaxUsed) { 182 StringRef ScopeName = normalizeAttrScopeName(Scope, SyntaxUsed); 183 StringRef AttrName = 184 normalizeAttrName(Name->getName(), ScopeName, SyntaxUsed); 185 return normalizeName(AttrName, ScopeName, SyntaxUsed); 186 } 187 188 AttributeCommonInfo::Kind 189 AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name, 190 const IdentifierInfo *ScopeName, 191 Syntax SyntaxUsed) { 192 return ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed); 193 } 194 195 AttributeCommonInfo::AttrArgsInfo 196 AttributeCommonInfo::getCXX11AttrArgsInfo(const IdentifierInfo *Name) { 197 StringRef AttrName = normalizeAttrName( 198 Name->getName(), /*NormalizedScopeName*/ "", Syntax::AS_CXX11); 199 #define CXX11_ATTR_ARGS_INFO 200 return llvm::StringSwitch<AttributeCommonInfo::AttrArgsInfo>(AttrName) 201 #include "clang/Basic/CXX11AttributeInfo.inc" 202 .Default(AttributeCommonInfo::AttrArgsInfo::None); 203 #undef CXX11_ATTR_ARGS_INFO 204 } 205 206 std::string AttributeCommonInfo::getNormalizedFullName() const { 207 return static_cast<std::string>( 208 normalizeName(getAttrName(), getScopeName(), getSyntax())); 209 } 210 211 std::string 212 AttributeCommonInfo::getNormalizedFullName(StringRef ScopeName, 213 StringRef AttrName) const { 214 return static_cast<std::string>( 215 normalizeName(AttrName, ScopeName, getSyntax())); 216 } 217 218 SourceRange AttributeCommonInfo::getNormalizedRange() const { 219 return hasScope() ? SourceRange(AttrScope.getNameLoc(), AttrRange.getEnd()) 220 : AttrRange; 221 } 222 223 static AttributeCommonInfo::Scope 224 getScopeFromNormalizedScopeName(StringRef ScopeName) { 225 return llvm::StringSwitch<AttributeCommonInfo::Scope>(ScopeName) 226 .Case("", AttributeCommonInfo::Scope::NONE) 227 .Case("clang", AttributeCommonInfo::Scope::CLANG) 228 .Case("gnu", AttributeCommonInfo::Scope::GNU) 229 .Case("gsl", AttributeCommonInfo::Scope::GSL) 230 .Case("hlsl", AttributeCommonInfo::Scope::HLSL) 231 .Case("vk", AttributeCommonInfo::Scope::VK) 232 .Case("msvc", AttributeCommonInfo::Scope::MSVC) 233 .Case("omp", AttributeCommonInfo::Scope::OMP) 234 .Case("riscv", AttributeCommonInfo::Scope::RISCV); 235 } 236 237 unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const { 238 // Both variables will be used in tablegen generated 239 // attribute spell list index matching code. 240 auto Syntax = static_cast<AttributeCommonInfo::Syntax>(getSyntax()); 241 StringRef ScopeName = normalizeAttrScopeName(getScopeName(), Syntax); 242 StringRef Name = 243 normalizeAttrName(getAttrName()->getName(), ScopeName, Syntax); 244 AttributeCommonInfo::Scope ComputedScope = 245 getScopeFromNormalizedScopeName(ScopeName); 246 247 #include "clang/Sema/AttrSpellingListIndex.inc" 248 } 249 250 #define ATTR_NAME(NAME) NAME, 251 static constexpr const char *AttrSpellingList[] = { 252 #include "clang/Basic/AttributeSpellingList.inc" 253 }; 254 255 #define ATTR_SCOPE_NAME(SCOPE_NAME) SCOPE_NAME, 256 static constexpr const char *AttrScopeSpellingList[] = { 257 #include "clang/Basic/AttributeSpellingList.inc" 258 }; 259 260 std::optional<StringRef> 261 AttributeCommonInfo::tryGetCorrectedScopeName(StringRef ScopeName) const { 262 if (ScopeName.size() > 0 && 263 !llvm::is_contained(AttrScopeSpellingList, ScopeName)) { 264 SimpleTypoCorrection STC(ScopeName); 265 for (const auto &Scope : AttrScopeSpellingList) 266 STC.add(Scope); 267 268 if (auto CorrectedScopeName = STC.getCorrection()) 269 return CorrectedScopeName; 270 } 271 return std::nullopt; 272 } 273 274 std::optional<StringRef> AttributeCommonInfo::tryGetCorrectedAttrName( 275 StringRef ScopeName, StringRef AttrName, const TargetInfo &Target, 276 const LangOptions &LangOpts) const { 277 if (!llvm::is_contained(AttrSpellingList, AttrName)) { 278 SimpleTypoCorrection STC(AttrName); 279 for (const auto &Attr : AttrSpellingList) 280 STC.add(Attr); 281 282 if (auto CorrectedAttrName = STC.getCorrection()) { 283 if (hasAttribute(getSyntax(), ScopeName, *CorrectedAttrName, Target, 284 LangOpts, 285 /*CheckPlugins=*/true)) 286 return CorrectedAttrName; 287 } 288 } 289 return std::nullopt; 290 } 291