10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
14#include "../utils/LexerUtils.h"
24 TemplateSpecializationTypeLoc
Loc;
30 auto It =
Node.redecls_begin();
31 auto EndIt =
Node.redecls_end();
45 unless(isExpansionInSystemHeader()),
46 has(functionDecl(unless(hasOtherDeclarations()), isDefinition(),
47 hasReturnTypeLoc(typeLoc().bind(
"return")))
49 .bind(
"functionTemplate"),
53static std::optional<TemplateSpecializationTypeLoc>
55 if (
const auto Dep = TheType.getAs<DependentNameTypeLoc>()) {
56 const IdentifierInfo *Identifier = Dep.getTypePtr()->getIdentifier();
57 if (!Identifier || Identifier->getName() !=
"type" ||
58 Dep.getTypePtr()->getKeyword() != ElaboratedTypeKeyword::Typename) {
61 TheType = Dep.getQualifierLoc().getTypeLoc();
66 if (
const auto SpecializationLoc =
67 TheType.getAs<TemplateSpecializationTypeLoc>()) {
69 const auto *Specialization =
70 dyn_cast<TemplateSpecializationType>(SpecializationLoc.getTypePtr());
74 const TemplateDecl *TD =
75 Specialization->getTemplateName().getAsTemplateDecl();
76 if (!TD || TD->getName() !=
"enable_if")
79 int NumArgs = SpecializationLoc.getNumArgs();
80 if (NumArgs != 1 && NumArgs != 2)
83 return SpecializationLoc;
88static std::optional<TemplateSpecializationTypeLoc>
90 if (
const auto Elaborated = TheType.getAs<ElaboratedTypeLoc>())
91 TheType = Elaborated.getNamedTypeLoc();
93 if (
const auto SpecializationLoc =
94 TheType.getAs<TemplateSpecializationTypeLoc>()) {
96 const auto *Specialization =
97 dyn_cast<TemplateSpecializationType>(SpecializationLoc.getTypePtr());
101 const TemplateDecl *TD =
102 Specialization->getTemplateName().getAsTemplateDecl();
103 if (!TD || TD->getName() !=
"enable_if_t")
106 if (!Specialization->isTypeAlias())
109 if (
const auto *AliasedType =
110 dyn_cast<DependentNameType>(Specialization->getAliasedType())) {
111 if (AliasedType->getIdentifier()->getName() !=
"type" ||
112 AliasedType->getKeyword() != ElaboratedTypeKeyword::Typename) {
118 int NumArgs = SpecializationLoc.getNumArgs();
119 if (NumArgs != 1 && NumArgs != 2)
122 return SpecializationLoc;
127static std::optional<TemplateSpecializationTypeLoc>
134static std::optional<EnableIfData>
136 if (
const auto Pointer = TheType.getAs<PointerTypeLoc>())
137 TheType = Pointer.getPointeeLoc();
138 else if (
const auto Reference = TheType.getAs<ReferenceTypeLoc>())
139 TheType = Reference.getPointeeLoc();
140 if (
const auto Qualified = TheType.getAs<QualifiedTypeLoc>())
141 TheType = Qualified.getUnqualifiedLoc();
148static std::pair<std::optional<EnableIfData>,
const Decl *>
158 const TemplateParameterList *TemplateParams =
159 FunctionTemplate->getTemplateParameters();
160 if (TemplateParams->size() == 0)
163 const NamedDecl *LastParam =
164 TemplateParams->getParam(TemplateParams->size() - 1);
165 if (
const auto *LastTemplateParam =
166 dyn_cast<NonTypeTemplateParmDecl>(LastParam)) {
168 if (!LastTemplateParam->hasDefaultArgument() ||
169 !LastTemplateParam->getName().empty())
173 LastTemplateParam->getTypeSourceInfo()->getTypeLoc()),
176 if (
const auto *LastTemplateParam =
177 dyn_cast<TemplateTypeParmDecl>(LastParam)) {
178 if (LastTemplateParam->hasDefaultArgument() &&
179 LastTemplateParam->getIdentifier() ==
nullptr) {
196 return SM.getFileLoc(Element.getRAngleLoc());
201 const TemplateSpecializationTypeLoc &EnableIf) {
205 const LangOptions &LangOpts = Context.getLangOpts();
206 const SourceManager &SM = Context.getSourceManager();
207 if (EnableIf.getNumArgs() > 1) {
208 TemplateArgumentLoc NextArg = EnableIf.getArgLoc(1);
209 return {EnableIf.getLAngleLoc().getLocWithOffset(1),
211 NextArg.getSourceRange().getBegin(), SM, LangOpts, tok::comma)};
214 return {EnableIf.getLAngleLoc().getLocWithOffset(1),
219 const TemplateSpecializationTypeLoc &EnableIf) {
220 TemplateArgumentLoc Arg = EnableIf.getArgLoc(1);
221 const LangOptions &LangOpts = Context.getLangOpts();
222 const SourceManager &SM = Context.getSourceManager();
224 SM, LangOpts, tok::comma)
225 .getLocWithOffset(1),
232static std::optional<StringRef>
234 const TemplateSpecializationTypeLoc &EnableIf) {
235 if (EnableIf.getNumArgs() > 1) {
236 const LangOptions &LangOpts = Context.getLangOpts();
237 const SourceManager &SM = Context.getSourceManager();
238 bool Invalid =
false;
239 StringRef
Text = Lexer::getSourceText(CharSourceRange::getCharRange(
241 SM, LangOpts, &Invalid)
252static std::optional<SourceLocation>
254 SourceManager &SM = Context.getSourceManager();
255 const LangOptions &LangOpts = Context.getLangOpts();
257 if (
const auto *Constructor = dyn_cast<CXXConstructorDecl>(Function)) {
258 for (
const CXXCtorInitializer *Init : Constructor->inits()) {
259 if (Init->getSourceOrder() == 0)
261 SM, LangOpts, tok::colon);
263 if (!Constructor->inits().empty())
266 if (Function->isDeleted()) {
267 SourceLocation FunctionEnd = Function->getSourceRange().getEnd();
269 tok::equal, tok::equal);
271 const Stmt *Body = Function->getBody();
275 return Body->getBeginLoc();
284 if (
const auto *Cast = dyn_cast<ImplicitCastExpr>(Expression))
285 Expression = Cast->getSubExprAsWritten();
286 if (isa<ParenExpr, DependentScopeDeclRefExpr>(Expression))
297 SourceRange ConditionRange,
298 ASTContext &Context) {
299 SourceManager &SM = Context.getSourceManager();
300 const LangOptions &LangOpts = Context.getLangOpts();
302 SourceLocation PrevTokenLoc = ConditionRange.getEnd();
303 if (PrevTokenLoc.isInvalid())
306 const bool SkipComments =
false;
309 PrevTokenLoc, SM, LangOpts, SkipComments);
310 bool EndsWithDoubleSlash =
311 PrevToken.is(tok::comment) &&
312 Lexer::getSourceText(CharSourceRange::getCharRange(
313 PrevTokenLoc, PrevTokenLoc.getLocWithOffset(2)),
314 SM, LangOpts) ==
"//";
316 bool Invalid =
false;
317 llvm::StringRef ConditionText = Lexer::getSourceText(
318 CharSourceRange::getCharRange(ConditionRange), SM, LangOpts, &Invalid);
322 auto AddParens = [&](llvm::StringRef
Text) -> std::string {
325 return "(" +
Text.str() +
")";
328 if (EndsWithDoubleSlash)
329 return AddParens(ConditionText);
330 return AddParens(ConditionText.trim());
345 ASTContext &Context) {
346 TemplateArgumentLoc EnableCondition = EnableIf.
Loc.getArgLoc(0);
351 EnableCondition.getSourceExpression(), ConditionRange, Context);
355 std::optional<StringRef> TypeText =
getTypeText(Context, EnableIf.
Loc);
359 SmallVector<const Expr *, 3> ExistingConstraints;
360 Function->getAssociatedConstraints(ExistingConstraints);
361 if (!ExistingConstraints.empty()) {
367 std::optional<SourceLocation> ConstraintInsertionLoc =
369 if (!ConstraintInsertionLoc)
372 std::vector<FixItHint> FixIts;
373 FixIts.push_back(FixItHint::CreateReplacement(
374 CharSourceRange::getTokenRange(EnableIf.
Outer.getSourceRange()),
376 FixIts.push_back(FixItHint::CreateInsertion(
377 *ConstraintInsertionLoc,
"requires " + *ConditionText +
" "));
390static std::vector<FixItHint>
392 const FunctionDecl *Function,
393 const Decl *LastTemplateParam,
395 SourceManager &SM = Context.getSourceManager();
396 const LangOptions &LangOpts = Context.getLangOpts();
398 TemplateArgumentLoc EnableCondition = EnableIf.
Loc.getArgLoc(0);
403 EnableCondition.getSourceExpression(), ConditionRange, Context);
407 SmallVector<const Expr *, 3> ExistingConstraints;
408 Function->getAssociatedConstraints(ExistingConstraints);
409 if (!ExistingConstraints.empty()) {
415 SourceRange RemovalRange;
416 const TemplateParameterList *TemplateParams =
417 FunctionTemplate->getTemplateParameters();
418 if (!TemplateParams || TemplateParams->size() == 0)
421 if (TemplateParams->size() == 1) {
423 SourceRange(TemplateParams->getTemplateLoc(),
428 LastTemplateParam->getSourceRange().getBegin(), SM,
429 LangOpts, tok::comma),
433 std::optional<SourceLocation> ConstraintInsertionLoc =
435 if (!ConstraintInsertionLoc)
438 std::vector<FixItHint> FixIts;
440 FixItHint::CreateRemoval(CharSourceRange::getCharRange(RemovalRange)));
441 FixIts.push_back(FixItHint::CreateInsertion(
442 *ConstraintInsertionLoc,
"requires " + *ConditionText +
" "));
447 const auto *FunctionTemplate =
448 Result.Nodes.getNodeAs<FunctionTemplateDecl>(
"functionTemplate");
449 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(
"function");
450 const auto *
ReturnType = Result.Nodes.getNodeAs<TypeLoc>(
"return");
451 if (!FunctionTemplate || !Function || !
ReturnType)
475 "use C++20 requires constraints instead of enable_if")
481 if (
auto [EnableIf, LastTemplateParam] =
483 EnableIf && LastTemplateParam) {
484 diag(LastTemplateParam->getSourceRange().getBegin(),
485 "use C++20 requires constraints instead of enable_if")
487 LastTemplateParam, *EnableIf,
const FunctionDecl * Decl
::clang::DynTypedNode Node
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
static std::pair< std::optional< EnableIfData >, const Decl * > matchTrailingTemplateParam(const FunctionTemplateDecl *FunctionTemplate)
static std::vector< FixItHint > handleReturnType(const FunctionDecl *Function, const TypeLoc &ReturnType, const EnableIfData &EnableIf, ASTContext &Context)
static SourceRange getTypeRange(ASTContext &Context, const TemplateSpecializationTypeLoc &EnableIf)
static std::optional< SourceLocation > findInsertionForConstraint(const FunctionDecl *Function, ASTContext &Context)
static std::optional< TemplateSpecializationTypeLoc > matchEnableIfSpecializationImplTypename(TypeLoc TheType)
static std::optional< std::string > getConditionText(const Expr *ConditionExpr, SourceRange ConditionRange, ASTContext &Context)
static std::vector< FixItHint > handleTrailingTemplateType(const FunctionTemplateDecl *FunctionTemplate, const FunctionDecl *Function, const Decl *LastTemplateParam, const EnableIfData &EnableIf, ASTContext &Context)
static std::optional< TemplateSpecializationTypeLoc > matchEnableIfSpecializationImplTrait(TypeLoc TheType)
static std::optional< StringRef > getTypeText(ASTContext &Context, const TemplateSpecializationTypeLoc &EnableIf)
static SourceRange getConditionRange(ASTContext &Context, const TemplateSpecializationTypeLoc &EnableIf)
bool isPrimaryExpression(const Expr *Expression)
static SourceLocation getRAngleFileLoc(const SourceManager &SM, const T &Element)
static std::optional< TemplateSpecializationTypeLoc > matchEnableIfSpecializationImpl(TypeLoc TheType)
static std::optional< EnableIfData > matchEnableIfSpecialization(TypeLoc TheType)
std::pair< Token, SourceLocation > getPreviousTokenAndStart(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
SourceLocation findNextAnyTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, TokenKind TK, TokenKinds... TKs)
SourceLocation findPreviousTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, tok::TokenKind TK)
TemplateSpecializationTypeLoc Loc