1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
//===--- FillInEnumSwitchCases.cpp - -------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Implements the "Add missing switch cases" refactoring operation.
//
//===----------------------------------------------------------------------===//
#include "RefactoringOperations.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/Edit/RefactoringFixits.h"
using namespace clang;
using namespace clang::tooling;
namespace {
class FillInEnumSwitchCasesOperation : public RefactoringOperation {
public:
FillInEnumSwitchCasesOperation(const EnumDecl *Enum, const SwitchStmt *Switch,
const DeclContext *SwitchContext)
: Enum(Enum), Switch(Switch), SwitchContext(SwitchContext) {}
const Stmt *getTransformedStmt() const override { return Switch; }
llvm::Expected<RefactoringResult>
perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
const RefactoringOptionSet &Options,
unsigned SelectedCandidateIndex) override;
const EnumDecl *Enum;
const SwitchStmt *Switch;
const DeclContext *SwitchContext;
};
} // end anonymous namespace
RefactoringOperationResult
clang::tooling::initiateFillInEnumSwitchCasesOperation(
ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
SourceRange SelectionRange, bool CreateOperation) {
const SwitchStmt *Switch;
const Decl *ParentDecl;
if (SelectionRange.isValid()) {
auto SelectedSet = Slice.getSelectedStmtSet();
if (!SelectedSet)
return std::nullopt;
Switch = dyn_cast_or_null<SwitchStmt>(SelectedSet->containsSelectionRange);
// FIXME: Improve the interface for this to make it similar to SelectedStmt
if (SelectedSet->containsSelectionRange)
ParentDecl =
Slice.parentDeclForIndex(*SelectedSet->containsSelectionRangeIndex);
} else {
auto SelectedStmt = Slice.nearestSelectedStmt(Stmt::SwitchStmtClass);
if (!SelectedStmt)
return std::nullopt;
Switch = cast<SwitchStmt>(SelectedStmt->getStmt());
ParentDecl = SelectedStmt->getParentDecl();
}
if (!Switch)
return std::nullopt;
// Ensure that the type is an enum.
const Expr *Cond = Switch->getCond()->IgnoreImpCasts();
const EnumDecl *ED = nullptr;
if (const auto *ET = Cond->getType()->getAs<EnumType>())
ED = ET->getDecl();
else {
// Enum literals are 'int' in C.
if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
if (const auto *EC = dyn_cast<EnumConstantDecl>(DRE->getDecl()))
ED = dyn_cast<EnumDecl>(EC->getDeclContext());
}
}
if (!ED)
return RefactoringOperationResult("The switch doesn't operate on an enum");
if (!ED->isCompleteDefinition())
return RefactoringOperationResult("The enum type is incomplete");
if (Switch->isAllEnumCasesCovered())
return RefactoringOperationResult("All enum cases are already covered");
RefactoringOperationResult Result;
Result.Initiated = true;
if (!CreateOperation)
return Result;
auto Operation = std::make_unique<FillInEnumSwitchCasesOperation>(
ED, Switch, dyn_cast<DeclContext>(ParentDecl));
Result.RefactoringOp = std::move(Operation);
return Result;
}
llvm::Expected<RefactoringResult>
FillInEnumSwitchCasesOperation::perform(ASTContext &Context,
const Preprocessor &ThePreprocessor,
const RefactoringOptionSet &Options,
unsigned SelectedCandidateIndex) {
std::vector<RefactoringReplacement> Replacements;
edit::fillInMissingSwitchEnumCases(
Context, Switch, Enum, SwitchContext,
[&](const FixItHint &Hint) { Replacements.push_back(Hint); });
return std::move(Replacements);
}
|