diff --git a/src/dredd/include_private/include/dredd/protobufs/protobuf_serialization.h b/src/dredd/include_private/include/dredd/protobufs/protobuf_serialization.h index ddc57327..5e98e1f0 100644 --- a/src/dredd/include_private/include/dredd/protobufs/protobuf_serialization.h +++ b/src/dredd/include_private/include/dredd/protobufs/protobuf_serialization.h @@ -33,7 +33,7 @@ #endif // The following should be the only place in the project where protobuf files -// related to serialization are are directly included. This is so that they can +// related to serialization are directly included. This is so that they can // be compiled in a manner where warnings are ignored. #include "google/protobuf/stubs/status.h" #include "google/protobuf/util/json_util.h" diff --git a/src/dredd/src/main.cc b/src/dredd/src/main.cc index 4e20ba48..64959a27 100644 --- a/src/dredd/src/main.cc +++ b/src/dredd/src/main.cc @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include "clang/Tooling/CommonOptionsParser.h" @@ -60,6 +62,19 @@ static llvm::cl::opt dump_asts( llvm::cl::desc("Dump each AST that is processed; useful for debugging"), llvm::cl::cat(mutate_category)); // NOLINTNEXTLINE +static llvm::cl::opt mutant_pass( + "mutant-pass", + llvm::cl::desc("Perform a pass to build the mutation tree. Must be passed " + "with --mutation_info_file."), + llvm::cl::cat(mutate_category)); +// TODO(James Lee-Jones): Rename this to something more representative of what +// it does. NOLINTNEXTLINE +static llvm::cl::opt enabled_mutations_file( + "enabled-mutations-file", + llvm::cl::desc(".json file containing information on which mutations " + "should be performed"), + llvm::cl::cat(mutate_category)); +// NOLINTNEXTLINE static llvm::cl::opt mutation_info_file( "mutation-info-file", llvm::cl::Required, llvm::cl::desc( @@ -96,10 +111,37 @@ int main(int argc, const char** argv) { // including their hierarchical structure. dredd::protobufs::MutationInfo mutation_info; + // Contains the mutations that the user wants to apply in each source + // file, including their hierarchical structure. + std::optional enabled_mutation_info; + + // If a set of mutants to instrument has been provided, convert them to + // a protobufs representation. + if (enabled_mutations_file.empty()) { + enabled_mutation_info = std::nullopt; + } else { + // TODO(James Lee-Jones): See if there is a nicer way to do this. + // TODO(James Lee-Jones): Add extra error checks if read fails. + enabled_mutation_info = dredd::protobufs::MutationInfo(); + std::ifstream enabled_mutations_json_file; + std::stringstream enabled_mutations_json; + enabled_mutations_json_file.open(enabled_mutations_file); + enabled_mutations_json << enabled_mutations_json_file.rdbuf(); + const std::string enabled_mutations_string = enabled_mutations_json.str(); + + auto json_read_status = google::protobuf::util::JsonStringToMessage( + enabled_mutations_string, &*enabled_mutation_info); + if (!json_read_status.ok()) { + llvm::errs() << "Error reading JSON data from " << enabled_mutations_file + << "\n"; + return 1; + } + } + const std::unique_ptr factory = - dredd::NewMutateFrontendActionFactory(!no_mutation_opts, dump_asts, - only_track_mutant_coverage, - mutation_id, mutation_info); + dredd::NewMutateFrontendActionFactory( + !no_mutation_opts, dump_asts, only_track_mutant_coverage, mutant_pass, + mutation_id, mutation_info, enabled_mutation_info); const int return_code = Tool.run(factory.get()); diff --git a/src/libdredd/include/libdredd/mutation.h b/src/libdredd/include/libdredd/mutation.h index 56c93438..7b4c8aaf 100644 --- a/src/libdredd/include/libdredd/mutation.h +++ b/src/libdredd/include/libdredd/mutation.h @@ -48,10 +48,12 @@ class Mutation { // The |dredd_declarations| argument provides a set of declarations that will // be added to the start of the source file being mutated. This allows // avoiding redundant repeat declarations. + // TODO(James Lee-Jones): Change 'mutation_pass' to something like + // 'apply_mutations'. virtual protobufs::MutationGroup Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const = 0; }; diff --git a/src/libdredd/include/libdredd/mutation_remove_stmt.h b/src/libdredd/include/libdredd/mutation_remove_stmt.h index b0f0f8ad..c388a828 100644 --- a/src/libdredd/include/libdredd/mutation_remove_stmt.h +++ b/src/libdredd/include/libdredd/mutation_remove_stmt.h @@ -38,7 +38,7 @@ class MutationRemoveStmt : public Mutation { protobufs::MutationGroup Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const override; diff --git a/src/libdredd/include/libdredd/mutation_replace_binary_operator.h b/src/libdredd/include/libdredd/mutation_replace_binary_operator.h index e07b0734..dbed64c8 100644 --- a/src/libdredd/include/libdredd/mutation_replace_binary_operator.h +++ b/src/libdredd/include/libdredd/mutation_replace_binary_operator.h @@ -40,7 +40,7 @@ class MutationReplaceBinaryOperator : public Mutation { protobufs::MutationGroup Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const override; diff --git a/src/libdredd/include/libdredd/mutation_replace_expr.h b/src/libdredd/include/libdredd/mutation_replace_expr.h index fee39226..a1f4350a 100644 --- a/src/libdredd/include/libdredd/mutation_replace_expr.h +++ b/src/libdredd/include/libdredd/mutation_replace_expr.h @@ -39,7 +39,7 @@ class MutationReplaceExpr : public Mutation { protobufs::MutationGroup Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const override; diff --git a/src/libdredd/include/libdredd/mutation_replace_unary_operator.h b/src/libdredd/include/libdredd/mutation_replace_unary_operator.h index 24c6f621..cb202841 100644 --- a/src/libdredd/include/libdredd/mutation_replace_unary_operator.h +++ b/src/libdredd/include/libdredd/mutation_replace_unary_operator.h @@ -39,7 +39,7 @@ class MutationReplaceUnaryOperator : public Mutation { protobufs::MutationGroup Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const override; diff --git a/src/libdredd/include/libdredd/new_mutate_frontend_action_factory.h b/src/libdredd/include/libdredd/new_mutate_frontend_action_factory.h index f30b452c..28d4dbc0 100644 --- a/src/libdredd/include/libdredd/new_mutate_frontend_action_factory.h +++ b/src/libdredd/include/libdredd/new_mutate_frontend_action_factory.h @@ -16,6 +16,7 @@ #define LIBDREDD_NEW_MUTATE_FRONTEND_ACTION_FACTORY_H #include +#include #include "clang/Tooling/Tooling.h" #include "libdredd/protobufs/dredd_protobufs.h" @@ -23,10 +24,11 @@ namespace dredd { std::unique_ptr -NewMutateFrontendActionFactory(bool optimise_mutations, bool dump_asts, - bool only_track_mutant_coverage, - int& mutation_id, - protobufs::MutationInfo& mutation_info); +NewMutateFrontendActionFactory( + bool optimise_mutations, bool dump_asts, bool only_track_mutant_coverage, + bool mutation_pass, int& mutation_id, + protobufs::MutationInfo& mutation_info, + const std::optional& enabled_mutation_info); } diff --git a/src/libdredd/include/libdredd/protobufs/dredd.proto b/src/libdredd/include/libdredd/protobufs/dredd.proto index b6382032..a52936d6 100644 --- a/src/libdredd/include/libdredd/protobufs/dredd.proto +++ b/src/libdredd/include/libdredd/protobufs/dredd.proto @@ -46,6 +46,7 @@ message MutationRemoveStmt { SourceLocation start = 2; SourceLocation end = 3; string snippet = 4; + bool enabled = 5; } enum MutationReplaceExprAction { @@ -74,6 +75,7 @@ message MutationReplaceExpr { SourceLocation end = 2; string snippet = 3; repeated MutationReplaceExprInstance instances = 4; + bool enabled = 5; } enum MutationReplaceBinaryOperatorAction { @@ -113,6 +115,7 @@ enum MutationReplaceBinaryOperatorAction { message MutationReplaceBinaryOperatorInstance { MutationReplaceBinaryOperatorAction action = 1; int32 mutation_id = 2; + bool enabled = 3; } enum BinaryOperator { @@ -159,6 +162,7 @@ message MutationReplaceBinaryOperator { string rhs_snippet = 9; BinaryOperator operator = 10; repeated MutationReplaceBinaryOperatorInstance instances = 11; + bool enabled = 12; } enum MutationReplaceUnaryOperatorAction { @@ -175,6 +179,7 @@ enum MutationReplaceUnaryOperatorAction { message MutationReplaceUnaryOperatorInstance { MutationReplaceUnaryOperatorAction action = 1; int32 mutation_id = 2; + bool enabled = 3; } enum UnaryOperator { @@ -196,4 +201,5 @@ message MutationReplaceUnaryOperator { string operand_snippet = 6; UnaryOperator operator = 7; repeated MutationReplaceUnaryOperatorInstance instances = 8; + bool enabled = 9; } diff --git a/src/libdredd/include_private/include/libdredd/mutate_ast_consumer.h b/src/libdredd/include_private/include/libdredd/mutate_ast_consumer.h index 6cefda09..9ad81467 100644 --- a/src/libdredd/include_private/include/libdredd/mutate_ast_consumer.h +++ b/src/libdredd/include_private/include/libdredd/mutate_ast_consumer.h @@ -16,6 +16,7 @@ #define LIBDREDD_MUTATE_AST_CONSUMER_H #include +#include #include #include @@ -31,18 +32,21 @@ namespace dredd { class MutateAstConsumer : public clang::ASTConsumer { public: - MutateAstConsumer(const clang::CompilerInstance& compiler_instance, - bool optimise_mutations, bool dump_ast, - bool only_track_mutant_coverage, int& mutation_id, - protobufs::MutationInfo& mutation_info) + MutateAstConsumer( + const clang::CompilerInstance& compiler_instance, bool optimise_mutations, + bool mutation_pass, bool dump_ast, bool only_track_mutant_coverage, + int& mutation_id, protobufs::MutationInfo& mutation_info, + const std::optional& enabled_mutation_info) : compiler_instance_(&compiler_instance), optimise_mutations_(optimise_mutations), + mutation_pass_(mutation_pass), dump_ast_(dump_ast), only_track_mutant_coverage_(only_track_mutant_coverage), visitor_(std::make_unique(compiler_instance, optimise_mutations)), mutation_id_(&mutation_id), - mutation_info_(&mutation_info) {} + mutation_info_(&mutation_info), + enabled_mutation_info_(&enabled_mutation_info) {} void HandleTranslationUnit(clang::ASTContext& ast_context) override; @@ -64,8 +68,9 @@ class MutateAstConsumer : public clang::ASTConsumer { int initial_mutation_id) const; protobufs::MutationTreeNode ApplyMutations( - const MutationTreeNode& mutation_tree_node, int initial_mutation_id, - clang::ASTContext& context, + const MutationTreeNode& mutation_tree_node, + std::optional& enabled_mutation_tree_node, + int initial_mutation_id, clang::ASTContext& context, std::unordered_set& dredd_declarations); const clang::CompilerInstance* compiler_instance_; @@ -73,6 +78,8 @@ class MutateAstConsumer : public clang::ASTConsumer { // True if and only if Dredd's optimisations are enabled. bool optimise_mutations_; + bool mutation_pass_; + // True if and only if the AST being consumed should be dumped; useful for // debugging. bool dump_ast_; @@ -88,6 +95,8 @@ class MutateAstConsumer : public clang::ASTConsumer { int* mutation_id_; protobufs::MutationInfo* mutation_info_; + + const std::optional* enabled_mutation_info_; }; } // namespace dredd diff --git a/src/libdredd/src/mutate_ast_consumer.cc b/src/libdredd/src/mutate_ast_consumer.cc index 99b5dd85..86c112b8 100644 --- a/src/libdredd/src/mutate_ast_consumer.cc +++ b/src/libdredd/src/mutate_ast_consumer.cc @@ -15,6 +15,7 @@ #include "libdredd/mutate_ast_consumer.h" #include +#include #include #include #include @@ -76,14 +77,36 @@ void MutateAstConsumer::HandleTranslationUnit(clang::ASTContext& ast_context) { std::unordered_set dredd_declarations; protobufs::MutationInfoForFile mutation_info_for_file; - mutation_info_for_file.set_filename( - ast_context.getSourceManager() - .getFileEntryForID(ast_context.getSourceManager().getMainFileID()) - ->getName() - .str()); + mutation_info_for_file.set_filename(filename); + + std::optional enabled_mutation_tree_node = + std::nullopt; + if (enabled_mutation_info_->has_value()) { + // This is used instead of find_if to avoid using protobufs internal + // pointers. + for (int i = 0; i < enabled_mutation_info_->value().info_for_files_size(); + i++) { + if (enabled_mutation_info_->value().info_for_files(i).filename() == + filename) { + enabled_mutation_tree_node = enabled_mutation_info_->value() + .info_for_files(i) + .mutation_tree_root(); + } + } + + // The enabled mutation file doesn't contain information about the current + // file, so skip it. + if (enabled_mutation_tree_node == std::nullopt) { + // TODO(James Lee-Jones): Throw an error as the provided enabled mutation + // file does not contain this file. + llvm::errs() << "Skipping due to errors\n"; + return; + } + } + *mutation_info_for_file.mutable_mutation_tree_root() = - ApplyMutations(visitor_->GetMutations(), initial_mutation_id, ast_context, - dredd_declarations); + ApplyMutations(visitor_->GetMutations(), enabled_mutation_tree_node, + initial_mutation_id, ast_context, dredd_declarations); if (initial_mutation_id == *mutation_id_) { // No possibilities for mutation were found; nothing else to do. @@ -92,6 +115,10 @@ void MutateAstConsumer::HandleTranslationUnit(clang::ASTContext& ast_context) { *mutation_info_->add_info_for_files() = mutation_info_for_file; + if (mutation_pass_) { + return; + } + auto& source_manager = ast_context.getSourceManager(); const clang::SourceLocation start_of_source_file = source_manager.translateLineCol(source_manager.getMainFileID(), 1, 1); @@ -359,27 +386,70 @@ std::string MutateAstConsumer::GetDreddPreludeC(int initial_mutation_id) const { } protobufs::MutationTreeNode MutateAstConsumer::ApplyMutations( - const MutationTreeNode& mutation_tree_node, int initial_mutation_id, - clang::ASTContext& context, + const MutationTreeNode& mutation_tree_node, + std::optional& enabled_mutation_tree_node, + int initial_mutation_id, clang::ASTContext& context, std::unordered_set& dredd_declarations) { assert(!(mutation_tree_node.IsEmpty() && mutation_tree_node.GetChildren().size() == 1) && "The mutation tree should already be compressed."); protobufs::MutationTreeNode result; - for (const auto& child : mutation_tree_node.GetChildren()) { + std::vector mutation_tree_node_children = + mutation_tree_node.GetChildren(); + // TODO(James Lee-Jones): Check list size matches or throw an error. + if (enabled_mutation_tree_node.has_value()) { + assert(mutation_tree_node_children.size() == + static_cast( + enabled_mutation_tree_node->children_size())); + } + for (int i = 0; + static_cast(i) < mutation_tree_node_children.size(); + i++) { + const auto* child = + mutation_tree_node_children.at(static_cast(i)); assert(!child->IsEmpty() && "The mutation tree should not have empty subtrees."); - *result.add_children() = ApplyMutations(*child, initial_mutation_id, - context, dredd_declarations); + std::optional enabled_child; + if (enabled_mutation_tree_node == std::nullopt) { + enabled_child = std::nullopt; + } else { + enabled_child = enabled_mutation_tree_node.value().children(i); + // TODO(James Lee-Jones): Check that this is equal to child. + } + *result.add_children() = + ApplyMutations(*child, enabled_child, initial_mutation_id, context, + dredd_declarations); } - for (const auto& mutation : mutation_tree_node.GetMutations()) { + + // TODO(James Lee-Jones): Check list size matches or throw an error. + const std::vector>& mutations = + mutation_tree_node.GetMutations(); + for (int i = 0; static_cast(i) < mutations.size(); i++) { const int mutation_id_old = *mutation_id_; - const auto mutation_group = mutation->Apply( - context, compiler_instance_->getPreprocessor(), optimise_mutations_, - only_track_mutant_coverage_, initial_mutation_id, *mutation_id_, - rewriter_, dredd_declarations); - if (*mutation_id_ > mutation_id_old) { - // Only add the result of applying the mutation if it had an effect. + bool enabled = true; + if (enabled_mutation_tree_node.has_value()) { + const protobufs::MutationGroup& kMutationGroup = + enabled_mutation_tree_node.value().mutation_groups(i); + if (kMutationGroup.has_remove_stmt()) { + enabled = kMutationGroup.remove_stmt().enabled(); + } else if (kMutationGroup.has_replace_binary_operator()) { + enabled = kMutationGroup.replace_binary_operator().enabled(); + } else if (kMutationGroup.has_replace_expr()) { + enabled = kMutationGroup.replace_expr().enabled(); + } else if (kMutationGroup.has_replace_unary_operator()) { + enabled = kMutationGroup.replace_unary_operator().enabled(); + } + } + + const auto mutation_group = + mutations.at(static_cast(i)) + ->Apply(context, compiler_instance_->getPreprocessor(), + optimise_mutations_, only_track_mutant_coverage_, + mutation_pass_ || !enabled, initial_mutation_id, + *mutation_id_, rewriter_, dredd_declarations); + if (enabled && *mutation_id_ > mutation_id_old) { + // Only add the result of applying the mutation if it is enabled and had + // an effect. *result.add_mutation_groups() = mutation_group; } } diff --git a/src/libdredd/src/mutation_remove_stmt.cc b/src/libdredd/src/mutation_remove_stmt.cc index 344f990b..e6a2c20e 100644 --- a/src/libdredd/src/mutation_remove_stmt.cc +++ b/src/libdredd/src/mutation_remove_stmt.cc @@ -41,7 +41,8 @@ MutationRemoveStmt::MutationRemoveStmt(const clang::Stmt& stmt, protobufs::MutationGroup MutationRemoveStmt::Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, + clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const { (void)dredd_declarations; // Unused. (void)optimise_mutations; // Unused. @@ -56,6 +57,20 @@ protobufs::MutationGroup MutationRemoveStmt::Apply( inner_result.mutable_end()->set_line(info_for_source_range_.GetEndLine()); inner_result.mutable_end()->set_column(info_for_source_range_.GetEndColumn()); *inner_result.mutable_snippet() = info_for_source_range_.GetSnippet(); + inner_result.set_enabled(true); + + // Subtracting |first_mutation_id_in_file| turns the global mutation id, + // |mutation_id|, into a file-local mutation id. + const int local_mutation_id = mutation_id - first_mutation_id_in_file; + + mutation_id++; + + protobufs::MutationGroup result; + *result.mutable_remove_stmt() = inner_result; + + if (mutation_pass) { + return result; + } clang::CharSourceRange source_range = clang::CharSourceRange::getTokenRange( GetSourceRangeInMainFile(preprocessor, *stmt_)); @@ -95,10 +110,6 @@ protobufs::MutationGroup MutationRemoveStmt::Apply( source_range = source_range_extended_with_semi; } - // Subtracting |first_mutation_id_in_file| turns the global mutation id, - // |mutation_id|, into a file-local mutation id. - const int local_mutation_id = mutation_id - first_mutation_id_in_file; - if (only_track_mutant_coverage) { const bool rewriter_result = rewriter.InsertTextBefore( source_range.getBegin(), "__dredd_record_covered_mutants(" + @@ -144,10 +155,6 @@ protobufs::MutationGroup MutationRemoveStmt::Apply( (void)rewriter_result; // Keep release-mode compilers happy. } - mutation_id++; - - protobufs::MutationGroup result; - *result.mutable_remove_stmt() = inner_result; return result; } diff --git a/src/libdredd/src/mutation_replace_binary_operator.cc b/src/libdredd/src/mutation_replace_binary_operator.cc index eac4bfac..9763cac5 100644 --- a/src/libdredd/src/mutation_replace_binary_operator.cc +++ b/src/libdredd/src/mutation_replace_binary_operator.cc @@ -522,7 +522,8 @@ std::string MutationReplaceBinaryOperator::GenerateMutatorFunction( protobufs::MutationGroup MutationReplaceBinaryOperator::Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, + clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const { // The protobuf object for the mutation, which will be wrapped in a // MutationGroup. @@ -553,6 +554,8 @@ protobufs::MutationGroup MutationReplaceBinaryOperator::Apply( inner_result.mutable_rhs_end()->set_column(info_for_rhs_.GetEndColumn()); *inner_result.mutable_rhs_snippet() = info_for_rhs_.GetSnippet(); + inner_result.set_enabled(true); + const std::string new_function_name = GetFunctionName(optimise_mutations, ast_context); std::string result_type = binary_operator_->getType() @@ -604,9 +607,11 @@ protobufs::MutationGroup MutationReplaceBinaryOperator::Apply( } } - ReplaceOperator(lhs_type, rhs_type, new_function_name, ast_context, - preprocessor, first_mutation_id_in_file, mutation_id, - rewriter); + if (!mutation_pass) { + ReplaceOperator(lhs_type, rhs_type, new_function_name, ast_context, + preprocessor, first_mutation_id_in_file, mutation_id, + rewriter); + } const std::string new_function = GenerateMutatorFunction( ast_context, new_function_name, result_type, lhs_type, rhs_type, @@ -616,7 +621,9 @@ protobufs::MutationGroup MutationReplaceBinaryOperator::Apply( // Add the mutation function to the set of Dredd declarations - there may // already be a matching function, in which case duplication will be avoided. - dredd_declarations.insert(new_function); + if (!mutation_pass) { + dredd_declarations.insert(new_function); + } protobufs::MutationGroup result; *result.mutable_replace_binary_operator() = inner_result; diff --git a/src/libdredd/src/mutation_replace_expr.cc b/src/libdredd/src/mutation_replace_expr.cc index 79f358e2..d0937d15 100644 --- a/src/libdredd/src/mutation_replace_expr.cc +++ b/src/libdredd/src/mutation_replace_expr.cc @@ -595,7 +595,8 @@ void MutationReplaceExpr::ReplaceExprWithFunctionCall( protobufs::MutationGroup MutationReplaceExpr::Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, + clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const { // The protobuf object for the mutation, which will be wrapped in a // MutationGroup. @@ -607,6 +608,7 @@ protobufs::MutationGroup MutationReplaceExpr::Apply( inner_result.mutable_end()->set_line(info_for_source_range_.GetEndLine()); inner_result.mutable_end()->set_column(info_for_source_range_.GetEndColumn()); *inner_result.mutable_snippet() = info_for_source_range_.GetSnippet(); + inner_result.set_enabled(true); const std::string new_function_name = GetFunctionName(optimise_mutations, ast_context); @@ -629,9 +631,11 @@ protobufs::MutationGroup MutationReplaceExpr::Apply( // Replace the expression with a function call. // Subtracting |first_mutation_id_in_file| turns the global mutation id, // |mutation_id|, into a file-local mutation id. - ReplaceExprWithFunctionCall(new_function_name, input_type, - mutation_id - first_mutation_id_in_file, - ast_context, preprocessor, rewriter); + if (!mutation_pass) { + ReplaceExprWithFunctionCall(new_function_name, input_type, + mutation_id - first_mutation_id_in_file, + ast_context, preprocessor, rewriter); + } const std::string new_function = GenerateMutatorFunction( ast_context, new_function_name, result_type, input_type, @@ -639,7 +643,9 @@ protobufs::MutationGroup MutationReplaceExpr::Apply( inner_result); assert(!new_function.empty() && "Unsupported expression."); - dredd_declarations.insert(new_function); + if (!mutation_pass) { + dredd_declarations.insert(new_function); + } protobufs::MutationGroup result; *result.mutable_replace_expr() = inner_result; diff --git a/src/libdredd/src/mutation_replace_unary_operator.cc b/src/libdredd/src/mutation_replace_unary_operator.cc index b9db1c3f..2cb24f14 100644 --- a/src/libdredd/src/mutation_replace_unary_operator.cc +++ b/src/libdredd/src/mutation_replace_unary_operator.cc @@ -338,7 +338,8 @@ void MutationReplaceUnaryOperator::GenerateUnaryOperatorReplacement( protobufs::MutationGroup MutationReplaceUnaryOperator::Apply( clang::ASTContext& ast_context, const clang::Preprocessor& preprocessor, bool optimise_mutations, bool only_track_mutant_coverage, - int first_mutation_id_in_file, int& mutation_id, clang::Rewriter& rewriter, + bool mutation_pass, int first_mutation_id_in_file, int& mutation_id, + clang::Rewriter& rewriter, std::unordered_set& dredd_declarations) const { // The protobuf object for the mutation, which will be wrapped in a // MutationGroup. @@ -366,6 +367,8 @@ protobufs::MutationGroup MutationReplaceUnaryOperator::Apply( info_for_sub_expr_.GetEndColumn()); *inner_result.mutable_operand_snippet() = info_for_sub_expr_.GetSnippet(); + inner_result.set_enabled(true); + const std::string new_function_name = GetFunctionName(optimise_mutations, ast_context); std::string result_type = unary_operator_->getType() @@ -439,6 +442,19 @@ protobufs::MutationGroup MutationReplaceUnaryOperator::Apply( } suffix.append(", " + std::to_string(local_mutation_id) + ")"); + const std::string new_function = GenerateMutatorFunction( + ast_context, new_function_name, result_type, input_type, + optimise_mutations, only_track_mutant_coverage, mutation_id, + inner_result); + assert(!new_function.empty() && "Unsupported opcode."); + + protobufs::MutationGroup result; + *result.mutable_replace_unary_operator() = inner_result; + + if (mutation_pass) { + return result; + } + // The prefix and suffix are ready, so make the relevant insertions. bool rewriter_result = rewriter.InsertTextBefore( unary_operator_source_range_in_main_file.getBegin(), prefix); @@ -448,16 +464,8 @@ protobufs::MutationGroup MutationReplaceUnaryOperator::Apply( assert(!rewriter_result && "Rewrite failed.\n"); (void)rewriter_result; // Keep release-mode compilers happy. - const std::string new_function = GenerateMutatorFunction( - ast_context, new_function_name, result_type, input_type, - optimise_mutations, only_track_mutant_coverage, mutation_id, - inner_result); - assert(!new_function.empty() && "Unsupported opcode."); - dredd_declarations.insert(new_function); - protobufs::MutationGroup result; - *result.mutable_replace_unary_operator() = inner_result; return result; } diff --git a/src/libdredd/src/new_mutate_frontend_action_factory.cc b/src/libdredd/src/new_mutate_frontend_action_factory.cc index ac3b4a0e..24f2d256 100644 --- a/src/libdredd/src/new_mutate_frontend_action_factory.cc +++ b/src/libdredd/src/new_mutate_frontend_action_factory.cc @@ -31,15 +31,19 @@ namespace dredd { class MutateFrontendAction : public clang::ASTFrontendAction { public: - MutateFrontendAction(bool optimise_mutations, bool dump_asts, - bool only_track_mutant_coverage, int& mutation_id, - protobufs::MutationInfo& mutation_info, - std::set& processed_files) + MutateFrontendAction( + bool optimise_mutations, bool dump_asts, bool only_track_mutant_coverage, + bool mutation_pass, int& mutation_id, + protobufs::MutationInfo& mutation_info, + const std::optional& enabled_mutation_info, + std::set& processed_files) : optimise_mutations_(optimise_mutations), dump_asts_(dump_asts), only_track_mutant_coverage_(only_track_mutant_coverage), + mutation_pass_(mutation_pass), mutation_id_(&mutation_id), mutation_info_(&mutation_info), + enabled_mutation_info_(&enabled_mutation_info), processed_files_(&processed_files) {} std::unique_ptr CreateASTConsumer( @@ -64,41 +68,50 @@ class MutateFrontendAction : public clang::ASTFrontendAction { bool optimise_mutations_; bool dump_asts_; bool only_track_mutant_coverage_; + bool mutation_pass_; int* mutation_id_; protobufs::MutationInfo* mutation_info_; + const std::optional* enabled_mutation_info_; std::set* processed_files_; }; std::unique_ptr -NewMutateFrontendActionFactory(bool optimise_mutations, bool dump_asts, - bool only_track_mutant_coverage, - int& mutation_id, - protobufs::MutationInfo& mutation_info) { +NewMutateFrontendActionFactory( + bool optimise_mutations, bool dump_asts, bool only_track_mutant_coverage, + bool mutation_pass, int& mutation_id, + protobufs::MutationInfo& mutation_info, + const std::optional& enabled_mutation_info) { class MutateFrontendActionFactory : public clang::tooling::FrontendActionFactory { public: - MutateFrontendActionFactory(bool optimise_mutations, bool dump_asts, - bool only_track_mutant_coverage, - int& mutation_id, - protobufs::MutationInfo& mutation_info) + MutateFrontendActionFactory( + bool optimise_mutations, bool dump_asts, + bool only_track_mutant_coverage, bool mutation_pass, int& mutation_id, + protobufs::MutationInfo& mutation_info, + const std::optional& enabled_mutation_info) : optimise_mutations_(optimise_mutations), dump_asts_(dump_asts), only_track_mutant_coverage_(only_track_mutant_coverage), + mutation_pass_(mutation_pass), mutation_id_(&mutation_id), - mutation_info_(&mutation_info) {} + mutation_info_(&mutation_info), + enabled_mutation_info_(&enabled_mutation_info) {} std::unique_ptr create() override { return std::make_unique( optimise_mutations_, dump_asts_, only_track_mutant_coverage_, - *mutation_id_, *mutation_info_, processed_files_); + mutation_pass_, *mutation_id_, *mutation_info_, + *enabled_mutation_info_, processed_files_); } private: bool optimise_mutations_; bool dump_asts_; bool only_track_mutant_coverage_; + bool mutation_pass_; int* mutation_id_; protobufs::MutationInfo* mutation_info_; + const std::optional* enabled_mutation_info_; // Stores the ids of the files that have been processed so far, to avoid // processing a file multiple times. @@ -106,16 +119,17 @@ NewMutateFrontendActionFactory(bool optimise_mutations, bool dump_asts, }; return std::make_unique( - optimise_mutations, dump_asts, only_track_mutant_coverage, mutation_id, - mutation_info); + optimise_mutations, dump_asts, only_track_mutant_coverage, mutation_pass, + mutation_id, mutation_info, enabled_mutation_info); } std::unique_ptr MutateFrontendAction::CreateASTConsumer( clang::CompilerInstance& compiler_instance, llvm::StringRef file) { (void)file; // Unused. return std::make_unique( - compiler_instance, optimise_mutations_, dump_asts_, - only_track_mutant_coverage_, *mutation_id_, *mutation_info_); + compiler_instance, optimise_mutations_, mutation_pass_, dump_asts_, + only_track_mutant_coverage_, *mutation_id_, *mutation_info_, + *enabled_mutation_info_); } } // namespace dredd diff --git a/src/libdreddtest/src/mutation_remove_stmt_test.cc b/src/libdreddtest/src/mutation_remove_stmt_test.cc index 315d2c9b..8c6a2a92 100644 --- a/src/libdreddtest/src/mutation_remove_stmt_test.cc +++ b/src/libdreddtest/src/mutation_remove_stmt_test.cc @@ -49,7 +49,7 @@ void TestRemoval(const std::string& original, const std::string& expected, std::unordered_set dredd_declarations; mutation_supplier(ast_unit->getPreprocessor(), ast_unit->getASTContext()) .Apply(ast_unit->getASTContext(), ast_unit->getPreprocessor(), true, - false, 0, mutation_id, rewriter, dredd_declarations); + false, false, 0, mutation_id, rewriter, dredd_declarations); ASSERT_EQ(1, mutation_id); ASSERT_EQ(0, dredd_declarations.size()); const clang::RewriteBuffer* rewrite_buffer = rewriter.getRewriteBufferFor( diff --git a/src/libdreddtest/src/mutation_replace_binary_operator_test.cc b/src/libdreddtest/src/mutation_replace_binary_operator_test.cc index 2a5bef1c..9aaa8c2b 100644 --- a/src/libdreddtest/src/mutation_replace_binary_operator_test.cc +++ b/src/libdreddtest/src/mutation_replace_binary_operator_test.cc @@ -58,7 +58,7 @@ void TestReplacement(const std::string& original, const std::string& expected, int mutation_id = 0; std::unordered_set dredd_declarations; mutation.Apply(ast_unit->getASTContext(), ast_unit->getPreprocessor(), - optimise_mutations, false, 0, mutation_id, rewriter, + optimise_mutations, false, false, 0, mutation_id, rewriter, dredd_declarations); ASSERT_EQ(num_replacements, mutation_id); ASSERT_EQ(1, dredd_declarations.size()); diff --git a/src/libdreddtest/src/mutation_replace_expr_test.cc b/src/libdreddtest/src/mutation_replace_expr_test.cc index d416e996..92692733 100644 --- a/src/libdreddtest/src/mutation_replace_expr_test.cc +++ b/src/libdreddtest/src/mutation_replace_expr_test.cc @@ -61,7 +61,7 @@ void TestReplacement(const std::string& original, const std::string& expected, int mutation_id = 0; std::unordered_set dredd_declarations; mutation.Apply(ast_unit->getASTContext(), ast_unit->getPreprocessor(), true, - false, 0, mutation_id, rewriter, dredd_declarations); + false, false, 0, mutation_id, rewriter, dredd_declarations); ASSERT_EQ(num_replacements, mutation_id); ASSERT_EQ(1, dredd_declarations.size()); ASSERT_EQ(expected_dredd_declaration, *dredd_declarations.begin()); diff --git a/src/libdreddtest/src/mutation_replace_unary_operator_test.cc b/src/libdreddtest/src/mutation_replace_unary_operator_test.cc index c31780bc..53493362 100644 --- a/src/libdreddtest/src/mutation_replace_unary_operator_test.cc +++ b/src/libdreddtest/src/mutation_replace_unary_operator_test.cc @@ -58,7 +58,7 @@ void TestReplacement(const std::string& original, const std::string& expected, int mutation_id = 0; std::unordered_set dredd_declarations; mutation.Apply(ast_unit->getASTContext(), ast_unit->getPreprocessor(), - optimise_mutations, false, 0, mutation_id, rewriter, + optimise_mutations, false, false, 0, mutation_id, rewriter, dredd_declarations); ASSERT_EQ(num_replacements, mutation_id); ASSERT_EQ(1, dredd_declarations.size());