476 lines
14 KiB
OCaml
476 lines
14 KiB
OCaml
(* Yoann Padioleau
|
|
*
|
|
* Copyright (C) 2002-2008 Yoann Padioleau
|
|
* Copyright (C) 2011 Facebook
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License (GPL)
|
|
* version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program 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
|
|
* file license.txt for more details.
|
|
*)
|
|
open Common
|
|
|
|
module Flag = Flag_parsing_cpp
|
|
module Ast = Ast_cpp
|
|
|
|
module TH = Token_helpers_cpp
|
|
module TV = Token_views_cpp
|
|
module Parser = Parser_cpp
|
|
module PI = Parse_info
|
|
|
|
open Parser_cpp
|
|
open Token_views_cpp
|
|
|
|
open Parsing_hacks_lib
|
|
|
|
(*****************************************************************************)
|
|
(* Prelude *)
|
|
(*****************************************************************************)
|
|
(*
|
|
* This file gathers parsing heuristics related to C++.
|
|
* See also Token_views_cpp.set_context_tag and
|
|
* Parsing_hacks_typedef.filter_for_typedef that have
|
|
* heuristics specific to C++.
|
|
*
|
|
* TODO: * TIdent_TemplatenameInQualifier
|
|
*
|
|
*)
|
|
|
|
(*****************************************************************************)
|
|
(* Helpers *)
|
|
(*****************************************************************************)
|
|
|
|
let no_space_between i1 i2 =
|
|
(PI.line_of_info i1 = PI.line_of_info i2) &&
|
|
(PI.col_of_info i1 + String.length (PI.str_of_info i1))= PI.col_of_info i2
|
|
|
|
(*****************************************************************************)
|
|
(* Template inference *)
|
|
(*****************************************************************************)
|
|
|
|
let templateLOOKAHEAD = 30
|
|
|
|
(* note: no need to check for TCPar to stop for instance the search,
|
|
* this is will be done automatically because we would be inside a
|
|
* Parenthised expression.
|
|
*)
|
|
let rec have_a_tsup_quite_close xs =
|
|
match xs with
|
|
| [] -> false
|
|
| x::xs ->
|
|
(match x with
|
|
| {t=TSup _} -> true
|
|
|
|
(* false positive *)
|
|
| {t=tok} when TH.is_static_cast_like tok -> false
|
|
|
|
(* ugly: *)
|
|
| {t=(TOBrace _ | TPtVirg _ | TCol _ | TAssign _ )} ->
|
|
false
|
|
|
|
| {t=TInf _} ->
|
|
(* probably nested template, still try
|
|
* TODO: bug when have i < DEG<...>::foo(...)
|
|
* we should recurse!
|
|
*)
|
|
have_a_tsup_quite_close xs
|
|
|
|
(* bugfix: but want allow some binary operator :) like '*' *)
|
|
| {t=tok} when TH.is_binary_operator_except_star tok -> false
|
|
|
|
| _ -> have_a_tsup_quite_close xs
|
|
)
|
|
|
|
|
|
(* precondition: there is a tsup *)
|
|
let rec find_tsup_quite_close tok_open xs =
|
|
let rec aux acc xs =
|
|
match xs with
|
|
| [] ->
|
|
raise (UnclosedSymbol
|
|
(spf "PB: find_tsup_quite_close, no > for < at line %d"
|
|
(TH.line_of_tok tok_open.t)))
|
|
| x::xs ->
|
|
(match x with
|
|
| {t=TSup ii} ->
|
|
List.rev acc, (x,ii), xs
|
|
|
|
| {t=TInf _} ->
|
|
(* recurse *)
|
|
let (before, (tsuptok,_), after) = find_tsup_quite_close x xs in
|
|
(* we don't care about this one, it will be eventually be
|
|
* transformed by the caller *)
|
|
aux (tsuptok:: (List.rev before) @(x::acc)) after
|
|
|
|
| x -> aux (x::acc) xs
|
|
)
|
|
in
|
|
aux [] xs
|
|
|
|
|
|
(* note: some macros in standard.h may expand to static_cast, so perhaps
|
|
* better to do template detection after macro expansion ?
|
|
*
|
|
* C-s for TInf_Template in the grammar and you will see all cases
|
|
* should be covered by the patterns below.
|
|
*)
|
|
let find_template_inf_sup xs =
|
|
let rec aux xs =
|
|
match xs with
|
|
| [] -> ()
|
|
|
|
(* template<...> *)
|
|
| {t=Ttemplate _}::({t=TInf i2} as tok2)::xs ->
|
|
change_tok tok2 (TInf_Template i2);
|
|
let (before_sup, (toksup, toksupi), rest) =
|
|
find_tsup_quite_close tok2 xs in
|
|
change_tok toksup (TSup_Template toksupi);
|
|
|
|
(* recurse *)
|
|
aux before_sup;
|
|
aux rest
|
|
|
|
(* static_cast<...> *)
|
|
| {t=tok1}::({t=TInf i2} as tok2)::xs
|
|
when TH.is_static_cast_like tok1 ->
|
|
change_tok tok2 (TInf_Template i2);
|
|
let (before_sup, (toksup, toksupi), rest) =
|
|
find_tsup_quite_close tok2 xs in
|
|
change_tok toksup (TSup_Template toksupi);
|
|
|
|
(* recurse *)
|
|
aux before_sup;
|
|
aux rest
|
|
|
|
(*
|
|
* TODO: have_a_tsup_quite_close does not handle a relational < followed
|
|
* by a regular template.
|
|
*)
|
|
| {t=TIdent (_,i1)}::({t=TInf i2} as tok2)::xs
|
|
when
|
|
no_space_between i1 i2 && (* safe guard, and good style anyway *)
|
|
have_a_tsup_quite_close (Common.take_safe templateLOOKAHEAD xs)
|
|
->
|
|
change_tok tok2 (TInf_Template i2);
|
|
let (before_sup, (toksup, toksupi), rest) =
|
|
find_tsup_quite_close tok2 xs in
|
|
change_tok toksup (TSup_Template toksupi);
|
|
|
|
(* old: was changing to TIdent_Templatename but now first need
|
|
* to do the typedef inference and then can transform the
|
|
* TIdent_Typedef into a TIdent_Templatename
|
|
*)
|
|
|
|
(* recurse *)
|
|
aux before_sup;
|
|
aux rest
|
|
|
|
(* special cases which allow extra space between ident and <
|
|
* but I think it would be better for people to fix their code
|
|
* | {t=TIdent (s,i1)}::({t=TInf i2} as tok2)
|
|
* ::tok3::({t=TSup i4} as tok4)::xs ->
|
|
* ...
|
|
*
|
|
*)
|
|
|
|
(* recurse *)
|
|
| _::xs -> aux xs
|
|
|
|
in
|
|
aux xs
|
|
|
|
(*****************************************************************************)
|
|
(* Main heuristics *)
|
|
(*****************************************************************************)
|
|
|
|
let reclassify_tokens_before_idents_or_typedefs xs =
|
|
let groups = List.rev xs in
|
|
|
|
let rec aux xs =
|
|
match xs with
|
|
| [] -> ()
|
|
|
|
(* xx::yy where yy is ident (funcall, variable, etc)
|
|
* need to do that recursively! if have a::b::c
|
|
*)
|
|
| Tok{t=TIdent _ | TIdent_ClassnameInQualifier _}
|
|
::Tok{t=TColCol _}
|
|
::Tok({t=TIdent (s2, i2)} as tok2)::xs ->
|
|
change_tok tok2 (TIdent_ClassnameInQualifier (s2, i2));
|
|
aux ((Tok tok2)::xs)
|
|
|
|
(* xx::t wher et is a type
|
|
* TODO need to do that recursively! if have a::b::c
|
|
*)
|
|
| Tok{t=TIdent_Typedef _}::Tok({t=TColCol icolcol} as tcolcol)
|
|
::Tok({t=TIdent (s2, i2)} as tok2)::xs ->
|
|
change_tok tok2 (TIdent_ClassnameInQualifier_BeforeTypedef (s2, i2));
|
|
change_tok tcolcol (TColCol_BeforeTypedef icolcol);
|
|
aux xs
|
|
|
|
(* xx::t<...> where t is a templatename *)
|
|
| Tok{t=TIdent_Templatename _}::Tok({t=TColCol icolcol} as tcolcol)
|
|
::Tok({t=TIdent (s2, i2)} as tok2)::xs ->
|
|
change_tok tok2 (TIdent_ClassnameInQualifier_BeforeTypedef (s2, i2));
|
|
change_tok tcolcol (TColCol_BeforeTypedef icolcol);
|
|
aux xs
|
|
|
|
(* t<...> where t is a typedef *)
|
|
| Angle (_, xs_angle, _)::Tok({t=TIdent_Typedef (s1, i1)} as tok1)::xs ->
|
|
aux xs_angle;
|
|
change_tok tok1 (TIdent_Templatename (s1, i1));
|
|
(* recurse with tok1 too! *)
|
|
aux (Tok tok1::xs)
|
|
|
|
(* TODO
|
|
* TIdent_TemplatenameInQualifier ?
|
|
*)
|
|
|
|
| x::xs ->
|
|
(match x with
|
|
| Tok _ -> ()
|
|
| Braces (_, xs, _)
|
|
| Parens (_, xs, _)
|
|
| Angle (_, xs, _)
|
|
-> aux (List.rev xs)
|
|
);
|
|
aux xs
|
|
in
|
|
aux groups;
|
|
()
|
|
|
|
|
|
(* quite similar to filter_for_typedef
|
|
* TODO: at some point need have to remove this and instead
|
|
* have a correct filter_for_typedef that also returns
|
|
* nested types in template arguments (and some
|
|
* typedef heuristics that work on template_arguments too)
|
|
*
|
|
* TODO: once you don't use it, remove certain grammar rules (C-s TODO)
|
|
*)
|
|
let find_template_commentize groups =
|
|
(* remove template *)
|
|
let rec aux xs =
|
|
xs +> List.iter (function
|
|
| TV.Braces (_, xs, _) ->
|
|
aux xs
|
|
| TV.Parens (_, xs, _) ->
|
|
aux xs
|
|
| TV.Angle (_, _xs, _) as angle ->
|
|
(* let's commentize everything *)
|
|
[angle] +> TV.iter_token_multi (fun tok ->
|
|
change_tok tok
|
|
(TComment_Cpp (Token_cpp.CplusplusTemplate, TH.info_of_tok tok.t))
|
|
)
|
|
|
|
| TV.Tok tok ->
|
|
(* todo? should also pass the static_cast<...> which normally
|
|
* expect some TInf_Template after. Right mow I manage
|
|
* that by having some extra rules in the grammar
|
|
*)
|
|
(match tok.t with
|
|
| Ttemplate _ ->
|
|
change_tok tok
|
|
(TComment_Cpp (Token_cpp.CplusplusTemplate, TH.info_of_tok tok.t))
|
|
| _ -> ()
|
|
)
|
|
)
|
|
in
|
|
aux groups
|
|
|
|
(* assumes a view without:
|
|
* - template arguments
|
|
*
|
|
* TODO: once you don't use it, remove certain grammar rules (C-s TODO)
|
|
*
|
|
* note: passing qualifiers is slightly less important than passing template
|
|
* arguments because they are before the name (as opposed to templates
|
|
* which are after) and most of our heuristics for typedefs
|
|
* look tokens forward, not backward (actually a few now look backward too)
|
|
*)
|
|
let find_qualifier_commentize xs =
|
|
let rec aux xs =
|
|
match xs with
|
|
| [] -> ()
|
|
|
|
| ({t=TIdent _} as t1)::({t=TColCol _} as t2)::xs ->
|
|
[t1; t2] +> List.iter (fun tok ->
|
|
change_tok tok
|
|
(TComment_Cpp (Token_cpp.CplusplusQualifier, TH.info_of_tok tok.t))
|
|
);
|
|
aux xs
|
|
|
|
(* need also to pass the top :: *)
|
|
| ({t=TColCol _} as t2)::xs ->
|
|
[t2] +> List.iter (fun tok ->
|
|
change_tok tok
|
|
(TComment_Cpp (Token_cpp.CplusplusQualifier, TH.info_of_tok tok.t))
|
|
);
|
|
aux xs
|
|
|
|
(* recurse *)
|
|
| _::xs ->
|
|
aux xs
|
|
in
|
|
aux xs
|
|
|
|
|
|
(* assumes a view where:
|
|
* - set_context_tag has been called.
|
|
* TODO: filter the 'explicit' keyword? filter the TCppDirectiveOther
|
|
* have a filter_for_constructor?
|
|
*)
|
|
let find_constructor xs =
|
|
let rec aux xs =
|
|
match xs with
|
|
| [] -> ()
|
|
|
|
(* { Foo(... *)
|
|
| {t=(TOBrace _ | TCBrace _ | TPtVirg _ | Texplicit _);_}
|
|
::({t=TIdent (s1, i1); where=(TV.InClassStruct s2)::_; _} as tok1)
|
|
::{t=TOPar _}::xs when s1 = s2 ->
|
|
change_tok tok1 (TIdent_Constructor(s1, i1));
|
|
aux xs
|
|
|
|
(* public: Foo(... could also filter the privacy directives so
|
|
* need only one rule
|
|
*)
|
|
| {t=(Tpublic _ | Tprotected _ | Tprivate _)}::{t=TCol _}
|
|
::({t=TIdent (s1, i1); where=(TV.InClassStruct s2)::_; _} as tok1)
|
|
::{t=TOPar _}::xs when s1 = s2 ->
|
|
change_tok tok1 (TIdent_Constructor(s1, i1));
|
|
aux xs
|
|
|
|
(* recurse *)
|
|
| _::xs -> aux xs
|
|
in
|
|
aux xs
|
|
|
|
(* assumes a view where:
|
|
* - template have been filtered but NOT the qualifiers!
|
|
*)
|
|
let find_constructor_outside_class xs =
|
|
let rec aux xs =
|
|
match xs with
|
|
| [] -> ()
|
|
|
|
| {t=TIdent (s1, _);_}::{t=TColCol _}::({t=TIdent (s2,i2);_} as tok)::xs
|
|
when s1 = s2 ->
|
|
change_tok tok (TIdent_Constructor (s2, i2));
|
|
aux (tok::xs)
|
|
|
|
|
|
(* recurse *)
|
|
| _::xs -> aux xs
|
|
in
|
|
aux xs
|
|
|
|
|
|
|
|
(* assumes have:
|
|
* - the typedefs
|
|
* - the right context
|
|
*
|
|
* TODO: filter the TCppDirectiveOther, have a filter_for_constructed?
|
|
*)
|
|
let find_constructed_object_and_more xs =
|
|
let rec aux xs =
|
|
match xs with
|
|
| [] -> ()
|
|
|
|
| {t=(Tdelete _| Tnew _);_}
|
|
::({t=TOCro i1} as tok1)::({t=TCCro i2} as tok2)::xs ->
|
|
change_tok tok1 (TOCro_new i1);
|
|
change_tok tok2 (TCCro_new i2);
|
|
aux xs
|
|
|
|
(* xx yy(1 ... *)
|
|
| {t=TIdent_Typedef _;_}::{t=TIdent _;_}::
|
|
({t=TOPar (ii);where=InArgument::_;_} as tok1)::xs ->
|
|
|
|
change_tok tok1 (TOPar_CplusplusInit ii);
|
|
aux xs
|
|
|
|
(* int yy(1 ... *)
|
|
| {t=tok;_}::{t=TIdent _;_}::
|
|
({t=TOPar (ii);where=InArgument::_;_} as tok1)::xs
|
|
when TH.is_basic_type tok
|
|
->
|
|
change_tok tok1 (TOPar_CplusplusInit ii);
|
|
aux xs
|
|
|
|
(* xx& yy(1 ... *)
|
|
| {t=TIdent_Typedef _;_}::{t=TAnd _}::{t=TIdent _;_}::
|
|
({t=TOPar (ii);where=InArgument::_;_} as tok1)::xs ->
|
|
|
|
change_tok tok1 (TOPar_CplusplusInit ii);
|
|
aux xs
|
|
|
|
(* xx yy(zz)
|
|
* The InArgument heuristic can't guess anything when just have
|
|
* idents inside the parenthesis. It's probably a constructed
|
|
* object though.
|
|
* TODO? could be a function declaration, especially when at Toplevel.
|
|
* If inside a function, then very probably a constructed object.
|
|
*)
|
|
| {t=TIdent_Typedef _;_}::{t=TIdent _;_}::
|
|
({t=TOPar (ii);} as tok1)::{t=TIdent _;_}::{t=TCPar _}::xs ->
|
|
|
|
change_tok tok1 (TOPar_CplusplusInit ii);
|
|
aux xs
|
|
|
|
(* xx yy(zz, ww) *)
|
|
| {t=TIdent_Typedef _;_}::{t=TIdent _;_}
|
|
::({t=TOPar (ii);} as tok1)
|
|
::{t=TIdent _;_}::{t=TComma _}::{t=TIdent _;_}
|
|
::{t=TCPar _}::xs ->
|
|
|
|
change_tok tok1 (TOPar_CplusplusInit ii);
|
|
aux xs
|
|
|
|
(* xx yy(&zz) *)
|
|
| {t=TIdent_Typedef _;_}::{t=TIdent _;_}
|
|
::({t=TOPar (ii);} as tok1)
|
|
::{t=TAnd _}
|
|
::{t=TIdent _;_}
|
|
::{t=TCPar _}::xs ->
|
|
|
|
change_tok tok1 (TOPar_CplusplusInit ii);
|
|
aux xs
|
|
|
|
(* int(), probably part of operator declaration
|
|
* could check that token before is a 'operator'
|
|
*)
|
|
| ({t=kind})::{t=TOPar _}::{t=TCPar _}::xs
|
|
when TH.is_basic_type kind ->
|
|
aux xs
|
|
|
|
(* int(...) unless it's int( * xxx ) *)
|
|
| ({t=_kind})::{t=TOPar _}::{t=TMul _}::xs ->
|
|
aux xs
|
|
| ({t=kind} as tok1)::{t=TOPar _}::xs
|
|
when TH.is_basic_type kind ->
|
|
let newone =
|
|
match kind with
|
|
| Tchar ii -> Tchar_Constr ii
|
|
| Tshort ii -> Tshort_Constr ii
|
|
| Tint ii -> Tint_Constr ii
|
|
| Tdouble ii -> Tdouble_Constr ii
|
|
| Tfloat ii -> Tfloat_Constr ii
|
|
| Tlong ii -> Tlong_Constr ii
|
|
| Tbool ii -> Tbool_Constr ii
|
|
| Tunsigned ii -> Tunsigned_Constr ii
|
|
| Tsigned ii -> Tsigned_Constr ii
|
|
| _ -> raise Impossible
|
|
in
|
|
change_tok tok1 newone;
|
|
aux xs
|
|
|
|
(* recurse *)
|
|
| _::xs -> aux xs
|
|
in
|
|
aux xs
|