diff --git a/clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp @@ -10,6 +10,7 @@ #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Lex/Lexer.h" +#include "../utils/LexerUtils.h" using namespace clang::ast_matchers; @@ -302,9 +303,16 @@ auto Diag = diag(Location, "use '= default' to define a trivial " + SpecialFunctionName); - if (ApplyFix) - Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;") + if (ApplyFix) { + // Skipping comments, check for a semicolon after Body->getSourceRange() + Optional Token = utils::lexer::findNextTokenSkippingComments( + Body->getSourceRange().getEnd().getLocWithOffset(1), + Result.Context->getSourceManager(), Result.Context->getLangOpts()); + StringRef Replacement = + Token && Token->is(tok::semi) ? "= default" : "= default;"; + Diag << FixItHint::CreateReplacement(Body->getSourceRange(), Replacement) << RemoveInitializers; + } } } // namespace modernize diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.h b/clang-tools-extra/clang-tidy/utils/LexerUtils.h --- a/clang-tools-extra/clang-tidy/utils/LexerUtils.h +++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.h @@ -80,6 +80,11 @@ } } +// Finds next token that's not a comment. +Optional findNextTokenSkippingComments(SourceLocation Start, + const SourceManager &SM, + const LangOptions &LangOpts); + /// Re-lex the provide \p Range and return \c false if either a macro spans /// multiple tokens, a pre-processor directive or failure to retrieve the /// next token is found, otherwise \c true. diff --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp --- a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp +++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp @@ -68,6 +68,16 @@ return findNextAnyTokenKind(Start, SM, LangOpts, tok::comma, tok::semi); } +Optional findNextTokenSkippingComments(SourceLocation Start, + const SourceManager &SM, + const LangOptions &LangOpts) { + Optional CurrentToken; + do { + CurrentToken = Lexer::findNextToken(Start, SM, LangOpts); + } while (CurrentToken && CurrentToken->is(tok::comment)); + return CurrentToken; +} + bool rangeContainsExpansionsOrDirectives(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts) { diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -158,6 +158,10 @@ - The 'objc-avoid-spinlock' check was renamed to :doc:`darwin-avoid-spinlock ` +- The :doc:`modernize-use-equals-default + ` fix no longer adds + semicolons where they would be redundant. + - New :doc:`readability-redundant-access-specifiers ` check. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default-copy.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default-copy.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default-copy.cpp @@ -119,7 +119,7 @@ struct BF { BF() = default; BF(const BF &Other) : Field1(Other.Field1), Field2(Other.Field2), Field3(Other.Field3), - Field4(Other.Field4) {} + Field4(Other.Field4) {}; // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default' // CHECK-FIXES: BF(const BF &Other) {{$}} // CHECK-FIXES: = default; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-equals-default.cpp @@ -7,7 +7,7 @@ ~OL(); }; -OL::OL() {} +OL::OL() {}; // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial default constructor [modernize-use-equals-default] // CHECK-FIXES: OL::OL() = default; OL::~OL() {} @@ -17,9 +17,9 @@ // Inline definitions. class IL { public: - IL() {} + IL() {} ; // Note embedded tab on this line // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: IL() = default; + // CHECK-FIXES: IL() = default ; // Note embedded tab on this line ~IL() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' // CHECK-FIXES: ~IL() = default; @@ -46,18 +46,20 @@ // Default member initializer class DMI { public: - DMI() {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: DMI() = default; + DMI() {} // Comment before semi-colon on next line + ; + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default' + // CHECK-FIXES: DMI() = default // Comment before semi-colon on next line + // CHECK-FIXES-NEXT: ; int Field = 5; }; // Class member class CM { public: - CM() {} + CM() {} /* Comments */ /* before */ /* semicolon */; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' - // CHECK-FIXES: CM() = default; + // CHECK-FIXES: CM() = default /* Comments */ /* before */ /* semicolon */; OL o; }; @@ -66,7 +68,7 @@ Priv() {} // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' // CHECK-FIXES: Priv() = default; - ~Priv() {} + ~Priv() {}; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' // CHECK-FIXES: ~Priv() = default; };