clang-tools 23.0.0git
TypeSwitchCaseTypesCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12
13using namespace clang::ast_matchers;
14
16
18 // Match calls to `llvm::TypeSwitch::Case` with a lambda expression.
19 // Explicit template arguments and their count are checked in `check()`.
20 Finder->addMatcher(
21 cxxMemberCallExpr(
22 argumentCountIs(1),
23 callee(memberExpr(member(cxxMethodDecl(hasName("Case"),
24 ofClass(cxxRecordDecl(hasName(
25 "::llvm::TypeSwitch"))))))
26 .bind("member")),
27 hasArgument(0, lambdaExpr().bind("lambda")))
28 .bind("call"),
29 this);
30}
31
32void TypeSwitchCaseTypesCheck::check(const MatchFinder::MatchResult &Result) {
33 const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
34 assert(Call);
35 const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
36 assert(Lambda);
37 const auto *MemExpr = Result.Nodes.getNodeAs<MemberExpr>("member");
38 assert(MemExpr);
39
40 // Only handle `Case<T>` with exactly one explicit template argument.
41 if (!MemExpr->hasExplicitTemplateArgs() || MemExpr->getNumTemplateArgs() != 1)
42 return;
43
44 const TemplateArgumentLoc &TemplateArg = MemExpr->getTemplateArgs()[0];
45 if (TemplateArg.getArgument().getKind() != TemplateArgument::Type)
46 return;
47
48 // Get the lambda's call operator to examine its parameter.
49 const CXXMethodDecl *CallOp = Lambda->getCallOperator();
50 if (!CallOp || CallOp->getNumParams() != 1)
51 return;
52
53 const ParmVarDecl *LambdaParam = CallOp->getParamDecl(0);
54 const QualType ParamType = LambdaParam->getType();
55
56 // Check if the parameter uses `auto`.
57 QualType ParamBaseType = ParamType.getNonReferenceType();
58 while (ParamBaseType->isPointerType())
59 ParamBaseType = ParamBaseType->getPointeeType();
60 const bool ParamIsAuto = ParamBaseType->getUnqualifiedDesugaredType()
61 ->getAs<TemplateTypeParmType>() != nullptr;
62
63 if (ParamIsAuto) {
64 // Warn about `.Case<T>([](auto x) {...})` -- prefer explicit lambda
65 // parameter type. We only emit a warning without a fixit because we cannot
66 // reliably determine the deduced type of `auto`. The actual type depends on
67 // how `dyn_cast<CaseT>` behaves for the `TypeSwitch` value type, which
68 // varies (e.g., pointer types return pointers, but MLIR handle types may
69 // return by value).
70 diag(Call->getExprLoc(),
71 "lambda parameter needlessly uses 'auto', use explicit type instead");
72 diag(LambdaParam->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
73 "replace 'auto' with explicit type", DiagnosticIDs::Note);
74 diag(TemplateArg.getLocation(),
75 "type from template argument can be inferred and removed",
76 DiagnosticIDs::Note);
77 return;
78 }
79
80 // Handle `.Case<T>([](T x) {...})` -> `.Case([](T x) {...})`.
81 // Only warn if the types match (otherwise it might be intentional or a bug).
82 const QualType CaseType = TemplateArg.getArgument().getAsType();
83 if (CaseType->getCanonicalTypeUnqualified() !=
84 ParamBaseType->getCanonicalTypeUnqualified())
85 return;
86
87 auto Diag = diag(Call->getExprLoc(), "redundant explicit template argument");
88
89 // Skip fixit if template argument involves macros.
90 const SourceLocation LAngleLoc = MemExpr->getLAngleLoc();
91 const SourceLocation RAngleLoc = MemExpr->getRAngleLoc();
92 if (LAngleLoc.isInvalid() || RAngleLoc.isInvalid() || LAngleLoc.isMacroID() ||
93 RAngleLoc.isMacroID())
94 return;
95
96 Diag << FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc));
97
98 // Also remove `template` keyword, if present.
99 if (MemExpr->hasTemplateKeyword())
100 Diag << FixItHint::CreateRemoval(MemExpr->getTemplateKeywordLoc());
101}
102
103} // namespace clang::tidy::llvm_check
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override