mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-15 17:45:58 +00:00
This CL removes the logic to handle fallthrough in the AST fuzzer. Bug: tint:1644 Change-Id: Ie636e2377bed8acfd7a644d2af6827efaf37a60e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/109000 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Ben Clayton <bclayton@google.com> Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
193 lines
8.1 KiB
C++
193 lines
8.1 KiB
C++
// Copyright 2021 The Tint Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "src/tint/fuzzers/tint_ast_fuzzer/mutations/delete_statement.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "src/tint/ast/block_statement.h"
|
|
#include "src/tint/ast/for_loop_statement.h"
|
|
#include "src/tint/ast/if_statement.h"
|
|
#include "src/tint/ast/loop_statement.h"
|
|
#include "src/tint/ast/variable_decl_statement.h"
|
|
#include "src/tint/fuzzers/tint_ast_fuzzer/jump_tracker.h"
|
|
#include "src/tint/fuzzers/tint_ast_fuzzer/util.h"
|
|
#include "src/tint/program_builder.h"
|
|
#include "src/tint/sem/for_loop_statement.h"
|
|
#include "src/tint/sem/if_statement.h"
|
|
#include "src/tint/sem/loop_statement.h"
|
|
#include "src/tint/sem/statement.h"
|
|
|
|
namespace tint::fuzzers::ast_fuzzer {
|
|
|
|
MutationDeleteStatement::MutationDeleteStatement(protobufs::MutationDeleteStatement message)
|
|
: message_(std::move(message)) {}
|
|
|
|
MutationDeleteStatement::MutationDeleteStatement(uint32_t statement_id) {
|
|
message_.set_statement_id(statement_id);
|
|
}
|
|
|
|
bool MutationDeleteStatement::IsApplicable(const tint::Program& program,
|
|
const NodeIdMap& node_id_map) const {
|
|
auto* statement_node = tint::As<ast::Statement>(node_id_map.GetNode(message_.statement_id()));
|
|
|
|
if (!statement_node) {
|
|
// The statement id is invalid or does not refer to a statement.
|
|
return false;
|
|
}
|
|
|
|
const auto* statement_sem_node = tint::As<sem::Statement>(program.Sem().Get(statement_node));
|
|
|
|
if (!statement_sem_node) {
|
|
// Semantic information for the statement is not available. This
|
|
// information is required in order to perform the deletion.
|
|
return false;
|
|
}
|
|
|
|
// Check whether it is OK to delete this statement.
|
|
if (!CanBeDeleted(*statement_node, program, JumpTracker(program))) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MutationDeleteStatement::Apply(const NodeIdMap& node_id_map,
|
|
tint::CloneContext* clone_context,
|
|
NodeIdMap* /* unused */) const {
|
|
const auto* statement_node =
|
|
tint::As<ast::Statement>(node_id_map.GetNode(message_.statement_id()));
|
|
const auto* statement_sem_node =
|
|
tint::As<sem::Statement>(clone_context->src->Sem().Get(statement_node));
|
|
const auto* sem_parent = statement_sem_node->Parent();
|
|
|
|
if (tint::Is<sem::IfStatement>(sem_parent) &&
|
|
tint::As<ast::IfStatement>(sem_parent->Declaration())->else_statement == statement_node) {
|
|
// Remove the "else" part of an if statement.
|
|
clone_context->Replace(statement_node, static_cast<const ast::Statement*>(nullptr));
|
|
} else if (tint::Is<sem::ForLoopStatement>(sem_parent) &&
|
|
tint::As<ast::ForLoopStatement>(sem_parent->Declaration())->initializer ==
|
|
statement_node) {
|
|
// Remove the initializer of a for loop.
|
|
clone_context->Replace(statement_node, static_cast<const ast::Statement*>(nullptr));
|
|
} else if (tint::Is<sem::ForLoopStatement>(sem_parent) &&
|
|
tint::As<ast::ForLoopStatement>(sem_parent->Declaration())->continuing ==
|
|
statement_node) {
|
|
// Remove the "continuing" statement of a for loop.
|
|
clone_context->Replace(statement_node, static_cast<const ast::Statement*>(nullptr));
|
|
} else if (tint::Is<sem::LoopContinuingBlockStatement>(statement_sem_node)) {
|
|
// Remove the "continuing" block of a loop.
|
|
clone_context->Replace(statement_node, static_cast<const ast::Statement*>(nullptr));
|
|
} else if (tint::Is<ast::CaseStatement>(statement_node)) {
|
|
// Remove a case statement from its enclosing switch statement.
|
|
const auto& case_statement_list =
|
|
&sem_parent->Declaration()->As<ast::SwitchStatement>()->body;
|
|
assert(std::find(case_statement_list->begin(), case_statement_list->end(),
|
|
statement_node) != case_statement_list->end() &&
|
|
"Statement not found.");
|
|
clone_context->Remove(*case_statement_list, statement_node);
|
|
} else if (tint::Is<ast::BlockStatement>(statement_node)) {
|
|
// Remove a block statement from the block that encloses it. A special case is required for
|
|
// this, since a sem::Block has itself as its associated sem::Block, so it is necessary to
|
|
// look at the parent to get the enclosing block.
|
|
const auto& statement_list =
|
|
sem_parent->Declaration()->As<ast::BlockStatement>()->statements;
|
|
assert(std::find(statement_list.begin(), statement_list.end(), statement_node) !=
|
|
statement_list.end() &&
|
|
"Statement not found.");
|
|
clone_context->Remove(statement_list, statement_node);
|
|
} else {
|
|
// Remove a non-block statement from the block that encloses it.
|
|
const auto& statement_list =
|
|
statement_sem_node->Block()->Declaration()->As<ast::BlockStatement>()->statements;
|
|
assert(std::find(statement_list.begin(), statement_list.end(), statement_node) !=
|
|
statement_list.end() &&
|
|
"Statement not found.");
|
|
clone_context->Remove(statement_list, statement_node);
|
|
}
|
|
}
|
|
|
|
protobufs::Mutation MutationDeleteStatement::ToMessage() const {
|
|
protobufs::Mutation mutation;
|
|
*mutation.mutable_delete_statement() = message_;
|
|
return mutation;
|
|
}
|
|
|
|
bool MutationDeleteStatement::CanBeDeleted(const ast::Statement& statement_node,
|
|
const Program& program,
|
|
const JumpTracker& jump_tracker) {
|
|
if (statement_node.Is<ast::VariableDeclStatement>()) {
|
|
// This is conservative. It would be possible to delete variable declarations if they are
|
|
// not used. Further analysis could allow that.
|
|
return false;
|
|
}
|
|
|
|
if (jump_tracker.ContainsReturn(statement_node) ||
|
|
jump_tracker.ContainsIntraproceduralDiscard(statement_node)) {
|
|
// This is conservative. It would be possible to delete a return/discard statement as long
|
|
// as there is still a return/discard on every control flow path.
|
|
return false;
|
|
}
|
|
|
|
if (jump_tracker.ContainsBreakForInnermostLoop(statement_node)) {
|
|
// This is conservative. Disallowing the removal of breaks ensures that loops cannot become
|
|
// statically infinite. However, a loop might in practice have multiple breaks, some of
|
|
// which can be removed.
|
|
return false;
|
|
}
|
|
|
|
if (auto* case_statement = statement_node.As<ast::CaseStatement>()) {
|
|
// It is not OK to delete the case statement which contains the default selector.
|
|
if (case_statement->ContainsDefault()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
auto* parent_sem = program.Sem().Get(&statement_node)->Parent();
|
|
if (parent_sem == nullptr) {
|
|
// Semantic information for the parent node is required.
|
|
return false;
|
|
}
|
|
|
|
auto* parent_stmt = parent_sem->Declaration();
|
|
|
|
// It does not make sense to delete the entire body of a loop or if statement.
|
|
if (auto* for_loop = parent_stmt->As<ast::ForLoopStatement>()) {
|
|
if (for_loop->body == &statement_node) {
|
|
return false;
|
|
}
|
|
}
|
|
if (auto* loop = parent_stmt->As<ast::LoopStatement>()) {
|
|
if (loop->body == &statement_node) {
|
|
return false;
|
|
}
|
|
}
|
|
if (auto* while_loop = parent_stmt->As<ast::WhileStatement>()) {
|
|
if (while_loop->body == &statement_node) {
|
|
return false;
|
|
}
|
|
}
|
|
if (auto* if_statement = parent_stmt->As<ast::IfStatement>()) {
|
|
if (if_statement->body == &statement_node) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace tint::fuzzers::ast_fuzzer
|