230 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- 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"
 | |
| 
 | |
| namespace clang {
 | |
| namespace tidy {
 | |
| namespace utils {
 | |
| namespace 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 Optional<SourceLocation>
 | |
| skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {
 | |
|   if (locDangerous(Start))
 | |
|     return None;
 | |
| 
 | |
|   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 None;
 | |
|   return Start;
 | |
| }
 | |
| 
 | |
| static Optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,
 | |
|                                              StringRef Text) {
 | |
|   if (locDangerous(Loc))
 | |
|     return None;
 | |
|   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 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:
 | |
|     Optional<SourceLocation> IgnoredParens =
 | |
|         skipLParensBackwards(Var.getLocation(), Context);
 | |
| 
 | |
|     if (IgnoredParens)
 | |
|       return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
 | |
|     return None;
 | |
|   }
 | |
|   llvm_unreachable("Unknown QualifierPolicy enum");
 | |
| }
 | |
| 
 | |
| static Optional<FixItHint> changePointerItself(const VarDecl &Var,
 | |
|                                                DeclSpec::TQ Qualifier,
 | |
|                                                const ASTContext &Context) {
 | |
|   if (locDangerous(Var.getLocation()))
 | |
|     return None;
 | |
| 
 | |
|   Optional<SourceLocation> IgnoredParens =
 | |
|       skipLParensBackwards(Var.getLocation(), Context);
 | |
|   if (IgnoredParens)
 | |
|     return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
 | |
|   return None;
 | |
| }
 | |
| 
 | |
| static 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 None;
 | |
| 
 | |
|       Optional<SourceLocation> IgnoredParens =
 | |
|           skipLParensBackwards(BeforeStar, Context);
 | |
| 
 | |
|       if (IgnoredParens)
 | |
|         return fixIfNotDangerous(*IgnoredParens,
 | |
|                                  buildQualifier(Qualifier, true));
 | |
|       return None;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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 None;
 | |
| }
 | |
| 
 | |
| static 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);
 | |
|   Optional<SourceLocation> IgnoredParens =
 | |
|       skipLParensBackwards(BeforeRef, Context);
 | |
|   if (IgnoredParens)
 | |
|     return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true));
 | |
| 
 | |
|   return None;
 | |
| }
 | |
| 
 | |
| 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 None;
 | |
| }
 | |
| } // namespace fixit
 | |
| } // namespace utils
 | |
| } // namespace tidy
 | |
| } // namespace clang
 |