Index: clang-tidy/misc/StringConstructorCheck.cpp =================================================================== --- clang-tidy/misc/StringConstructorCheck.cpp +++ clang-tidy/misc/StringConstructorCheck.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "StringConstructorCheck.h" +#include "../utils/FixItHintUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -54,7 +55,7 @@ isDefinition(), hasType(pointerType(pointee(isAnyCharacter(), isConstQualified()))), hasInitializer(ignoringParenImpCasts(BoundStringLiteral))); - auto ConstStrLiteral = expr(ignoringParenImpCasts(anyOf( + const auto ConstStrLiteral = expr(ignoringParenImpCasts(anyOf( BoundStringLiteral, declRefExpr(hasDeclaration(anyOf( ConstPtrStrLiteralDecl, ConstStrLiteralDecl)))))); @@ -88,7 +89,7 @@ // Detect the expression: string("...", 0); hasArgument(1, ZeroExpr.bind("empty-string")), // Detect the expression: string("...", -4); - hasArgument(1, NegativeExpr.bind("negative-length")), + hasArgument(1, NegativeExpr.bind("negative-length")), // Detect the expression: string("lit", 0x1234567); hasArgument(1, LargeLengthExpr.bind("large-length")), // Detect the expression: string("lit", 5) @@ -100,11 +101,26 @@ } void StringConstructorCheck::check(const MatchFinder::MatchResult &Result) { - const auto *E = Result.Nodes.getNodeAs("constructor"); + const ASTContext &Ctx = *Result.Context; + const auto *E = Result.Nodes.getNodeAs("constructor"); + assert(E && "missing constructor expression"); SourceLocation Loc = E->getLocStart(); if (Result.Nodes.getNodeAs("swapped-parameter")) { - diag(Loc, "constructor parameters are probably swapped"); + auto D = diag(Loc, "constructor parameters are probably swapped"); + + SourceRange Param1 = E->getArg(0)->getSourceRange(); + SourceRange Param2 = E->getArg(1)->getSourceRange(); + StringRef Param1String = + utils::create_fix_it::getSourceRangeAsString(Ctx, Param1); + StringRef Param2String = + utils::create_fix_it::getSourceRangeAsString(Ctx, Param2); + if (!Param1String.empty() && !Param2String.empty()) { + D << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(Param1), + Param2String) + << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(Param2), + Param1String); + } } else if (Result.Nodes.getNodeAs("empty-string")) { diag(Loc, "constructor creating an empty string"); } else if (Result.Nodes.getNodeAs("negative-length")) { Index: clang-tidy/misc/SwappedArgumentsCheck.cpp =================================================================== --- clang-tidy/misc/SwappedArgumentsCheck.cpp +++ clang-tidy/misc/SwappedArgumentsCheck.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "SwappedArgumentsCheck.h" +#include "../utils/FixItHintUtils.h" #include "clang/AST/ASTContext.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/SmallPtrSet.h" @@ -46,24 +47,8 @@ Cast->getCastKind() == CK_PointerToBoolean; } -/// \brief Get a StringRef representing a SourceRange. -static StringRef getAsString(const MatchFinder::MatchResult &Result, - SourceRange R) { - const SourceManager &SM = *Result.SourceManager; - // Don't even try to resolve macro or include contraptions. Not worth emitting - // a fixit for. - if (R.getBegin().isMacroID() || - !SM.isWrittenInSameFile(R.getBegin(), R.getEnd())) - return StringRef(); - - const char *Begin = SM.getCharacterData(R.getBegin()); - const char *End = SM.getCharacterData(Lexer::getLocForEndOfToken( - R.getEnd(), 0, SM, Result.Context->getLangOpts())); - - return StringRef(Begin, End - Begin); -} - void SwappedArgumentsCheck::check(const MatchFinder::MatchResult &Result) { + const ASTContext &Ctx = *Result.Context; auto *Call = Result.Nodes.getStmtAs("call"); llvm::SmallPtrSet UsedArgs; @@ -108,8 +93,10 @@ << LHS->getType() << LHSFrom->getType() << RHS->getType() << RHSFrom->getType() << LHSRange << RHSRange; - StringRef RHSString = getAsString(Result, RHSRange); - StringRef LHSString = getAsString(Result, LHSRange); + StringRef RHSString = + utils::create_fix_it::getSourceRangeAsString(Ctx, RHSRange); + StringRef LHSString = + utils::create_fix_it::getSourceRangeAsString(Ctx, LHSRange); if (!LHSString.empty() && !RHSString.empty()) { D << FixItHint::CreateReplacement( CharSourceRange::getTokenRange(LHSRange), RHSString) Index: clang-tidy/utils/FixItHintUtils.h =================================================================== --- clang-tidy/utils/FixItHintUtils.h +++ clang-tidy/utils/FixItHintUtils.h @@ -24,6 +24,9 @@ /// \brief Creates fix to make VarDecl const qualified. FixItHint changeVarDeclToConst(const VarDecl &Var); +/// \brief Get a StringRef representing a SourceRange. +StringRef getSourceRangeAsString(const ASTContext &Context, SourceRange R); + } // namespace create_fix_it } // utils } // namespace tidy Index: clang-tidy/utils/FixItHintUtils.cpp =================================================================== --- clang-tidy/utils/FixItHintUtils.cpp +++ clang-tidy/utils/FixItHintUtils.cpp @@ -30,6 +30,21 @@ return FixItHint::CreateInsertion(Var.getTypeSpecStartLoc(), "const "); } +StringRef getSourceRangeAsString(const ASTContext &Context, SourceRange R) { + const SourceManager &SM = Context.getSourceManager(); + // Don't even try to resolve macro or include contraptions. Not worth emitting + // a fixit for. + if (R.getBegin().isMacroID() || + !SM.isWrittenInSameFile(R.getBegin(), R.getEnd())) + return StringRef(); + + const char *Begin = SM.getCharacterData(R.getBegin()); + const char *End = SM.getCharacterData( + Lexer::getLocForEndOfToken(R.getEnd(), 0, SM, Context.getLangOpts())); + + return StringRef(Begin, End - Begin); +} + } // namespace create_fix_it } // namespace utils } // namespace tidy Index: test/clang-tidy/misc-string-constructor.cpp =================================================================== --- test/clang-tidy/misc-string-constructor.cpp +++ test/clang-tidy/misc-string-constructor.cpp @@ -22,8 +22,10 @@ void Test() { std::string str('x', 4); // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor parameters are probably swapped [misc-string-constructor] + // CHECK-FIXES: std::string str(4, 'x'); std::wstring wstr(L'x', 4); // CHECK-MESSAGES: [[@LINE-1]]:16: warning: constructor parameters are probably swapped + // CHECK-FIXES: std::wstring wstr(4, L'x'); std::string s0(0, 'x'); // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string std::string s1(-4, 'x');