forked from OSchip/llvm-project
156 lines
6.0 KiB
C++
156 lines
6.0 KiB
C++
//===--- MakeUniqueCheck.cpp - clang-tidy----------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MakeUniqueCheck.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace modernize {
|
|
|
|
static const char PointerType[] = "pointerType";
|
|
static const char ConstructorCall[] = "constructorCall";
|
|
static const char NewExpression[] = "newExpression";
|
|
|
|
void MakeUniqueCheck::registerMatchers(MatchFinder *Finder) {
|
|
if (getLangOpts().CPlusPlus11) {
|
|
Finder->addMatcher(
|
|
cxxBindTemporaryExpr(has(
|
|
cxxConstructExpr(
|
|
hasType(qualType(hasDeclaration(classTemplateSpecializationDecl(
|
|
matchesName("::std::unique_ptr"),
|
|
templateArgumentCountIs(2),
|
|
hasTemplateArgument(0, templateArgument(refersToType(
|
|
qualType().bind(PointerType)))),
|
|
hasTemplateArgument(
|
|
1, templateArgument(refersToType(qualType(
|
|
hasDeclaration(classTemplateSpecializationDecl(
|
|
matchesName("::std::default_delete"),
|
|
templateArgumentCountIs(1),
|
|
hasTemplateArgument(
|
|
0, templateArgument(refersToType(
|
|
qualType(equalsBoundNode(
|
|
PointerType))))))))))))))),
|
|
argumentCountIs(1),
|
|
hasArgument(
|
|
0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
|
|
equalsBoundNode(PointerType))))))
|
|
.bind(NewExpression)))
|
|
.bind(ConstructorCall))),
|
|
this);
|
|
}
|
|
}
|
|
|
|
void MakeUniqueCheck::check(const MatchFinder::MatchResult &Result) {
|
|
SourceManager &SM = *Result.SourceManager;
|
|
const auto *Construct =
|
|
Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
|
|
const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
|
|
const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
|
|
|
|
if (New->getNumPlacementArgs() != 0)
|
|
return;
|
|
|
|
SourceLocation ConstructCallStart = Construct->getExprLoc();
|
|
|
|
bool Invalid = false;
|
|
StringRef ExprStr = Lexer::getSourceText(
|
|
CharSourceRange::getCharRange(
|
|
ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
|
|
SM, LangOptions(), &Invalid);
|
|
if (Invalid)
|
|
return;
|
|
|
|
auto Diag = diag(ConstructCallStart, "use std::make_unique instead");
|
|
|
|
// Find the location of the template's left angle.
|
|
size_t LAngle = ExprStr.find("<");
|
|
SourceLocation ConstructCallEnd;
|
|
if (LAngle == StringRef::npos) {
|
|
// If the template argument is missing (because it is part of the alias)
|
|
// we have to add it back.
|
|
ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
|
|
Diag << FixItHint::CreateInsertion(
|
|
ConstructCallEnd, "<" + Type->getAsString(getLangOpts()) + ">");
|
|
} else {
|
|
ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
|
|
}
|
|
|
|
Diag << FixItHint::CreateReplacement(
|
|
CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
|
|
"std::make_unique");
|
|
|
|
// If the unique_ptr is built with brace enclosed direct initialization, use
|
|
// parenthesis instead.
|
|
if (Construct->isListInitialization()) {
|
|
SourceRange BraceRange = Construct->getParenOrBraceRange();
|
|
Diag << FixItHint::CreateReplacement(
|
|
CharSourceRange::getCharRange(
|
|
BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
|
|
"(");
|
|
Diag << FixItHint::CreateReplacement(
|
|
CharSourceRange::getCharRange(BraceRange.getEnd(),
|
|
BraceRange.getEnd().getLocWithOffset(1)),
|
|
")");
|
|
}
|
|
|
|
SourceLocation NewStart = New->getSourceRange().getBegin();
|
|
SourceLocation NewEnd = New->getSourceRange().getEnd();
|
|
switch (New->getInitializationStyle()) {
|
|
case CXXNewExpr::NoInit: {
|
|
Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
|
|
break;
|
|
}
|
|
case CXXNewExpr::CallInit: {
|
|
SourceRange InitRange = New->getDirectInitRange();
|
|
Diag << FixItHint::CreateRemoval(
|
|
SourceRange(NewStart, InitRange.getBegin()));
|
|
Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
|
|
break;
|
|
}
|
|
case CXXNewExpr::ListInit: {
|
|
// Range of the substring that we do not want to remove.
|
|
SourceRange InitRange;
|
|
if (const auto *NewConstruct = New->getConstructExpr()) {
|
|
// Direct initialization with initialization list.
|
|
// struct S { S(int x) {} };
|
|
// std::unique_ptr<S>(new S{5});
|
|
// The arguments in the initialization list are going to be forwarded to
|
|
// the constructor, so this has to be replaced with:
|
|
// struct S { S(int x) {} };
|
|
// std::make_unique<S>(5);
|
|
InitRange = SourceRange(
|
|
NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
|
|
NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
|
|
} else {
|
|
// Aggregate initialization.
|
|
// std::unique_ptr<Pair>(new Pair{first, second});
|
|
// Has to be replaced with:
|
|
// std::make_unique<Pair>(Pair{first, second});
|
|
InitRange = SourceRange(
|
|
New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
|
|
New->getInitializer()->getSourceRange().getEnd());
|
|
}
|
|
Diag << FixItHint::CreateRemoval(
|
|
CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
|
|
Diag << FixItHint::CreateRemoval(
|
|
SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace modernize
|
|
} // namespace tidy
|
|
} // namespace clang
|