123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- //===--- FixItHintUtils.cpp - clang-tidy-----------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "FixItHintUtils.h"
- #include "LexerUtils.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/Type.h"
- #include <optional>
- namespace clang::tidy::utils::fixit {
- FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
- SourceLocation AmpLocation = Var.getLocation();
- auto Token = utils::lexer::getPreviousToken(
- AmpLocation, Context.getSourceManager(), Context.getLangOpts());
- if (!Token.is(tok::unknown))
- AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,
- Context.getSourceManager(),
- Context.getLangOpts());
- return FixItHint::CreateInsertion(AmpLocation, "&");
- }
- static bool isValueType(const Type *T) {
- return !(isa<PointerType>(T) || isa<ReferenceType>(T) || isa<ArrayType>(T) ||
- isa<MemberPointerType>(T) || isa<ObjCObjectPointerType>(T));
- }
- static bool isValueType(QualType QT) { return isValueType(QT.getTypePtr()); }
- static bool isMemberOrFunctionPointer(QualType QT) {
- return (QT->isPointerType() && QT->isFunctionPointerType()) ||
- isa<MemberPointerType>(QT.getTypePtr());
- }
- static bool locDangerous(SourceLocation S) {
- return S.isInvalid() || S.isMacroID();
- }
- static std::optional<SourceLocation>
- skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {
- if (locDangerous(Start))
- return std::nullopt;
- auto PreviousTokenLParen = [&Start, &Context]() {
- Token T;
- T = lexer::getPreviousToken(Start, Context.getSourceManager(),
- Context.getLangOpts());
- return T.is(tok::l_paren);
- };
- while (Start.isValid() && PreviousTokenLParen())
- Start = lexer::findPreviousTokenStart(Start, Context.getSourceManager(),
- Context.getLangOpts());
- if (locDangerous(Start))
- return std::nullopt;
- return Start;
- }
- static std::optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,
- StringRef Text) {
- if (locDangerous(Loc))
- return std::nullopt;
- return FixItHint::CreateInsertion(Loc, Text);
- }
- // Build a string that can be emitted as FixIt with either a space in before
- // or after the qualifier, either ' const' or 'const '.
- static std::string buildQualifier(DeclSpec::TQ Qualifier,
- bool WhitespaceBefore = false) {
- if (WhitespaceBefore)
- return (llvm::Twine(' ') + DeclSpec::getSpecifierName(Qualifier)).str();
- return (llvm::Twine(DeclSpec::getSpecifierName(Qualifier)) + " ").str();
- }
- static std::optional<FixItHint> changeValue(const VarDecl &Var,
- DeclSpec::TQ Qualifier,
- QualifierTarget QualTarget,
- QualifierPolicy QualPolicy,
- const ASTContext &Context) {
- switch (QualPolicy) {
- case QualifierPolicy::Left:
- return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
- buildQualifier(Qualifier));
- case QualifierPolicy::Right:
- std::optional<SourceLocation> IgnoredParens =
- skipLParensBackwards(Var.getLocation(), Context);
- if (IgnoredParens)
- return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
- return std::nullopt;
- }
- llvm_unreachable("Unknown QualifierPolicy enum");
- }
- static std::optional<FixItHint> changePointerItself(const VarDecl &Var,
- DeclSpec::TQ Qualifier,
- const ASTContext &Context) {
- if (locDangerous(Var.getLocation()))
- return std::nullopt;
- std::optional<SourceLocation> IgnoredParens =
- skipLParensBackwards(Var.getLocation(), Context);
- if (IgnoredParens)
- return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
- return std::nullopt;
- }
- static std::optional<FixItHint>
- changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
- QualifierTarget QualTarget, QualifierPolicy QualPolicy,
- const ASTContext &Context) {
- // The pointer itself shall be marked as `const`. This is always to the right
- // of the '*' or in front of the identifier.
- if (QualTarget == QualifierTarget::Value)
- return changePointerItself(Var, Qualifier, Context);
- // Mark the pointee `const` that is a normal value (`int* p = nullptr;`).
- if (QualTarget == QualifierTarget::Pointee && isValueType(Pointee)) {
- // Adding the `const` on the left side is just the beginning of the type
- // specification. (`const int* p = nullptr;`)
- if (QualPolicy == QualifierPolicy::Left)
- return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
- buildQualifier(Qualifier));
- // Adding the `const` on the right side of the value type requires finding
- // the `*` token and placing the `const` left of it.
- // (`int const* p = nullptr;`)
- if (QualPolicy == QualifierPolicy::Right) {
- SourceLocation BeforeStar = lexer::findPreviousTokenKind(
- Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
- tok::star);
- if (locDangerous(BeforeStar))
- return std::nullopt;
- std::optional<SourceLocation> IgnoredParens =
- skipLParensBackwards(BeforeStar, Context);
- if (IgnoredParens)
- return fixIfNotDangerous(*IgnoredParens,
- buildQualifier(Qualifier, true));
- return std::nullopt;
- }
- }
- if (QualTarget == QualifierTarget::Pointee && Pointee->isPointerType()) {
- // Adding the `const` to the pointee if the pointee is a pointer
- // is the same as 'QualPolicy == Right && isValueType(Pointee)'.
- // The `const` must be left of the last `*` token.
- // (`int * const* p = nullptr;`)
- SourceLocation BeforeStar = lexer::findPreviousTokenKind(
- Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
- tok::star);
- return fixIfNotDangerous(BeforeStar, buildQualifier(Qualifier, true));
- }
- return std::nullopt;
- }
- static std::optional<FixItHint>
- changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,
- QualifierTarget QualTarget, QualifierPolicy QualPolicy,
- const ASTContext &Context) {
- if (QualPolicy == QualifierPolicy::Left && isValueType(Pointee))
- return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
- buildQualifier(Qualifier));
- SourceLocation BeforeRef = lexer::findPreviousAnyTokenKind(
- Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
- tok::amp, tok::ampamp);
- std::optional<SourceLocation> IgnoredParens =
- skipLParensBackwards(BeforeRef, Context);
- if (IgnoredParens)
- return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true));
- return std::nullopt;
- }
- std::optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,
- const ASTContext &Context,
- DeclSpec::TQ Qualifier,
- QualifierTarget QualTarget,
- QualifierPolicy QualPolicy) {
- assert((QualPolicy == QualifierPolicy::Left ||
- QualPolicy == QualifierPolicy::Right) &&
- "Unexpected Insertion Policy");
- assert((QualTarget == QualifierTarget::Pointee ||
- QualTarget == QualifierTarget::Value) &&
- "Unexpected Target");
- QualType ParenStrippedType = Var.getType().IgnoreParens();
- if (isValueType(ParenStrippedType))
- return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
- if (ParenStrippedType->isReferenceType())
- return changeReferencee(Var, Qualifier, Var.getType()->getPointeeType(),
- QualTarget, QualPolicy, Context);
- if (isMemberOrFunctionPointer(ParenStrippedType))
- return changePointerItself(Var, Qualifier, Context);
- if (ParenStrippedType->isPointerType())
- return changePointer(Var, Qualifier,
- ParenStrippedType->getPointeeType().getTypePtr(),
- QualTarget, QualPolicy, Context);
- if (ParenStrippedType->isArrayType()) {
- const Type *AT = ParenStrippedType->getBaseElementTypeUnsafe();
- assert(AT && "Did not retrieve array element type for an array.");
- if (isValueType(AT))
- return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
- if (AT->isPointerType())
- return changePointer(Var, Qualifier, AT->getPointeeType().getTypePtr(),
- QualTarget, QualPolicy, Context);
- }
- return std::nullopt;
- }
- } // namespace clang::tidy::utils::fixit
|