xref: /llvm-project/clang-tools-extra/clang-tidy/misc/MisplacedConstCheck.cpp (revision cbdc3e1bf9da09911ba353bcd20c6709bda43893)
1cf6cefd8SAaron Ballman //===--- MisplacedConstCheck.cpp - clang-tidy------------------------------===//
2cf6cefd8SAaron Ballman //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6cf6cefd8SAaron Ballman //
7cf6cefd8SAaron Ballman //===----------------------------------------------------------------------===//
8cf6cefd8SAaron Ballman 
9cf6cefd8SAaron Ballman #include "MisplacedConstCheck.h"
10cf6cefd8SAaron Ballman #include "clang/AST/ASTContext.h"
11cf6cefd8SAaron Ballman #include "clang/ASTMatchers/ASTMatchFinder.h"
12cf6cefd8SAaron Ballman 
13cf6cefd8SAaron Ballman using namespace clang::ast_matchers;
14cf6cefd8SAaron Ballman 
157d2ea6c4SCarlos Galvez namespace clang::tidy::misc {
16cf6cefd8SAaron Ballman 
registerMatchers(MatchFinder * Finder)17cf6cefd8SAaron Ballman void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) {
1884c5f196SAlexander Lanin   auto NonConstAndNonFunctionPointerType = hasType(pointerType(unless(
1984c5f196SAlexander Lanin       pointee(anyOf(isConstQualified(), ignoringParens(functionType()))))));
2084c5f196SAlexander Lanin 
21cf6cefd8SAaron Ballman   Finder->addMatcher(
2215f3cd6bSMatheus Izvekov       valueDecl(hasType(qualType(
2315f3cd6bSMatheus Izvekov                     isConstQualified(),
2415f3cd6bSMatheus Izvekov                     elaboratedType(namesType(typedefType(hasDeclaration(
2515f3cd6bSMatheus Izvekov                         anyOf(typedefDecl(NonConstAndNonFunctionPointerType)
2615f3cd6bSMatheus Izvekov                                   .bind("typedef"),
2784c5f196SAlexander Lanin                               typeAliasDecl(NonConstAndNonFunctionPointerType)
2815f3cd6bSMatheus Izvekov                                   .bind("typeAlias")))))))))
29cf6cefd8SAaron Ballman           .bind("decl"),
30cf6cefd8SAaron Ballman       this);
31cf6cefd8SAaron Ballman }
32cf6cefd8SAaron Ballman 
guessAlternateQualification(ASTContext & Context,QualType QT)33cf6cefd8SAaron Ballman static QualType guessAlternateQualification(ASTContext &Context, QualType QT) {
34cf6cefd8SAaron Ballman   // We're given a QualType from a typedef where the qualifiers apply to the
35cf6cefd8SAaron Ballman   // pointer instead of the pointee. Strip the const qualifier from the pointer
36cf6cefd8SAaron Ballman   // type and add it to the pointee instead.
37cf6cefd8SAaron Ballman   if (!QT->isPointerType())
38cf6cefd8SAaron Ballman     return QT;
39cf6cefd8SAaron Ballman 
40cf6cefd8SAaron Ballman   Qualifiers Quals = QT.getLocalQualifiers();
41cf6cefd8SAaron Ballman   Quals.removeConst();
42cf6cefd8SAaron Ballman 
43cf6cefd8SAaron Ballman   QualType NewQT = Context.getPointerType(
44cf6cefd8SAaron Ballman       QualType(QT->getPointeeType().getTypePtr(), Qualifiers::Const));
45cf6cefd8SAaron Ballman   return NewQT.withCVRQualifiers(Quals.getCVRQualifiers());
46cf6cefd8SAaron Ballman }
47cf6cefd8SAaron Ballman 
check(const MatchFinder::MatchResult & Result)48cf6cefd8SAaron Ballman void MisplacedConstCheck::check(const MatchFinder::MatchResult &Result) {
49cf6cefd8SAaron Ballman   const auto *Var = Result.Nodes.getNodeAs<ValueDecl>("decl");
50cf6cefd8SAaron Ballman   ASTContext &Ctx = *Result.Context;
51cf6cefd8SAaron Ballman   QualType CanQT = Var->getType().getCanonicalType();
52cf6cefd8SAaron Ballman 
5384c5f196SAlexander Lanin   SourceLocation AliasLoc;
54*cbdc3e1bSPiotr Zegar   const char *AliasType = nullptr;
5584c5f196SAlexander Lanin   if (const auto *Typedef = Result.Nodes.getNodeAs<TypedefDecl>("typedef")) {
5684c5f196SAlexander Lanin     AliasLoc = Typedef->getLocation();
5784c5f196SAlexander Lanin     AliasType = "typedef";
5884c5f196SAlexander Lanin   } else if (const auto *TypeAlias =
5984c5f196SAlexander Lanin                  Result.Nodes.getNodeAs<TypeAliasDecl>("typeAlias")) {
6084c5f196SAlexander Lanin     AliasLoc = TypeAlias->getLocation();
6184c5f196SAlexander Lanin     AliasType = "type alias";
6284c5f196SAlexander Lanin   } else {
6384c5f196SAlexander Lanin     llvm_unreachable("registerMatchers has registered an unknown matcher,"
6484c5f196SAlexander Lanin                      " code out of sync");
6584c5f196SAlexander Lanin   }
6684c5f196SAlexander Lanin 
6784c5f196SAlexander Lanin   diag(Var->getLocation(), "%0 declared with a const-qualified %1; "
6884c5f196SAlexander Lanin                            "results in the type being '%2' instead of '%3'")
6984c5f196SAlexander Lanin       << Var << AliasType << CanQT.getAsString(Ctx.getPrintingPolicy())
70cf6cefd8SAaron Ballman       << guessAlternateQualification(Ctx, CanQT)
71cf6cefd8SAaron Ballman              .getAsString(Ctx.getPrintingPolicy());
7284c5f196SAlexander Lanin   diag(AliasLoc, "%0 declared here", DiagnosticIDs::Note) << AliasType;
73cf6cefd8SAaron Ballman }
74cf6cefd8SAaron Ballman 
757d2ea6c4SCarlos Galvez } // namespace clang::tidy::misc
76