[clang-tidy] Avoid incorrect fixes in modernize-use-using

Avoid fixes for typedefs with multiple types and for typedefs with struct
definitions. Partially addresses http://llvm.org/PR28334

llvm-svn: 292918
This commit is contained in:
Alexander Kornienko 2017-01-24 11:41:02 +00:00
parent c456e686a9
commit 4b1dfbbc78
2 changed files with 57 additions and 28 deletions

View File

@ -25,9 +25,10 @@ void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
// Checks if 'typedef' keyword can be removed - we do it only if // Checks if 'typedef' keyword can be removed - we do it only if
// it is the only declaration in a declaration chain. // it is the only declaration in a declaration chain.
static bool CheckRemoval(SourceManager &SM, const SourceLocation &LocStart, static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc,
const SourceLocation &LocEnd, ASTContext &Context) { ASTContext &Context) {
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(LocStart); assert(StartLoc.isFileID() && "StartLoc must not be in a macro");
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(StartLoc);
StringRef File = SM.getBufferData(LocInfo.first); StringRef File = SM.getBufferData(LocInfo.first);
const char *TokenBegin = File.data() + LocInfo.second; const char *TokenBegin = File.data() + LocInfo.second;
Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(), Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
@ -35,28 +36,39 @@ static bool CheckRemoval(SourceManager &SM, const SourceLocation &LocStart,
Token Tok; Token Tok;
int ParenLevel = 0; int ParenLevel = 0;
bool FoundTypedef = false;
while (!DeclLexer.LexFromRawLexer(Tok)) { while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) {
if (SM.isBeforeInTranslationUnit(LocEnd, Tok.getLocation())) switch (Tok.getKind()) {
break; case tok::l_brace:
if (Tok.getKind() == tok::TokenKind::l_paren) case tok::r_brace:
ParenLevel++; // This might be the `typedef struct {...} T;` case.
if (Tok.getKind() == tok::TokenKind::r_paren)
ParenLevel--;
if (Tok.getKind() == tok::TokenKind::semi)
break;
// if there is comma and we are not between open parenthesis then it is
// two or more declatarions in this chain
if (ParenLevel == 0 && Tok.getKind() == tok::TokenKind::comma)
return false; return false;
case tok::l_paren:
if (Tok.is(tok::TokenKind::raw_identifier)) { ParenLevel++;
if (Tok.getRawIdentifier() == "typedef") break;
return true; case tok::r_paren:
ParenLevel--;
break;
case tok::comma:
if (ParenLevel == 0) {
// If there is comma and we are not between open parenthesis then it is
// two or more declarations in this chain.
return false;
}
break;
case tok::raw_identifier:
if (Tok.getRawIdentifier() == "typedef") {
FoundTypedef = true;
}
break;
default:
break;
} }
} }
return false; // Sanity check against weird macro cases.
return FoundTypedef;
} }
void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
@ -67,14 +79,19 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
auto &Context = *Result.Context; auto &Context = *Result.Context;
auto &SM = *Result.SourceManager; auto &SM = *Result.SourceManager;
if (auto *D = MatchedDecl->getUnderlyingType()->getAsCXXRecordDecl()) {
//TypeLoc TL = MatchedDecl->getTypeSourceInfo()->getTypeLoc();
llvm::errs() << D->getNameAsString() << "\n";
}
auto Diag = auto Diag =
diag(MatchedDecl->getLocStart(), "use 'using' instead of 'typedef'"); diag(MatchedDecl->getLocStart(), "use 'using' instead of 'typedef'");
if (MatchedDecl->getLocStart().isMacroID()) SourceLocation StartLoc = MatchedDecl->getLocStart();
if (StartLoc.isMacroID())
return; return;
if (CheckRemoval(SM, MatchedDecl->getLocStart(), MatchedDecl->getLocEnd(), if (CheckRemoval(SM, StartLoc, Context)) {
Context)) {
Diag << FixItHint::CreateReplacement( Diag << FixItHint::CreateReplacement(
MatchedDecl->getSourceRange(), MatchedDecl->getSourceRange(),
"using " + MatchedDecl->getNameAsString() + " = " + "using " + MatchedDecl->getNameAsString() + " = " +

View File

@ -78,8 +78,13 @@ typedef Test<my_class *> another;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using another = Test<my_class *>; // CHECK-FIXES: using another = Test<my_class *>;
typedef int* PInt;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using PInt = int *;
typedef int bla1, bla2, bla3; typedef int bla1, bla2, bla3;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: typedef int bla1, bla2, bla3;
#define CODE typedef int INT #define CODE typedef int INT
@ -131,8 +136,15 @@ int typedef Bax;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: using Bax = int; // CHECK-FIXES: using Bax = int;
// FIXME: Avoid incorrect fixes in these cases. typedef struct Q1 { int a; } S1;
//typedef struct Q1 { int a; } S1; // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
//typedef struct { int b; } S2; // CHECK-FIXES: typedef struct Q1 { int a; } S1;
//struct Q2 { int c; } typedef S3; typedef struct { int b; } S2;
//struct { int d; } typedef S4; // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: typedef struct { int b; } S2;
struct Q2 { int c; } typedef S3;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: struct Q2 { int c; } typedef S3;
struct { int d; } typedef S4;
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
// CHECK-FIXES: struct { int d; } typedef S4;