blob: 2cab0ee182c2c7e4dc5c3daa260828a316328363 [file] [log] [blame]
//===--- FillInEnumSwitchCases.cpp - -------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// 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 None;
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 None;
Switch = cast<SwitchStmt>(SelectedStmt->getStmt());
ParentDecl = SelectedStmt->getParentDecl();
}
if (!Switch)
return None;
// 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 = llvm::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);
}