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();
44 has(functionDecl(unless(hasOtherDeclarations()), isDefinition(),
45 hasReturnTypeLoc(typeLoc().bind(
"return")))
47 .bind(
"functionTemplate"),
51static std::optional<TemplateSpecializationTypeLoc>
53 if (
const auto Dep = TheType.getAs<DependentNameTypeLoc>()) {
54 const IdentifierInfo *Identifier = Dep.getTypePtr()->getIdentifier();
55 if (!Identifier || Identifier->getName() !=
"type" ||
56 Dep.getTypePtr()->getKeyword() != ElaboratedTypeKeyword::Typename) {
59 TheType = Dep.getQualifierLoc().getTypeLoc();
62 if (
const auto SpecializationLoc =
63 TheType.getAs<TemplateSpecializationTypeLoc>()) {
65 const auto *Specialization =
66 dyn_cast<TemplateSpecializationType>(SpecializationLoc.getTypePtr());
70 const TemplateDecl *TD =
71 Specialization->getTemplateName().getAsTemplateDecl();
72 if (!TD || TD->getName() !=
"enable_if")
75 int NumArgs = SpecializationLoc.getNumArgs();
76 if (NumArgs != 1 && NumArgs != 2)
79 return SpecializationLoc;
84static std::optional<TemplateSpecializationTypeLoc>
86 if (
const auto Elaborated = TheType.getAs<ElaboratedTypeLoc>())
87 TheType = Elaborated.getNamedTypeLoc();
89 if (
const auto SpecializationLoc =
90 TheType.getAs<TemplateSpecializationTypeLoc>()) {
92 const auto *Specialization =
93 dyn_cast<TemplateSpecializationType>(SpecializationLoc.getTypePtr());
97 const TemplateDecl *TD =
98 Specialization->getTemplateName().getAsTemplateDecl();
99 if (!TD || TD->getName() !=
"enable_if_t")
102 if (!Specialization->isTypeAlias())
105 if (
const auto *AliasedType =
106 dyn_cast<DependentNameType>(Specialization->getAliasedType())) {
107 if (AliasedType->getIdentifier()->getName() !=
"type" ||
108 AliasedType->getKeyword() != ElaboratedTypeKeyword::Typename) {
114 int NumArgs = SpecializationLoc.getNumArgs();
115 if (NumArgs != 1 && NumArgs != 2)
118 return SpecializationLoc;
123static std::optional<TemplateSpecializationTypeLoc>
130static std::optional<EnableIfData>
132 if (
const auto Pointer = TheType.getAs<PointerTypeLoc>())
133 TheType = Pointer.getPointeeLoc();
134 else if (
const auto Reference = TheType.getAs<ReferenceTypeLoc>())
135 TheType = Reference.getPointeeLoc();
136 if (
const auto Qualified = TheType.getAs<QualifiedTypeLoc>())
137 TheType = Qualified.getUnqualifiedLoc();
144static std::pair<std::optional<EnableIfData>,
const Decl *>
154 const TemplateParameterList *TemplateParams =
155 FunctionTemplate->getTemplateParameters();
156 if (TemplateParams->size() == 0)
159 const NamedDecl *LastParam =
160 TemplateParams->getParam(TemplateParams->size() - 1);
161 if (
const auto *LastTemplateParam =
162 dyn_cast<NonTypeTemplateParmDecl>(LastParam)) {
164 if (!LastTemplateParam->hasDefaultArgument() ||
165 !LastTemplateParam->getName().empty())
169 LastTemplateParam->getTypeSourceInfo()->getTypeLoc()),
172 if (
const auto *LastTemplateParam =
173 dyn_cast<TemplateTypeParmDecl>(LastParam)) {
174 if (LastTemplateParam->hasDefaultArgument() &&
175 LastTemplateParam->getIdentifier() ==
nullptr) {
177 LastTemplateParam->getDefaultArgumentInfo()->getTypeLoc()),
190 return SM.getFileLoc(Element.getRAngleLoc());
195 const TemplateSpecializationTypeLoc &EnableIf) {
199 const LangOptions &LangOpts = Context.getLangOpts();
200 const SourceManager &SM = Context.getSourceManager();
201 if (EnableIf.getNumArgs() > 1) {
202 TemplateArgumentLoc NextArg = EnableIf.getArgLoc(1);
203 return {EnableIf.getLAngleLoc().getLocWithOffset(1),
205 NextArg.getSourceRange().getBegin(), SM, LangOpts, tok::comma)};
208 return {EnableIf.getLAngleLoc().getLocWithOffset(1),
213 const TemplateSpecializationTypeLoc &EnableIf) {
214 TemplateArgumentLoc Arg = EnableIf.getArgLoc(1);
215 const LangOptions &LangOpts = Context.getLangOpts();
216 const SourceManager &SM = Context.getSourceManager();
218 SM, LangOpts, tok::comma)
219 .getLocWithOffset(1),
226static std::optional<StringRef>
228 const TemplateSpecializationTypeLoc &EnableIf) {
229 if (EnableIf.getNumArgs() > 1) {
230 const LangOptions &LangOpts = Context.getLangOpts();
231 const SourceManager &SM = Context.getSourceManager();
232 bool Invalid =
false;
233 StringRef
Text = Lexer::getSourceText(CharSourceRange::getCharRange(
235 SM, LangOpts, &Invalid)
246static std::optional<SourceLocation>
248 SourceManager &SM = Context.getSourceManager();
249 const LangOptions &LangOpts = Context.getLangOpts();
251 if (
const auto *Constructor = dyn_cast<CXXConstructorDecl>(Function)) {
252 for (
const CXXCtorInitializer *Init : Constructor->inits()) {
253 if (Init->getSourceOrder() == 0)
255 SM, LangOpts, tok::colon);
257 if (Constructor->init_begin() != Constructor->init_end())
260 if (Function->isDeleted()) {
261 SourceLocation FunctionEnd = Function->getSourceRange().getEnd();
263 tok::equal, tok::equal);
265 const Stmt *Body = Function->getBody();
269 return Body->getBeginLoc();
278 if (
const auto *Cast = dyn_cast<ImplicitCastExpr>(Expression))
279 Expression = Cast->getSubExprAsWritten();
280 if (isa<ParenExpr, DependentScopeDeclRefExpr>(Expression))
291 SourceRange ConditionRange,
292 ASTContext &Context) {
293 SourceManager &SM = Context.getSourceManager();
294 const LangOptions &LangOpts = Context.getLangOpts();
296 SourceLocation PrevTokenLoc = ConditionRange.getEnd();
297 if (PrevTokenLoc.isInvalid())
300 const bool SkipComments =
false;
303 PrevTokenLoc, SM, LangOpts, SkipComments);
304 bool EndsWithDoubleSlash =
305 PrevToken.is(tok::comment) &&
306 Lexer::getSourceText(CharSourceRange::getCharRange(
307 PrevTokenLoc, PrevTokenLoc.getLocWithOffset(2)),
308 SM, LangOpts) ==
"//";
310 bool Invalid =
false;
311 llvm::StringRef ConditionText = Lexer::getSourceText(
312 CharSourceRange::getCharRange(ConditionRange), SM, LangOpts, &Invalid);
316 auto AddParens = [&](llvm::StringRef
Text) -> std::string {
319 return "(" +
Text.str() +
")";
322 if (EndsWithDoubleSlash)
323 return AddParens(ConditionText);
324 return AddParens(ConditionText.trim());
339 ASTContext &Context) {
340 TemplateArgumentLoc EnableCondition = EnableIf.
Loc.getArgLoc(0);
345 EnableCondition.getSourceExpression(), ConditionRange, Context);
349 std::optional<StringRef> TypeText =
getTypeText(Context, EnableIf.
Loc);
353 SmallVector<const Expr *, 3> ExistingConstraints;
354 Function->getAssociatedConstraints(ExistingConstraints);
355 if (!ExistingConstraints.empty()) {
361 std::optional<SourceLocation> ConstraintInsertionLoc =
363 if (!ConstraintInsertionLoc)
366 std::vector<FixItHint> FixIts;
367 FixIts.push_back(FixItHint::CreateReplacement(
368 CharSourceRange::getTokenRange(EnableIf.
Outer.getSourceRange()),
370 FixIts.push_back(FixItHint::CreateInsertion(
371 *ConstraintInsertionLoc,
"requires " + *ConditionText +
" "));
384static std::vector<FixItHint>
386 const FunctionDecl *Function,
387 const Decl *LastTemplateParam,
389 SourceManager &SM = Context.getSourceManager();
390 const LangOptions &LangOpts = Context.getLangOpts();
392 TemplateArgumentLoc EnableCondition = EnableIf.
Loc.getArgLoc(0);
397 EnableCondition.getSourceExpression(), ConditionRange, Context);
401 SmallVector<const Expr *, 3> ExistingConstraints;
402 Function->getAssociatedConstraints(ExistingConstraints);
403 if (!ExistingConstraints.empty()) {
409 SourceRange RemovalRange;
410 const TemplateParameterList *TemplateParams =
411 FunctionTemplate->getTemplateParameters();
412 if (!TemplateParams || TemplateParams->size() == 0)
415 if (TemplateParams->size() == 1) {
417 SourceRange(TemplateParams->getTemplateLoc(),
422 LastTemplateParam->getSourceRange().getBegin(), SM,
423 LangOpts, tok::comma),
427 std::optional<SourceLocation> ConstraintInsertionLoc =
429 if (!ConstraintInsertionLoc)
432 std::vector<FixItHint> FixIts;
434 FixItHint::CreateRemoval(CharSourceRange::getCharRange(RemovalRange)));
435 FixIts.push_back(FixItHint::CreateInsertion(
436 *ConstraintInsertionLoc,
"requires " + *ConditionText +
" "));
441 const auto *FunctionTemplate =
442 Result.Nodes.getNodeAs<FunctionTemplateDecl>(
"functionTemplate");
443 const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(
"function");
444 const auto *
ReturnType = Result.Nodes.getNodeAs<TypeLoc>(
"return");
445 if (!FunctionTemplate || !Function || !
ReturnType)
469 "use C++20 requires constraints instead of enable_if")
475 if (
auto [EnableIf, LastTemplateParam] =
477 EnableIf && LastTemplateParam) {
478 diag(LastTemplateParam->getSourceRange().getBegin(),
479 "use C++20 requires constraints instead of enable_if")
481 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