// Copyright (c) 2018-present, Facebook, Inc.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
DEFINE-CHECKER TEST_REFERENCE = {
  SET report_when =
          WHEN method_return_type("instancetype")
          AND HOLDS-NEXT WITH-TRANSITION Parameters
          (has_type("MyClass") OR has_type("MyClass &"))
          AND declaration_has_name(REGEXP("^new.*:$"))
          HOLDS-IN-NODE ObjCMethodDecl;
  SET message = "Found reference in parameter of method new";
};

DEFINE-CHECKER TEST_PARAMETER_LABEL = {
  LET method_has_parameter_type =
        WHEN
          HOLDS-NEXT WITH-TRANSITION ParameterName "number"
            (has_type("int"))
          HOLDS-IN-NODE ObjCMessageExpr;
  SET report_when =
      WHEN
         method_has_parameter_type
         AND call_method(REGEXP("^new.*:$"))
      HOLDS-IN-NODE ObjCMessageExpr;
  SET message = "Found method with parameter labeled with `number` and with type `int`";
};

DEFINE-CHECKER TEST_PARAMETER_LABEL_REGEXP = {
  LET method_has_parameter_type =
        WHEN
          HOLDS-NEXT WITH-TRANSITION ParameterName REGEXP("num.*")
            (has_type("int"))
          HOLDS-IN-NODE ObjCMessageExpr;
  SET report_when =
      WHEN
         method_has_parameter_type
         AND call_method(REGEXP("^new.*:$"))
      HOLDS-IN-NODE ObjCMessageExpr;
  SET message = "Found method with parameter labeled with `number` and with type `int`";
};

DEFINE-CHECKER TEST_PARAMETER_LABEL_EMPTY_STRUCT = {
  LET is_empty_init_list =
    WHEN ((is_node("ImplicitValueInitExpr")) HOLDS-EVERYWHERE-NEXT)
     HOLDS-IN-NODE InitListExpr;
  LET is_empty_struct =
    WHEN ((is_empty_init_list) HOLDS-EVERYWHERE-NEXT)
  	 HOLDS-IN-NODE CXXBindTemporaryExpr;
  LET method_has_parameter_type =
        WHEN
          HOLDS-NEXT WITH-TRANSITION ParameterName "newWithStruct"
            (is_empty_struct)
          HOLDS-IN-NODE ObjCMessageExpr;
  SET report_when =
      WHEN
         method_has_parameter_type
         AND call_method(REGEXP("^new.*:$"))
      HOLDS-IN-NODE ObjCMessageExpr;
  SET message = "Do not pass empty struct to the parameter `newWithStruct` of method `new...`";
};

DEFINE-CHECKER TEST_PARAMETER_LABEL_EMPTY_MAP = {
   //This means the node has no children.
  LET constructor_with_no_parameters =
    WHEN (FALSE HOLDS-EVERYWHERE-NEXT) HOLDS-IN-NODE CXXConstructExpr;

  LET temp_expr =
    WHEN (constructor_with_no_parameters HOLDS-EVERYWHERE-NEXT) HOLDS-IN-NODE CXXBindTemporaryExpr;

   LET is_empty_map =
    WHEN (temp_expr HOLDS-EVERYWHERE-NEXT) HOLDS-IN-NODE MaterializeTemporaryExpr;

  LET method_has_parameter_type =
        WHEN
          HOLDS-NEXT WITH-TRANSITION ParameterName "map"
            (is_empty_map)
          HOLDS-IN-NODE ObjCMessageExpr;
  SET report_when =
      WHEN
         method_has_parameter_type
         AND call_method(REGEXP("^new.*:$"))
      HOLDS-IN-NODE ObjCMessageExpr;
  SET message = "Do not pass empty map to the parameter `map` of method `new...`";
};

DEFINE-CHECKER TEST_PARAMETER_SELECTOR = {

 LET initialized_with_selector_expr =
  WHEN (is_node("ObjCSelectorExpr") HOLDS-EVERYWHERE-NEXT) HOLDS-IN-NODE CXXConstructExpr;

  LET materialized_with_selector_expr =
   WHEN (initialized_with_selector_expr HOLDS-EVENTUALLY) HOLDS-IN-NODE CXXConstructExpr;

  LET method_has_parameter_type =
        WHEN
          HOLDS-NEXT WITH-TRANSITION ParameterName "newWithAction"
            (initialized_with_selector_expr OR
            materialized_with_selector_expr)
          HOLDS-IN-NODE ObjCMessageExpr;

  SET report_when =
      WHEN
         method_has_parameter_type
         AND call_method(REGEXP("^new.*:$"))
      HOLDS-IN-NODE ObjCMessageExpr;
  SET message = "Do not construct the Component action with a selector only...`";
};

DEFINE-CHECKER DISCOURAGED_HASH_METHOD_INVOCATION = {
  SET report_when = call_method("hash");
  SET message = "Don't use the hash method";
};

DEFINE-CHECKER PARAMETER_TRANS_TYPE = {
  SET report_when =
    WHEN
      HOLDS-NEXT WITH-TRANSITION Parameters (has_type("int"))
      HOLDS-IN-NODE ObjCMessageExpr;
  SET message = "Found method called with an argument of type int";
};

DEFINE-CHECKER TEST_PARAMETER_SELECTOR_BY_TYPE = {

 LET initialized_with_selector_expr =
  WHEN (is_node("ObjCSelectorExpr") HOLDS-EVERYWHERE-NEXT) HOLDS-IN-NODE CXXConstructExpr;

  LET materialized_with_selector_expr =
   WHEN (initialized_with_selector_expr HOLDS-EVENTUALLY) HOLDS-IN-NODE CXXConstructExpr;

  LET method_has_parameter_type =
    WHEN
      HOLDS-NEXT WITH-TRANSITION Parameters
        (has_type("CKComponentAction") AND
        (initialized_with_selector_expr OR
        materialized_with_selector_expr))
      HOLDS-IN-NODE ObjCMessageExpr;

  SET report_when =
      WHEN
         method_has_parameter_type
         AND call_method(REGEXP("^new.*:$"))
      HOLDS-IN-NODE ObjCMessageExpr;
  SET message = "Do not construct the Component action with a selector only...`";
};

DEFINE-CHECKER NEW_COMPONENT_USING_MEM_MODEL = {
  SET report_when =
          WHEN method_return_type("instancetype")
          AND HOLDS-NEXT WITH-TRANSITION Parameters (has_type("REGEXP('FBMem.*')*"))
          AND declaration_has_name(REGEXP("^new.*:$"))
          HOLDS-IN-NODE ObjCMethodDecl;

  SET message = "Consider using fragment models instead.";
};


DEFINE-CHECKER TITLE_NOT_INITIALIZED = {
	LET rectangle_is_not_initialized_with_title =
 	  WHEN
		HOLDS-NEXT WITH-TRANSITION FieldName "title"
		(is_node("ImplicitValueInitExpr"))
	   HOLDS-IN-NODE InitListExpr;

    LET exists_rectangle_is_not_initialized_with_title =
      rectangle_is_not_initialized_with_title HOLDS-EVENTUALLY;

    LET parameter_rectangle_is_not_initialized_with_title =
       WHEN
          HOLDS-NEXT WITH-TRANSITION Parameters
            (exists_rectangle_is_not_initialized_with_title)
	   HOLDS-IN-NODE ObjCMessageExpr;

	SET report_when =
	  WHEN
		 call_method(REGEXP("^new.*:$")) AND
		 (parameter_rectangle_is_not_initialized_with_title)
	  HOLDS-IN-NODE ObjCMessageExpr;

	SET message = "The title is not initialized";
	SET severity = "WARNING";
	SET mode = "ON";
};

DEFINE-CHECKER FIELD_STRUCT_STRING = {
  LET has_field_string =
     WHEN
        HOLDS-NEXT WITH-TRANSITION Fields
          (has_type("NSString*"))
   HOLDS-IN-NODE CXXRecordDecl;

	SET report_when =
	  WHEN
		 declaration_has_name("Rectangle") AND
		 (has_field_string)
	  HOLDS-IN-NODE CXXRecordDecl;
	SET message = "Found a field of type NSString";
	SET severity = "WARNING";
	SET mode = "ON";
};

DEFINE-CHECKER OBJC_BLOCK_CAPTURING_VALUES = {

			 SET report_when =
				 		 WHEN
				 			 objc_block_is_capturing_values()
				 		 HOLDS-IN-NODE BlockDecl;

      SET message = "ObjC Block capturing values";
      SET mode = "ON";
};

DEFINE-CHECKER ADHERE_TO_PROTOCOL = {

  SET report_when =
  WHEN
    adhere_to_protocol()
  HOLDS-IN-NODE ObjCInterfaceDecl;

  SET message = "Found class adhering to protocol";
  SET mode = "ON";

};

DEFINE-CHECKER ONLY_ONE_CLASS_METHOD = {

  SET report_when =
       WHEN
          objc_class_has_only_one_constructor_method_named(REGEXP("newWith.+"))
       HOLDS-IN-NODE ObjCImplementationDecl;

  SET message = "Found class with only one class method";
  SET mode = "ON";

};