// Finds the location of the qualifying `const` token in the `FunctionDecl`'s
// return type. Returns `None` when the return type is not `const`-qualified or
// `const` does not appear in `Def`'s source like when the type is an alias or a
// macro.
static llvm::Optional<Token>
findConstToRemove(const FunctionDecl *Def,
                  const MatchFinder::MatchResult &Result) {
  if (!Def->getReturnType().isLocalConstQualified())
    return None;

  // Get the begin location for the function name, including any qualifiers
  // written in the source (for out-of-line declarations). A FunctionDecl's
  // "location" is the start of its name, so, when the name is unqualified, we
  // use `getLocation()`.
  SourceLocation NameBeginLoc = Def->getQualifier()
                                    ? Def->getQualifierLoc().getBeginLoc()
                                    : Def->getLocation();
  // Since either of the locs can be in a macro, use `makeFileCharRange` to be
  // sure that we have a consistent `CharSourceRange`, located entirely in the
  // source file.
  CharSourceRange FileRange = Lexer::makeFileCharRange(
      CharSourceRange::getCharRange(Def->getBeginLoc(), NameBeginLoc),
      *Result.SourceManager, Result.Context->getLangOpts());

  if (FileRange.isInvalid())
    return None;

  return utils::lexer::getConstQualifyingToken(FileRange, *Result.Context,
                                               *Result.SourceManager);
}
Example #2
0
bool Commit::canRemoveRange(CharSourceRange range,
                            FileOffset &Offs, unsigned &Len) {
  const SourceManager &SM = SourceMgr;
  range = Lexer::makeFileCharRange(range, SM, LangOpts);
  if (range.isInvalid())
    return false;

  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
    return false;
  if (SM.isInSystemHeader(range.getBegin()) ||
      SM.isInSystemHeader(range.getEnd()))
    return false;

  if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
    return false;

  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
  if (beginInfo.first != endInfo.first ||
      beginInfo.second > endInfo.second)
    return false;

  Offs = FileOffset(beginInfo.first, beginInfo.second);
  Len = endInfo.second - beginInfo.second;
  return true;
}
Example #3
0
static RawComment toRawComment(ASTContext &Context, CharSourceRange Range) {
  if (Range.isInvalid())
    return RawComment();

  auto &SourceMgr = Context.SourceMgr;
  unsigned BufferID = SourceMgr.findBufferContainingLoc(Range.getStart());
  unsigned Offset = SourceMgr.getLocOffsetInBuffer(Range.getStart(), BufferID);
  unsigned EndOffset = SourceMgr.getLocOffsetInBuffer(Range.getEnd(), BufferID);
  LangOptions FakeLangOpts;
  Lexer L(FakeLangOpts, SourceMgr, BufferID, nullptr, LexerMode::Swift,
          HashbangMode::Disallowed,
          CommentRetentionMode::ReturnAsTokens,
          TriviaRetentionMode::WithoutTrivia,
          Offset, EndOffset);
  SmallVector<SingleRawComment, 16> Comments;
  Token Tok;
  while (true) {
    L.lex(Tok);
    if (Tok.is(tok::eof))
      break;
    assert(Tok.is(tok::comment));
    addCommentToList(Comments, SingleRawComment(Tok.getRange(), SourceMgr));
  }
  RawComment Result;
  Result.Comments = Context.AllocateCopy(Comments);
  return Result;
}
void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
  SourceManager &SM = *Result.SourceManager;
  if (const auto *E =
          Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
    CharSourceRange Range = Lexer::makeFileCharRange(
        CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());

    if (Range.isInvalid())
      return;

    auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
                << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
                << FixItHint::CreateInsertion(Range.getEnd(), ")");

    if (auto Fix =
            Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
                                             /*IsAngled=*/true))
      Diag << *Fix;

    return;
  }

  SourceLocation AutoPtrLoc;
  if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
    //   std::auto_ptr<int> i;
    //        ^
    if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>())
      AutoPtrLoc = Loc.getTemplateNameLoc();
  } else if (const auto *D =
                 Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
    // using std::auto_ptr;
    //            ^
    AutoPtrLoc = D->getNameInfo().getBeginLoc();
  } else {
    llvm_unreachable("Bad Callback. No node provided.");
  }

  if (AutoPtrLoc.isMacroID())
    AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc);

  // Ensure that only the 'auto_ptr' token is replaced and not the template
  // aliases.
  if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) !=
      "auto_ptr")
    return;

  SourceLocation EndLoc =
      AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1);
  diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead")
      << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc),
                                      "unique_ptr");
}
void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
  SourceManager &SM = *Result.SourceManager;
  if (const auto *E =
          Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
    CharSourceRange Range = Lexer::makeFileCharRange(
        CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());

    if (Range.isInvalid())
      return;

    auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
                << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
                << FixItHint::CreateInsertion(Range.getEnd(), ")");

    auto Insertion =
        Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
                                         /*IsAngled=*/true);
    if (Insertion.hasValue())
      Diag << Insertion.getValue();

    return;
  }

  SourceLocation IdentifierLoc;
  if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
    IdentifierLoc = locateFromTypeLoc(TL, SM);
  } else if (const auto *D =
                 Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
    IdentifierLoc = locateFromUsingDecl(D, SM);
  } else {
    llvm_unreachable("Bad Callback. No node provided.");
  }

  if (IdentifierLoc.isMacroID())
    IdentifierLoc = SM.getSpellingLoc(IdentifierLoc);

  // Ensure that only the 'auto_ptr' token is replaced and not the template
  // aliases.
  if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions()))
    return;

  SourceLocation EndLoc =
      IdentifierLoc.getLocWithOffset(strlen("auto_ptr") - 1);
  diag(IdentifierLoc, "auto_ptr is deprecated, use unique_ptr instead")
      << FixItHint::CreateReplacement(SourceRange(IdentifierLoc, EndLoc),
                                      "unique_ptr");
}
void UpgradeDurationConversionsCheck::check(
    const MatchFinder::MatchResult &Result) {
  const llvm::StringRef Message =
      "implicit conversion to 'int64_t' is deprecated in this context; use an "
      "explicit cast instead";

  const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg");
  SourceLocation Loc = ArgExpr->getBeginLoc();

  if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) {
    if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) {
      // For each location matched in a template instantiation, we check if the
      // location can also be found in `MatchedTemplateLocations`. If it is not
      // found, that means the expression did not create a match without the
      // instantiation and depends on template parameters. A manual fix is
      // probably required so we provide only a warning.
      diag(Loc, Message);
    }
    return;
  }

  // We gather source locations from template matches not in template
  // instantiations for future matches.
  internal::Matcher<Stmt> IsInsideTemplate =
      hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl())));
  if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty())
    MatchedTemplateLocations.insert(Loc.getRawEncoding());

  DiagnosticBuilder Diag = diag(Loc, Message);
  CharSourceRange SourceRange = Lexer::makeFileCharRange(
      CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
      *Result.SourceManager, Result.Context->getLangOpts());
  if (SourceRange.isInvalid())
    // An invalid source range likely means we are inside a macro body. A manual
    // fix is likely needed so we do not create a fix-it hint.
    return;

  Diag << FixItHint::CreateInsertion(SourceRange.getBegin(),
                                     "static_cast<int64_t>(")
       << FixItHint::CreateInsertion(SourceRange.getEnd(), ")");
}
Example #7
0
bool Rewriter::IncreaseIndentation(CharSourceRange range,
                                   SourceLocation parentIndent) {
  if (range.isInvalid()) return true;
  if (!isRewritable(range.getBegin())) return true;
  if (!isRewritable(range.getEnd())) return true;
  if (!isRewritable(parentIndent)) return true;

  FileID StartFileID, EndFileID, parentFileID;
  unsigned StartOff, EndOff, parentOff;

  StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
  EndOff   = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
  parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);

  if (StartFileID != EndFileID || StartFileID != parentFileID)
    return true;
  if (StartOff > EndOff)
    return true;

  FileID FID = StartFileID;
  StringRef MB = SourceMgr->getBufferData(FID);

  unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
  unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
  unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
  
  const SrcMgr::ContentCache *
      Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
  
  // Find where the lines start.
  unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
  unsigned startLineOffs = Content->SourceLineCache[startLineNo];

  // Find the whitespace at the start of each line.
  StringRef parentSpace, startSpace;
  {
    unsigned i = parentLineOffs;
    while (isWhitespace(MB[i]))
      ++i;
    parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);

    i = startLineOffs;
    while (isWhitespace(MB[i]))
      ++i;
    startSpace = MB.substr(startLineOffs, i-startLineOffs);
  }
  if (parentSpace.size() >= startSpace.size())
    return true;
  if (!startSpace.startswith(parentSpace))
    return true;

  StringRef indent = startSpace.substr(parentSpace.size());

  // Indent the lines between start/end offsets.
  RewriteBuffer &RB = getEditBuffer(FID);
  for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {
    unsigned offs = Content->SourceLineCache[lineNo];
    unsigned i = offs;
    while (isWhitespace(MB[i]))
      ++i;
    StringRef origIndent = MB.substr(offs, i-offs);
    if (origIndent.startswith(startSpace))
      RB.InsertText(offs, indent, /*InsertAfter=*/false);
  }

  return false;
}
void ConstructorParamReplacer::run(const MatchFinder::MatchResult &Result) {
  assert(IncludeManager && "Include directives manager not set.");
  SourceManager &SM = *Result.SourceManager;
  const CXXConstructorDecl *Ctor =
      Result.Nodes.getNodeAs<CXXConstructorDecl>(PassByValueCtorId);
  const ParmVarDecl *ParamDecl =
      Result.Nodes.getNodeAs<ParmVarDecl>(PassByValueParamId);
  const CXXCtorInitializer *Initializer =
      Result.Nodes.getNodeAs<CXXCtorInitializer>(PassByValueInitializerId);
  assert(Ctor && ParamDecl && Initializer && "Bad Callback, missing node.");

  // Check this now to avoid unnecessary work. The param locations are checked
  // later.
  if (!Owner.isFileModifiable(SM, Initializer->getSourceLocation()))
    return;

  // The parameter will be in an unspecified state after the move, so check if
  // the parameter is used for anything else other than the copy. If so do not
  // apply any changes.
  if (!paramReferredExactlyOnce(Ctor, ParamDecl))
    return;

  llvm::SmallVector<const ParmVarDecl *, 2> AllParamDecls;
  collectParamDecls(Ctor, ParamDecl, AllParamDecls);

  // Generate all replacements for the params.
  llvm::SmallVector<Replacement, 2> ParamReplaces;
  for (unsigned I = 0, E = AllParamDecls.size(); I != E; ++I) {
    TypeLoc ParamTL = AllParamDecls[I]->getTypeSourceInfo()->getTypeLoc();
    ReferenceTypeLoc RefTL = ParamTL.getAs<ReferenceTypeLoc>();
    SourceRange Range(AllParamDecls[I]->getLocStart(), ParamTL.getLocEnd());
    CharSourceRange CharRange = Lexer::makeFileCharRange(
        CharSourceRange::getTokenRange(Range), SM, LangOptions());

    // do not generate a replacement when the parameter is already a value
    if (RefTL.isNull())
      continue;

    // transform non-value parameters (e.g: const-ref) to values
    TypeLoc ValueTypeLoc = RefTL.getPointeeLoc();
    llvm::SmallString<32> ValueStr = Lexer::getSourceText(
        CharSourceRange::getTokenRange(ValueTypeLoc.getSourceRange()), SM,
        LangOptions());

    // If it's impossible to change one of the parameter (e.g: comes from an
    // unmodifiable header) quit the callback now, do not generate any changes.
    if (CharRange.isInvalid() || ValueStr.empty() ||
        !Owner.isFileModifiable(SM, CharRange.getBegin()))
      return;

    // 'const Foo &param' -> 'Foo param'
    //  ~~~~~~~~~~~           ~~~^
    ValueStr += ' ';
    ParamReplaces.push_back(Replacement(SM, CharRange, ValueStr));
  }

  // Reject the changes if the the risk level is not acceptable.
  if (!Owner.isAcceptableRiskLevel(RL_Reasonable)) {
    RejectedChanges++;
    return;
  }

  // if needed, include <utility> in the file that uses std::move()
  const FileEntry *STDMoveFile =
      SM.getFileEntryForID(SM.getFileID(Initializer->getLParenLoc()));
  const tooling::Replacement &IncludeReplace =
      IncludeManager->addAngledInclude(STDMoveFile, "utility");
  if (IncludeReplace.isApplicable()) {
    Owner.addReplacementForCurrentTU(IncludeReplace);
    AcceptedChanges++;
  }

  // const-ref params becomes values (const Foo & -> Foo)
  for (const Replacement *I = ParamReplaces.begin(), *E = ParamReplaces.end();
       I != E; ++I) {
    Owner.addReplacementForCurrentTU(*I);
  }
  AcceptedChanges += ParamReplaces.size();

  // move the value in the init-list
  Owner.addReplacementForCurrentTU(Replacement(
      SM, Initializer->getLParenLoc().getLocWithOffset(1), 0, "std::move("));
  Owner.addReplacementForCurrentTU(
      Replacement(SM, Initializer->getRParenLoc(), 0, ")"));
  AcceptedChanges += 2;
}