/*
	This file is part of solidity.

	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	solidity is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
 * @author Christian <c@ethdev.com>
 * @date 2015
 * Object containing the type and other annotations for the AST nodes.
 */

#pragma once

#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTEnums.h>
#include <libsolidity/ast/ExperimentalFeatures.h>

#include <libsolutil/SetOnce.h>

#include <map>
#include <memory>
#include <optional>
#include <set>
#include <vector>

namespace solidity::yul
{
struct AsmAnalysisInfo;
struct Identifier;
struct Dialect;
}

namespace solidity::frontend
{

class Type;
class ArrayType;

struct CallGraph;

struct ASTAnnotation
{
	ASTAnnotation() = default;

	ASTAnnotation(ASTAnnotation const&) = delete;
	ASTAnnotation(ASTAnnotation&&) = delete;

	ASTAnnotation& operator=(ASTAnnotation const&) = delete;
	ASTAnnotation& operator=(ASTAnnotation&&) = delete;

	virtual ~ASTAnnotation() = default;
};

struct DocTag
{
	std::string content;	///< The text content of the tag.
	std::string paramName;	///< Only used for @param, stores the parameter name.
};

struct StructurallyDocumentedAnnotation
{
	StructurallyDocumentedAnnotation() = default;

	StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation const&) = delete;
	StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation&&) = delete;

	StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation const&) = delete;
	StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation&&) = delete;

	virtual ~StructurallyDocumentedAnnotation() = default;

	/// Mapping docstring tag name -> content.
	std::multimap<std::string, DocTag> docTags;
	/// contract that @inheritdoc references if it exists
	ContractDefinition const* inheritdocReference = nullptr;
};

struct SourceUnitAnnotation: ASTAnnotation
{
	/// The "absolute" (in the compiler sense) path of this source unit.
	util::SetOnce<std::string> path;
	/// The exported symbols (all global symbols).
	util::SetOnce<std::map<ASTString, std::vector<Declaration const*>>> exportedSymbols;
	/// Experimental features.
	std::set<ExperimentalFeature> experimentalFeatures;
	/// Using the new ABI coder. Set to `false` if using ABI coder v1.
	util::SetOnce<bool> useABICoderV2;
};

struct ScopableAnnotation
{
	ScopableAnnotation() = default;

	ScopableAnnotation(ScopableAnnotation const&) = delete;
	ScopableAnnotation(ScopableAnnotation&&) = delete;

	ScopableAnnotation& operator=(ScopableAnnotation const&) = delete;
	ScopableAnnotation& operator=(ScopableAnnotation&&) = delete;

	virtual ~ScopableAnnotation() = default;

	/// The scope this declaration resides in. Can be nullptr if it is the global scope.
	/// Filled by the Scoper.
	ASTNode const* scope = nullptr;
	/// Pointer to the contract this declaration resides in. Can be nullptr if the current scope
	/// is not part of a contract. Filled by the Scoper.
	ContractDefinition const* contract = nullptr;
};

struct DeclarationAnnotation: ASTAnnotation, ScopableAnnotation
{
};

struct ImportAnnotation: DeclarationAnnotation
{
	/// The absolute path of the source unit to import.
	util::SetOnce<std::string> absolutePath;
	/// The actual source unit.
	SourceUnit const* sourceUnit = nullptr;
};

struct TypeDeclarationAnnotation: DeclarationAnnotation
{
	/// The name of this type, prefixed by proper namespaces if globally accessible.
	util::SetOnce<std::string> canonicalName;
};

struct StructDeclarationAnnotation: TypeDeclarationAnnotation
{
	/// Whether the struct is recursive, i.e. if the struct (recursively) contains a member that involves a struct of the same
	/// type, either in a dynamic array, as member of another struct or inside a mapping.
	/// Only cases in which the recursive occurrence is within a dynamic array or a mapping are valid, while direct
	/// recursion immediately raises an error.
	/// Will be filled in by the DeclarationTypeChecker.
	std::optional<bool> recursive;
	/// Whether the struct contains a mapping type, either directly or, indirectly inside another
	/// struct or an array.
	std::optional<bool> containsNestedMapping;
};

struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
{
	/// List of functions and modifiers without a body. Can also contain functions from base classes.
	std::optional<std::vector<Declaration const*>> unimplementedDeclarations;
	/// List of all (direct and indirect) base contracts in order from derived to
	/// base, including the contract itself.
	std::vector<ContractDefinition const*> linearizedBaseContracts;
	/// Mapping containing the nodes that define the arguments for base constructors.
	/// These can either be inheritance specifiers or modifier invocations.
	std::map<FunctionDefinition const*, ASTNode const*> baseConstructorArguments;
	/// A graph with edges representing calls between functions that may happen during contract construction.
	util::SetOnce<std::shared_ptr<CallGraph const>> creationCallGraph;
	/// A graph with edges representing calls between functions that may happen in a deployed contract.
	util::SetOnce<std::shared_ptr<CallGraph const>> deployedCallGraph;

	/// List of contracts whose bytecode is referenced by this contract, e.g. through "new".
	/// The Value represents the ast node that referenced the contract.
	std::map<ContractDefinition const*, ASTNode const*, ASTCompareByID<ContractDefinition>> contractDependencies;
};

struct CallableDeclarationAnnotation: DeclarationAnnotation
{
	/// The set of functions/modifiers/events this callable overrides.
	std::set<CallableDeclaration const*> baseFunctions;
};

struct FunctionDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
{
};

struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
{
};

struct ErrorDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
{
};


struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
{
};

struct VariableDeclarationAnnotation: DeclarationAnnotation, StructurallyDocumentedAnnotation
{
	/// Type of variable (type of identifier referencing this variable).
	Type const* type = nullptr;
	/// The set of functions this (public state) variable overrides.
	std::set<CallableDeclaration const*> baseFunctions;
};

struct StatementAnnotation: ASTAnnotation
{
};

struct InlineAssemblyAnnotation: StatementAnnotation
{
	struct ExternalIdentifierInfo
	{
		Declaration const* declaration = nullptr;
		/// Suffix used, one of "slot", "offset", "length", "address", "selector" or empty.
		std::string suffix;
		size_t valueSize = size_t(-1);
	};

	/// Mapping containing resolved references to external identifiers and their value size
	std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences;
	/// Information generated during analysis phase.
	std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
	/// True, if the assembly block was annotated to be memory-safe.
	bool markedMemorySafe = false;
	/// True, if the assembly block involves any memory opcode or assigns to variables in memory.
	util::SetOnce<bool> hasMemoryEffects;
};

struct BlockAnnotation: StatementAnnotation, ScopableAnnotation
{
};

struct TryCatchClauseAnnotation: ASTAnnotation, ScopableAnnotation
{
};

struct ForStatementAnnotation: StatementAnnotation, ScopableAnnotation
{
};

struct ReturnAnnotation: StatementAnnotation
{
	/// Reference to the return parameters of the function.
	ParameterList const* functionReturnParameters = nullptr;
};

struct TypeNameAnnotation: ASTAnnotation
{
	/// Type declared by this type name, i.e. type of a variable where this type name is used.
	/// Set during reference resolution stage.
	Type const* type = nullptr;
};

struct IdentifierPathAnnotation: ASTAnnotation
{
	/// Referenced declaration, set during reference resolution stage.
	Declaration const* referencedDeclaration = nullptr;
	/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
	util::SetOnce<VirtualLookup> requiredLookup;

	/// Declaration of each path element.
	std::vector<Declaration const*> pathDeclarations;
};

struct ExpressionAnnotation: ASTAnnotation
{
	/// Inferred type of the expression.
	Type const* type = nullptr;
	/// Whether the expression is a constant variable
	util::SetOnce<bool> isConstant;
	/// Whether the expression is pure, i.e. compile-time constant.
	util::SetOnce<bool> isPure;
	/// Whether it is an LValue (i.e. something that can be assigned to).
	util::SetOnce<bool> isLValue;
	/// Whether the expression is used in a context where the LValue is actually required.
	bool willBeWrittenTo = false;
	/// Whether the expression is an lvalue that is only assigned.
	/// Would be false for --, ++, delete, +=, -=, ....
	/// Only relevant if isLvalue == true
	bool lValueOfOrdinaryAssignment = false;

	/// Types and - if given - names of arguments if the expr. is a function
	/// that is called, used for overload resolution
	std::optional<FuncCallArguments> arguments;

	/// True if the expression consists solely of the name of the function and the function is called immediately
	/// instead of being stored or processed. The name may be qualified with the name of a contract, library
	/// module, etc., that clarifies the scope. For example: `m.L.f()`, where `m` is a module, `L` is a library
	/// and `f` is a function is a direct call. This means that the function to be called is known at compilation
	/// time and it's not necessary to rely on any runtime dispatch mechanism to resolve it.
	/// Note that even the simplest expressions, like `(f)()`, result in an indirect call even if they consist of
	/// values known at compilation time.
	bool calledDirectly = false;
};

struct IdentifierAnnotation: ExpressionAnnotation
{
	/// Referenced declaration, set at latest during overload resolution stage.
	Declaration const* referencedDeclaration = nullptr;
	/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
	util::SetOnce<VirtualLookup> requiredLookup;
	/// List of possible declarations it could refer to (can contain duplicates).
	std::vector<Declaration const*> candidateDeclarations;
	/// List of possible declarations it could refer to.
	std::vector<Declaration const*> overloadedDeclarations;
};

struct MemberAccessAnnotation: ExpressionAnnotation
{
	/// Referenced declaration, set at latest during overload resolution stage.
	Declaration const* referencedDeclaration = nullptr;
	/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
	util::SetOnce<VirtualLookup> requiredLookup;
};

struct BinaryOperationAnnotation: ExpressionAnnotation
{
	/// The common type that is used for the operation, not necessarily the result type (which
	/// e.g. for comparisons is bool).
	Type const* commonType = nullptr;
};

enum class FunctionCallKind
{
	FunctionCall,
	TypeConversion,
	StructConstructorCall
};

struct FunctionCallAnnotation: ExpressionAnnotation
{
	util::SetOnce<FunctionCallKind> kind;
	/// If true, this is the external call of a try statement.
	bool tryCall = false;
};

}